Merge branch 'polyfill/vision' of gitlab.alibaba-inc.com:ali-lowcode/ali-lowcode-engine into polyfill/vision

This commit is contained in:
xiaoxian.xlf 2020-05-04 17:29:52 +08:00
commit 49c477003b
29 changed files with 1491 additions and 205 deletions

View File

@ -20,21 +20,22 @@
"@ali/lowcode-plugin-sample-logo": "^0.8.8",
"@ali/lowcode-plugin-sample-preview": "^0.8.11",
"@ali/lowcode-plugin-settings-pane": "^0.8.8",
"@ali/lowcode-plugin-source-editor": "^0.8.5",
"@ali/lowcode-plugin-undo-redo": "^0.8.9",
"@ali/lowcode-plugin-variable-bind-dialog": "^0.8.7",
"@ali/lowcode-plugin-zh-en": "^0.8.11",
"@ali/lowcode-react-renderer": "^0.8.5",
"@ali/lowcode-runtime": "^0.8.13",
"@ali/lowcode-setters": "^0.8.11",
"@ali/lowcode-utils": "^0.8.2",
"@ali/vs-variable-setter": "^3.1.0",
"@ali/ve-action-pane": "^4.7.0-beta.0",
"@ali/ve-datapool-pane": "^6.4.3",
"@ali/ve-i18n-manage-pane": "^4.3.0",
"@ali/ve-i18n-pane": "^4.0.0-beta.0",
"@ali/ve-trunk-pane": "^5.1.0-beta.14",
"@ali/vs-variable-setter": "^3.1.0",
"@ali/vu-legao-design-fetch-context": "^1.0.3",
"@ali/ve-page-history": "1.2.0",
"@ali/ve-history-pane": "4.0.0",
"@ali/ve-page-history-pane": "^5.0.0-beta.0",
"@alifd/next": "^1.19.12",
"@alife/theme-lowcode-dark": "^0.1.0",
"@alife/theme-lowcode-light": "^0.1.0",

View File

@ -9,7 +9,7 @@ import settingsPane from '@ali/lowcode-plugin-settings-pane';
import designer from '@ali/lowcode-plugin-designer';
import eventBindDialog from '@ali/lowcode-plugin-event-bind-dialog';
import variableBindDialog from '@ali/lowcode-plugin-variable-bind-dialog';
import sourceEditor from '@ali/lowcode-plugin-source-editor';
// import sourceEditor from '@ali/lowcode-plugin-source-editor';
export default {
LowcodeSkeleton,
logo,
@ -22,5 +22,5 @@ export default {
designer,
eventBindDialog,
variableBindDialog,
sourceEditor
// sourceEditor
};

View File

@ -1,6 +1,6 @@
import React from 'react';
import ReactDOM from 'react-dom';
import { registerSetters } from '@ali/lowcode-setters';
// import { registerSetters } from '@ali/lowcode-setters';
import config from './config/skeleton';
import components from './config/components';
import utils from './config/utils';
@ -8,7 +8,7 @@ import utils from './config/utils';
import './global.scss';
import './config/theme.scss';
registerSetters();
// registerSetters();
const Skeleton = components.LowcodeSkeleton;
const LCE_CONTAINER = document.getElementById('lce-container');

View File

@ -2,8 +2,12 @@
import { createElement } from 'react';
import { Button } from '@alifd/next';
import Engine, { Panes } from '@ali/visualengine';
import { ActionUtil as actionUtil } from '@ali/visualengine-utils';
import getTrunkPane from '@ali/ve-trunk-pane';
import DatapoolPane from '@ali/ve-datapool-pane';
import PageHistoryManager from '@ali/ve-page-history';
import HistoryPane from '@ali/ve-history-pane';
import PageHistoryPane from '@ali/ve-page-history-pane';
// import I18nPane from '@ali/ve-i18n-pane';
import I18nManagePane from '@ali/ve-i18n-manage-pane';
import ActionPane from '@ali/ve-action-pane';
@ -273,6 +277,43 @@ function initI18nPane() {
// 动作面板
function initActionPane() {
actionUtil.setActions({
module: {
compiled: "'use strict';\n\nexports.__esModule = true;\n\nvar _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };\n\nexports.submit = submit;\nexports.onLoadData = onLoadData;\nexports.add = add;\nexports.edit = edit;\nexports.del = del;\nexports.search = search;\nexports.reset = reset;\n/**\n* 点击弹框的“确认”\n*/\nfunction submit() {\n var _this = this;\n\n this.$('form').submit(function (data, error) {\n if (data) {\n _this.dataSourceMap['table_submit'].load(data).then(function (res) {\n _this.utils.toast({\n type: 'success',\n title: '提交成功'\n });\n _this.$('dialog').hide();\n _this.dataSourceMap['table_list'].load();\n }).catch(function () {\n _this.utils.toast({\n type: 'error',\n title: '提交失败'\n });\n });\n }\n });\n}\n\n/**\n* tablePc onLoadData\n* @param currentPage 当前页码\n* @param pageSize 每页显示条数\n* @param searchKey 搜索关键字\n* @param orderColumn 排序列\n* @param orderType 排序方式desc,asc\n* @param from 触发来源order,search,pagination\n*/\nfunction onLoadData(currentPage, pageSize, searchKey, orderColumn, orderType, from) {\n var tableParams = {\n currentPage: from === 'search' ? 1 : currentPage,\n pageSize: pageSize,\n searchKey: searchKey,\n orderColumn: orderColumn,\n orderType: orderType\n };\n this.setState({ tableParams: tableParams });\n}\n\n// 点击新增\nfunction add() {\n this.setState({\n formData: null\n });\n this.$('dialog').show();\n}\n\n// 点击编辑\nfunction edit(rowData) {\n this.setState({\n formData: rowData\n });\n this.$('dialog').show();\n}\n\n// 点击删除\nfunction del(rowData) {\n var _this2 = this;\n\n this.utils.dialog({\n method: 'confirm',\n title: '提示',\n content: '确认删除该条目吗?',\n onOk: function onOk() {\n _this2.dataSourceMap['table_delete'].load({ id: rowData.id }).then(function () {\n _this2.utils.toast({\n type: 'success',\n title: '删除成功'\n });\n _this2.dataSourceMap['table_list'].load();\n }).catch(function () {\n _this2.utils.toast({\n type: 'error',\n title: '删除失败'\n });\n });\n }\n });\n}\n\n/**\n* button onClick\n*/\nfunction search() {\n var filterData = this.$('filter').getValue();\n this.setState({\n filterData: filterData,\n tableParams: _extends({}, this.state.tableParams, {\n time: Date.now(),\n currentPage: 1\n })\n });\n}\n\n/**\n* button onClick\n*/\nfunction reset() {\n this.$('filter').reset();\n this.setState({\n filterData: {},\n tableParams: _extends({}, this.state.tableParams, {\n time: Date.now(),\n currentPage: 1\n })\n });\n}",
source: "/**\n* 点击弹框的“确认”\n*/\nexport function submit() {\n this.$('form').submit((data, error) => {\n if (data) {\n this.dataSourceMap['table_submit'].load(data).then((res) => {\n this.utils.toast({\n type: 'success',\n title: '提交成功'\n });\n this.$('dialog').hide();\n this.dataSourceMap['table_list'].load();\n }).catch(()=>{\n this.utils.toast({\n type: 'error',\n title: '提交失败'\n });\n })\n }\n })\n}\n\n/**\n* tablePc onLoadData\n* @param currentPage 当前页码\n* @param pageSize 每页显示条数\n* @param searchKey 搜索关键字\n* @param orderColumn 排序列\n* @param orderType 排序方式desc,asc\n* @param from 触发来源order,search,pagination\n*/\nexport function onLoadData(currentPage, pageSize, searchKey, orderColumn, orderType, from) {\n const tableParams = {\n currentPage: from === 'search' ? 1 : currentPage,\n pageSize,\n searchKey,\n orderColumn,\n orderType\n };\n this.setState({ tableParams });\n}\n\n// 点击新增\nexport function add() {\n this.setState({\n formData: null,\n });\n this.$('dialog').show();\n}\n\n\n// 点击编辑\nexport function edit(rowData) {\n this.setState({\n formData: rowData\n });\n this.$('dialog').show();\n}\n\n// 点击删除\nexport function del(rowData) {\n this.utils.dialog({\n method: 'confirm',\n title: '提示',\n content: '确认删除该条目吗?',\n onOk: () => {\n this.dataSourceMap['table_delete'].load({ id: rowData.id }).then(() => {\n this.utils.toast({\n type: 'success',\n title: '删除成功'\n });\n this.dataSourceMap['table_list'].load();\n }).catch(()=>{\n this.utils.toast({\n type: 'error',\n title: '删除失败'\n });\n })\n }\n })\n}\n\n/**\n* button onClick\n*/\nexport function search(){\n const filterData = this.$('filter').getValue();\n this.setState({\n filterData,\n tableParams: {\n ...this.state.tableParams,\n time: Date.now(),\n currentPage: 1\n }\n });\n}\n\n/**\n* button onClick\n*/\nexport function reset(){\n this.$('filter').reset();\n this.setState({\n filterData: {},\n tableParams: {\n ...this.state.tableParams,\n time: Date.now(),\n currentPage: 1\n }\n });\n}"
},
type: "FUNCTION",
list: [
{
"id": "submit",
"title": "submit"
},
{
"id": "onLoadData",
"title": "onLoadData"
},
{
"id": "add",
"title": "add"
},
{
"id": "edit",
"title": "edit"
},
{
"id": "del",
"title": "del"
},
{
"id": "search",
"title": "search"
},
{
"id": "reset",
"title": "reset"
}
]
});
const props = {
enableGlobalJS: false,
enableVsCodeEdit: false,
@ -284,6 +325,98 @@ function initActionPane() {
});
}
// 操作历史与页面历史面板
function initHistoryPane() {
// let historyConfigs = {getDesignerModuleConfigs(
// this.designerConfigs,
// 'history',
// )};
let historyConfigs = {
enableRedoAndUndo: true,
enablePageHistory: true,
};;
// if (!historyConfigs) {
// return;
// }
// if (historyConfigs === true) {
// historyConfigs = {
// enableRedoAndUndo: true,
// enablePageHistory: true,
// };
// }
// if (historyConfigs.enableRedoAndUndo === undefined) {
// historyConfigs.enableRedoAndUndo = true;
// }
// if (historyConfigs.enablePageHistory === undefined) {
// historyConfigs.enablePageHistory = true;
// }
const isDemoMode = false;
const isEnvSupportsHistoryPane = true;
const historyManager = PageHistoryManager.getManager();
console.log('PageHistoryManager', historyManager);
console.log('PageHistoryManager.onOpenPane', historyManager.onOpenPane);
// 历史撤销、重做以及唤起页面历史按钮
if (typeof HistoryPane === 'function') {
// const historyPane = {
// ...HistoryPane({
// showPageHistory:
// isEnvSupportsHistoryPane
// // && this.app.isForm()
// && !isDemoMode,
// historyManager,
// historyConfigs,
// }),
// index: -940,
// };
// console.log('aaaaaa', historyPane);
Panes.add(HistoryPane, {
props : {
showPageHistory:
isEnvSupportsHistoryPane
// && this.app.isForm()
&& !isDemoMode,
historyManager,
historyConfigs,
index: -940,
}
});
} else {
Panes.add(HistoryPane, {
index: -940,
});
}
// 页面历史 UI 面板
if (
PageHistoryPane
&& !isDemoMode
&& isEnvSupportsHistoryPane
) {
console.log(1111, PageHistoryPane({
historyManager: PageHistoryManager.getManager(),
app: {},
}))
Panes.add(PageHistoryPane, {
props : {
historyManager: {
historyManager,
app: {
}
},
index: -940,
},
});
}
}
async function init() {
Engine.Env.setEnv('RE_VERSION', '7.2.0');
Engine.Env.setSupportFeatures({
@ -297,6 +430,7 @@ async function init() {
initI18nPane();
initActionPane();
initDemoPanes();
initHistoryPane();
Engine.init();
}

View File

@ -1,8 +1,11 @@
declare module "@ali/visualengine";
declare module "@ali/visualengine-utils";
declare module "@ali/ve-trunk-pane";
declare module "@ali/vs-variable-setter";
declare module "@ali/ve-datapool-pane";
declare module "@ali/ve-i18n-manage-pane";
declare module "@ali/ve-action-pane";
declare module "@ali/vu-legao-design-fetch-context";
declare module '@ali/visualengine';
declare module '@ali/visualengine-utils';
declare module '@ali/ve-trunk-pane';
declare module '@ali/vs-variable-setter';
declare module '@ali/ve-datapool-pane';
declare module '@ali/ve-history-pane';
declare module '@ali/ve-page-history-pane';
declare module '@ali/ve-page-history';
declare module '@ali/ve-i18n-manage-pane';
declare module '@ali/ve-action-pane';
declare module '@ali/vu-legao-design-fetch-context';

View File

@ -465,6 +465,14 @@ export class DocumentModel {
getRoot() {
return this.rootNode;
}
/**
* vision
*/
getHistory(): History {
return this.history;
}
get root() {
return this.rootNode;
}

View File

@ -1,6 +1,7 @@
import { Component, ReactElement } from 'react';
import { Icon } from '@alifd/next';
import classNames from 'classnames';
import { Title, observer } from '@ali/lowcode-editor-core';
import { Title, observer, Tip } from '@ali/lowcode-editor-core';
import { DockProps } from '../types';
import PanelDock from '../widget/panel-dock';
import { composeTitle } from '../widget/utils';
@ -21,6 +22,25 @@ export function DockView({ title, icon, description, size, className, onClick }:
);
}
function HelpTip({ tip }: any) {
if (tip && tip.url) {
return (
<div>
<a href={tip.url} target="_blank" rel="noopener noreferrer">
<Icon type="help" size="small" className="lc-help-tip"/>
</a>
<Tip>{tip.content}</Tip>
</div>
);
}
return (
<div>
<Icon type="help" size="small" className="lc-help-tip"/>
<Tip>{tip.content}</Tip>
</div>
)
}
@observer
export class PanelDockView extends Component<DockProps & { dock: PanelDock }> {
componentDidMount() {
@ -196,7 +216,7 @@ class PanelTitle extends Component<{ panel: Panel; className?: string }> {
data-name={panel.name}
>
<Title title={panel.title || panel.name} />
{/*pane.help ? <HelpTip tip={panel.help} /> : null*/}
{panel.help ? <HelpTip tip={panel.help} /> : null}
</div>
);
}

View File

@ -13,12 +13,15 @@ export default class LeftFixedPane extends Component<{ area: Area<PanelConfig, P
}
render() {
const { area } = this.props;
const hideTitleBar = area.current?.config.props?.hideTitleBar;
return (
<div
className={classNames('lc-left-fixed-pane', {
'lc-area-visible': area.visible,
})}
>
{
!hideTitleBar && (
<Button
text
className="lc-pane-close"
@ -28,6 +31,8 @@ export default class LeftFixedPane extends Component<{ area: Area<PanelConfig, P
>
<Icon type="close" />
</Button>
)
}
<Contents area={area} />
</div>
);

View File

@ -32,6 +32,7 @@ export default class LeftFloatPane extends Component<{ area: Area<any, Panel> }>
// focusout remove focus
// onEsc
const width = area.current?.config.props?.width;
const hideTitleBar = area.current?.config.props?.hideTitleBar;
const style = width ? {
width
} : undefined;
@ -42,6 +43,8 @@ export default class LeftFloatPane extends Component<{ area: Area<any, Panel> }>
})}
style={style}
>
{
!hideTitleBar && (
<Button
text
className="lc-pane-close"
@ -51,6 +54,8 @@ export default class LeftFloatPane extends Component<{ area: Area<any, Panel> }>
>
<Icon type="close" />
</Button>
)
}
<Contents area={area} />
</div>
);

View File

@ -57,4 +57,6 @@
--color-block-background-deep-dark: @normal-5;
--color-layer-mask-background: @dark-alpha-7;
--color-layer-tooltip-background: rgba(44,47,51,0.8);
--pane-title-bg-color: rgba(31,56,88,.04);
}

View File

@ -54,19 +54,25 @@ body {
display: none;
}
.lc-panel-title {
height: 32px;
background-color: var(--pane-title-bg-color);
height: 38px;
font-size: 14px;
background-color: var(--pane-title-bg-color,rgba(31,56,88,.04));
display: flex;
align-items: center;
justify-content: center;
padding: 0 15px;
border-bottom: 1px solid var(--color-line-normal,rgba(31,56,88,.1));
.lc-help-tip {
margin-left: 4px;
color: rgba(0,0,0,0.4);
cursor: pointer;
}
}
.lc-panel-body {
position: absolute;
top: 32px;
top: 38px;
bottom: 0;
left: 0;
right: 0;

View File

@ -1,5 +1,6 @@
import { Component } from 'react';
import { TipContainer, observer } from '@ali/lowcode-editor-core';
import classNames from 'classnames';
import { Skeleton } from '../skeleton';
import TopArea from './top-area';
import LeftArea from './left-area';
@ -12,15 +13,15 @@ import RightArea from './right-area';
import './workbench.less';
@observer
export class Workbench extends Component<{ skeleton: Skeleton}> {
export class Workbench extends Component<{ skeleton: Skeleton, className?: string }> {
shouldComponentUpdate() {
return false;
}
render() {
const { skeleton } = this.props;
const { skeleton, className } = this.props;
return (
<div className="lc-workbench">
<div className={classNames('lc-workbench', className)}>
<TopArea area={skeleton.topArea} />
<div className="lc-workbench-body">
<LeftArea area={skeleton.leftArea} />

View File

@ -163,15 +163,23 @@ export default class Panel implements IWidget {
this.setActive(true);
}
/**
* @deprecated
*/
getSupportedPositions() {
return ['default'];
}
/**
* @deprecated
*/
getCurrentPosition() {
return 'default';
}
/**
* @deprecated
*/
setPosition(position: string) {
}
}

View File

@ -20,7 +20,6 @@
"@ali/lowcode-plugin-outline-pane": "^0.8.12",
"@ali/lowcode-plugin-undo-redo": "^0.8.9",
"@ali/lowcode-plugin-zh-en": "^0.8.11",
"@ali/lowcode-setters": "^0.8.11",
"@ali/ve-i18n-util": "^2.0.2",
"@ali/ve-icons": "^4.1.9",
"@ali/ve-less-variables": "2.0.3",

View File

@ -2,16 +2,16 @@ import { isJSBlock } from '@ali/lowcode-types';
import { isPlainObject } from '@ali/lowcode-utils';
import { globalContext, Editor } from '@ali/lowcode-editor-core';
import { Designer, TransformStage } from '@ali/lowcode-designer';
import { registerSetters } from '@ali/lowcode-setters';
// import { registerSetters } from '@ali/lowcode-setters';
import Outline from '@ali/lowcode-plugin-outline-pane';
import DesignerPlugin from '@ali/lowcode-plugin-designer';
import { Skeleton, SettingsPrimaryPane } from '@ali/lowcode-editor-skeleton';
import Preview from '@ali/lowcode-plugin-sample-preview';
import SourceEditor from '@ali/lowcode-plugin-source-editor';
// import SourceEditor from '@ali/lowcode-plugin-source-editor';
import { i18nReducer } from './i18n-reducer';
registerSetters();
// registerSetters();
export const editor = new Editor();
globalContext.register(editor, Editor);

View File

@ -1,155 +0,0 @@
import { Component, ReactNode } from 'react';
import {
PopupField,
Field as NormalField,
EntryField,
PlainField,
createSettingFieldView,
SettingsPane,
createField,
} from '@ali/lowcode-editor-skeleton';
import { createSetterContent } from '@ali/lowcode-editor-core';
import { isPlainObject } from '@ali/lowcode-utils';
import { isSetterConfig } from '@ali/lowcode-types';
import context from './context';
import { VE_HOOKS } from './base/const';
export class Placeholder extends Component {
render() {
console.info(this.props);
return 'rending placeholder here';
}
}
export class SettingField extends Component<{
prop: any;
selected?: boolean;
forceDisplay?: string;
className?: string;
children?: ReactNode;
compact?: boolean;
key?: string;
addonProps?: object;
}> {
constructor(props: any) {
super(props);
console.info(props);
}
render() {
const { prop, selected, addonProps } = this.props;
const display = this.props.forceDisplay || prop.getDisplay();
if (display === 'none') {
return null;
}
// 标准的属性,即每一个 Field 在 VE 下都拥有的属性
const standardProps = {
className: this.props.className,
compact: this.props.compact,
isSupportMultiSetter: this.supportMultiSetter(),
isSupportVariable: prop.isSupportVariable(),
isUseVariable: prop.isUseVariable(),
prop,
setUseVariable: () => prop.setUseVariable(!prop.isUseVariable()),
tip: prop.getTip(),
title: prop.getTitle(),
};
// 部分 Field 所需要的额外 fieldProps
const extraProps = {};
const ctx = context;
const plugin = ctx.getPlugin(VE_HOOKS.VE_SETTING_FIELD_PROVIDER);
let Field;
if (typeof plugin === 'function') {
Field = plugin(display, FIELD_TYPE_MAP, prop);
}
if (!Field) {
Field = FIELD_TYPE_MAP[display] || PlainField;
}
createField()
this._prepareProps(display, extraProps);
if (display === 'entry') {
return <Field {...{ ...standardProps, ...extraProps }} />;
}
let setter;
const props: any = {
prop,
selected,
};
const fieldProps = { ...standardProps, ...extraProps };
if (prop.isUseVariable() && !this.variableSetter.isPopup) {
props.placeholder = '请输入表达式: ${var}';
props.key = `${prop.getId()}-variable`;
setter = React.createElement(this.variableSetter, props);
return <Field {...fieldProps}>{setter}</Field>;
}
// for composited prop
if (prop.getVisibleItems) {
setter = prop
.getVisibleItems()
.map((item: any) => <SettingField {...{ key: item.getId(), prop: item, selected }} />);
return <Field {...fieldProps}>{setter}</Field>;
}
setter = createSetterContent(prop.getSetter(), {
...addonProps,
...props,
});
return <Field {...fieldProps}>{setter}</Field>;
}
private supportMultiSetter() {
const { prop } = this.props;
const setter = prop && prop.getConfig && prop.getConfig('setter');
return prop.isSupportVariable() || Array.isArray(setter);
}
private _prepareProps(displayType: string, extraProps: IExtraProps): void {
const { prop } = this.props;
extraProps.propName = prop.isGroup() ? '组合属性,无属性名称' : prop.getName();
switch (displayType) {
case 'title':
break;
case 'block':
assign(extraProps, { isGroup: prop.isGroup() });
break;
case 'accordion':
assign(extraProps, {
headDIY: true,
isExpand: prop.isExpand(),
isGroup: prop.isGroup(),
onExpandChange: () => prop.onExpandChange(() => this.forceUpdate()),
toggleExpand: () => {
prop.toggleExpand();
},
});
break;
case 'entry':
assign(extraProps, { stageName: prop.getName() });
break;
default:
break;
}
}
}
const Field = {
SettingField: Placeholder,
Stage: Placeholder,
PopupField: Placeholder,
EntryField: Placeholder,
AccordionField: Placeholder,
BlockField: Placeholder,
InlineField: Placeholder,
};
export default Field;

View File

@ -0,0 +1,150 @@
import classnames from 'classnames';
import * as React from 'react';
import { Component } from 'react';
import InlineTip from './inlinetip';
import { isPlainObject } from '@ali/lowcode-utils';
interface IHelpTip {
url?: string;
content?: string;
}
function splitWord(title: string): JSX.Element[] {
return (title || '').split('').map((w, i) => <b key={`word${i}`} className='engine-word'>{w}</b>);
}
function getFieldTitle(title: string, tip: IHelpTip, compact?: boolean, propName?: string): JSX.Element {
const className = classnames('engine-field-title', { 've-compact': compact });
let titleContent = null;
if (!compact && typeof title === 'string') {
titleContent = splitWord(title);
}
let tipUrl = null;
let tipContent = null;
tipContent = (
<div>
<div>{propName}</div>
</div>
);
if (isPlainObject(tip)) {
tipUrl = tip.url;
tipContent = (
<div>
<div>{propName}</div>
<div>{tip.content}</div>
</div>
);
} else if (tip) {
tipContent = (
<div>
<div>{propName}</div>
<div>{tip}</div>
</div>
);
}
return (
<a
className={className}
target='_blank'
rel='noopener noreferrer'
href={tipUrl!}
>
{titleContent || (typeof title === 'object' ? '' : title)}
<InlineTip position='top'>{tipContent}</InlineTip>
</a>
);
}
export interface IVEFieldProps {
prop: any;
children: JSX.Element | string;
title?: string;
tip?: any;
propName?: string;
className?: string;
compact?: boolean;
stageName?: string;
/**
* render the top-header by jsx
*/
headDIY?: boolean;
isSupportVariable?: boolean;
isSupportMultiSetter?: boolean;
isUseVariable?: boolean;
isGroup?: boolean;
isExpand?: boolean;
toggleExpand?: () => any;
onExpandChange?: (fn: () => any) => any;
}
interface IVEFieldState {
hasError?: boolean;
}
export default class VEField extends Component<IVEFieldProps, IVEFieldState> {
public static displayName = 'VEField';
public readonly props: IVEFieldProps;
public classNames: string[] = [];
public state: IVEFieldState = {
hasError: false,
};
public componentDidCatch(error: Error, info: React.ErrorInfo) {
console.error(error);
console.warn(info.componentStack);
}
public renderHead(): JSX.Element | JSX.Element[] | null {
const { title, tip, compact, propName } = this.props;
return getFieldTitle(title!, tip, compact, propName);
}
public renderBody(): JSX.Element | string {
return this.props.children;
}
public renderFoot(): any {
return null;
}
public render(): JSX.Element {
const { stageName, headDIY } = this.props;
const classNameList = classnames(...this.classNames, this.props.className);
const fieldProps: any = {};
if (stageName) {
// 为 stage 切换奠定基础
fieldProps['data-stage-target'] = this.props.stageName;
}
if (this.state.hasError) {
return (
<div>Field render error, please open console to find out.</div>
);
}
const headContent = headDIY ? this.renderHead()
: <div className='engine-field-head'>{this.renderHead()}</div>;
return (
<div className={classNameList} { ...fieldProps }>
{headContent}
<div className='engine-field-body'>
{this.renderBody()}
</div>
<div className='engine-field-foot'>
{this.renderFoot()}
</div>
</div>
);
}
}

View File

@ -0,0 +1,272 @@
@import '~@ali/ve-less-variables/index.less';
.engine-setting-field {
white-space: nowrap;
position: relative;
&:after, &:before {
content: " ";
display: table;
}
&:after {
clear: both;
}
.engine-field-title {
font-size: 12px;
font-family: @font-family;
line-height: 1em;
user-select: none;
color: var(--color-text, @dark-alpha-3);
width: fit-content;
white-space: initial;
word-break: break-word;
&::first-letter {
text-transform: capitalize;
}
.engine-word {
flex: 1;
text-align: center;
font-weight: normal;
&:first-child {
text-align: left;
}
&:last-of-type {
text-align: right;
}
&:only-of-type {
text-align: center;
}
overflow: hidden;
}
}
a.engine-field-title {
border-bottom: 1px dashed var(--color-line-normal, @normal-alpha-7);
text-decoration: none;
padding-bottom: 2px;
&:hover {
cursor: help;
}
}
.engine-field-variable-wrapper {
margin-left: 5px;
}
.engine-field-variable {
cursor: pointer;
opacity: 0.6;
&.engine-active {
opacity: 1;
color: var(--color-brand, @brand-color-1);
}
}
.engine-field-head {
padding-left: 10px;
height: 32px;
background: var(--color-block-background-shallow, @normal-alpha-8);
display: flex;
align-items: center;
font-weight: 500;
border-top: 1px solid var(--color-line-normal, @normal-alpha-7);
border-bottom: 1px solid var(--color-line-normal, @normal-alpha-7);
color: var(--color-title, @dark-alpha-2);
>.engine-icontip {
margin-left: 2px;
}
}
.engine-field-body {
min-height: 20px;
margin: 6px 0;
&:after, &:before {
content: " ";
display: table;
}
&:after {
clear: both;
}
.engine-field-head {
height: 28px;
border: none;
font-weight: 400;
}
}
&.engine-plain-field {
>.engine-field-variable {
position: absolute;
right: 5px;
top: 8px;
}
&:hover {
>.engine-field-variable {
opacity: 1;
}
}
}
&.engine-entry-field {
cursor: pointer;
display: flex;
align-items: center;
height: 32px;
padding-left: 10px;
font-weight: 500;
border-top: 1px solid var(--color-line-normal, @normal-alpha-7);
border-bottom: 1px solid var(--color-line-normal, @normal-alpha-7);
background: var(--color-block-background-shallow, @normal-alpha-8);
margin-bottom: 6px;
>.engine-field-title {
letter-spacing: 1px;
}
>.engine-icontip {
margin-left: 2px;
}
>.engine-field-arrow {
position: absolute;
right: 5px;
top: 50%;
transform: translateY(-50%) rotate(-90deg);
opacity: 0.4;
}
&:hover {
>.engine-field-arrow {
opacity: 1;
}
}
}
&.engine-popup-field {
cursor: pointer;
display: flex;
align-items: center;
height: 32px;
padding-left: 10px;
background: var(--color-block-background-shallow, @normal-alpha-8);
margin-bottom: 1px;
>.engine-field-title {
letter-spacing: 1px;
}
>.engine-icontip {
margin-left: 2px;
}
>.engine-field-icon {
position: absolute;
right: 5px;
top: 50%;
transform: translateY(-50%);
opacity: 0.6;
}
&:hover {
>.engine-field-icon {
opacity: 1;
}
}
}
&.engine-block-field {
>.engine-field-head{
> .engine-field-title {
letter-spacing: 1px;
}
>.engine-field-variable {
margin-left: 2px;
}
}
>.engine-field-body {
margin: 6px;
}
}
&.engine-inline-field {
display: flex;
align-items: center;
margin: 10px;
>.engine-field-head {
display: inline-flex;
background: none;
padding: 0;
border: none;
>.engine-field-title {
display: inline-flex;
width: 50px;
margin-right: 5px;
}
}
>.engine-field-body {
width: 100%;
display: inline-flex;
align-items: flex-start;
padding: 0;
margin: 0;
flex: 1;
position: relative;
}
>.engine-field-variable {
margin-left: 2px;
}
&:hover {
>.engine-field-variable {
opacity: 1;
}
}
}
&.engine-accordion-field {
>.engine-field-head {
position: relative;
cursor: pointer;
>.engine-field-title {
letter-spacing: 1px;
}
>.engine-field-arrow {
transform: rotate(180deg);
position: absolute;
right: 7px;
top: 7px;
transition: transform 0.1s ease;
opacity: 0.6;
}
>.engine-field-variable {
margin-left: 2px;
}
}
&.engine-collapsed {
>.engine-field-head {
margin-bottom: 6px;
}
>.engine-field-head > .engine-field-arrow {
transform: rotate(0);
}
>.engine-field-body {
display: none;
}
}
>.engine-field-body {
margin: 6px;
}
}
}
.engine-block-field,.engine-accordion-field,.engine-entry-field {
.engine-input-control {
margin: 10px;
}
}
.engine-field-tip-icon {
margin-left: 2px;
}

View File

@ -0,0 +1,376 @@
import Icons from '@ali/ve-icons';
import classNames from 'classnames';
import { Component } from 'react';
import { testType } from '@ali/ve-utils';
import VEField, { IVEFieldProps } from './field';
import { SettingField } from './settingField';
import VariableSwitcher from './variableSwitcher';
import popups from '@ali/ve-popups';
import './fields.less';
interface IHelpTip {
url?: string;
content?: string | JSX.Element;
}
function renderTip(tip: IHelpTip, prop?: { propName?: string }) {
const propName = prop && prop.propName;
if (!tip) {
return (
<Icons.Tip position="top" key="icon" className="engine-field-tip-icon">
<div>
<div>{propName}</div>
</div>
</Icons.Tip>
);
}
if (testType(tip) === 'object') {
return (
<Icons.Tip position="top" url={tip.url} key="icon-tip" className="engine-field-tip-icon">
<div>
<div>{propName}</div>
<div>{tip.content}</div>
</div>
</Icons.Tip>
);
}
return (
<Icons.Tip position="top" key="icon" className="engine-field-tip-icon">
<div>
<div>{propName}</div>
<div>{tip}</div>
</div>
</Icons.Tip>
);
}
export class PlainField extends VEField {
public static defaultProps = {
headDIY: true,
};
public static displayName: string = 'PlainField';
public renderHead(): null {
return null;
}
}
export class InlineField extends VEField {
public static displayName = 'InlineField';
constructor(props: any) {
super(props);
this.classNames = ['engine-setting-field', 'engine-inline-field'];
}
public renderFoot() {
return (
<div className="engine-field-variable-wrapper">
<VariableSwitcher {...this.props} />
</div>
);
}
}
export class BlockField extends VEField {
public static displayName = 'BlockField';
constructor(props: IVEFieldProps) {
super(props);
this.classNames = ['engine-setting-field', 'engine-block-field', props.isGroup ? 'engine-group-field' : ''];
}
public renderHead() {
const { title, tip, propName } = this.props;
return [
<span className="engine-field-title" key={title}>
{title}
</span>,
renderTip(tip, { propName }),
<VariableSwitcher {...this.props} />,
];
}
}
export class AccordionField extends VEField {
public readonly props: IVEFieldProps;
private willDetach?: () => any;
constructor(props: IVEFieldProps) {
super(props);
this._generateClassNames(props);
if (this.props.onExpandChange) {
this.willDetach = this.props.onExpandChange(() => this.forceUpdate());
}
}
public componentWillReceiveProps(nextProps: IVEFieldProps) {
this.classNames = this._generateClassNames(nextProps);
}
public componentWillUnmount() {
if (this.willDetach) {
this.willDetach();
}
}
public renderHead() {
const { title, tip, toggleExpand, propName } = this.props;
return (
<div className="engine-field-head" onClick={() => toggleExpand && toggleExpand()}>
<Icons name="arrow" className="engine-field-arrow" size="12px" />
<span className="engine-field-title">{title}</span>
{renderTip(tip, { propName })}
{<VariableSwitcher {...this.props} />}
</div>
);
}
private _generateClassNames(props: IVEFieldProps) {
this.classNames = [
'engine-setting-field',
'engine-accordion-field',
props.isGroup ? 'engine-group-field' : '',
!props.isExpand ? 'engine-collapsed' : '',
];
return this.classNames;
}
}
export class EntryField extends VEField {
constructor(props: any) {
super(props);
this.classNames = ['engine-setting-field', 'engine-entry-field'];
}
public render() {
const { propName, stageName, tip, title } = this.props;
const classNameList = classNames(...this.classNames, this.props.className);
const fieldProps: any = {};
if (stageName) {
// 为 stage 切换奠定基础
fieldProps['data-stage-target'] = this.props.stageName;
}
const innerElements = [
<span className="engine-field-title" key="field-title">
{title}
</span>,
renderTip(tip, { propName }),
<Icons name="arrow" className="engine-field-arrow" size="12px" key="engine-field-arrow-icon" />,
];
return (
<div className={classNameList} {...fieldProps}>
{innerElements}
</div>
);
}
}
export class PopupField extends VEField {
constructor(props: any) {
super(props);
this.classNames = ['engine-setting-field', 'engine-popup-field'];
}
public renderBody() {
return '';
}
public render() {
const { propName, stageName, tip, title } = this.props;
const classNameList = classNames(...this.classNames, this.props.className);
const fieldProps: any = {};
if (stageName) {
// 为 stage 切换奠定基础
fieldProps['data-stage-target'] = this.props.stageName;
}
return (
<div
className={classNameList}
onClick={(e) =>
popups.popup({
cancelOnBlur: true,
content: this.props.children,
position: 'left bottom',
showClose: true,
sizeFixed: true,
target: e.currentTarget,
})
}
>
<span className="engine-field-title">{title}</span>
{renderTip(tip, { propName })}
<VariableSwitcher {...this.props} />
<Icons name="popup" className="engine-field-icon" size="medium" />
</div>
);
}
}
export class CaptionField extends VEField {
constructor(props: IVEFieldProps) {
super(props);
this.classNames = ['engine-setting-field', 'engine-caption-field'];
}
public renderHead() {
const { title, tip, propName } = this.props;
return (
<div>
<span className="engine-field-title">{title}</span>
{renderTip(tip, { propName })}
</div>
);
}
}
export class Stage extends Component {
public readonly props: {
key: any;
stage: any;
current?: boolean;
direction?: any;
};
public stage: any;
public additionClassName: string;
public shell: Element | null = null;
private willDetach: () => any;
public componentWillMount() {
this.stage = this.props.stage;
if (this.stage.onCurrentTabChange) {
this.willDetach = this.stage.onCurrentTabChange(() => this.forceUpdate());
}
}
public componentDidMount() {
this.doSkate();
}
public componentWillReceiveProps(props: any) {
if (props.stage !== this.stage) {
this.stage = props.stage;
if (this.willDetach) {
this.willDetach();
}
if (this.stage.onCurrentTabChange) {
this.willDetach = this.stage.onCurrentTabChange(() => this.forceUpdate());
}
}
}
public componentDidUpdate() {
this.doSkate();
}
public componentWillUnmount() {
if (this.willDetach) {
this.willDetach();
}
}
public doSkate() {
if (this.additionClassName) {
setTimeout(() => {
const elem = this.shell;
if (elem && elem.classList) {
if (this.props.current) {
elem.classList.remove(this.additionClassName);
} else {
elem.classList.add(this.additionClassName);
}
this.additionClassName = '';
}
}, 10);
}
}
public render() {
const stage = this.stage;
let content = null;
let tabs = null;
let className = 'engine-settings-stage';
if (stage.getTabs) {
const selected = stage.getNode();
// stat for cache
stage.stat();
const currentTab = stage.getCurrentTab();
if (stage.hasTabs()) {
className += ' engine-has-tabs';
tabs = (
<div className="engine-settings-tabs">
{stage.getTabs().map((tab: any) => (
<div
key={tab.getId()}
className={`engine-settings-tab${tab === currentTab ? ' engine-active' : ''}`}
onClick={() => stage.setCurrentTab(tab)}
>
{tab.getTitle()}
{renderTip(tab.getTip())}
</div>
))}
</div>
);
}
if (currentTab) {
if (currentTab.getVisibleItems) {
content = currentTab
.getVisibleItems()
.map((item: any) => <SettingField key={item.getId()} selected={selected} prop={item} />);
} else if (currentTab.getSetter) {
content = (
<SettingField key={currentTab.getId()} selected={selected} prop={currentTab} forceDisplay="plain" />
);
}
}
} else {
content = stage.getContent();
}
if (this.props.current) {
if (this.props.direction) {
this.additionClassName = `engine-stagein-${this.props.direction}`;
className += ` ${this.additionClassName}`;
}
} else if (this.props.direction) {
this.additionClassName = `engine-stageout-${this.props.direction}`;
}
let stageBacker = null;
if (stage.hasBack()) {
className += ' engine-has-backer';
stageBacker = (
<div className="engine-settings-stagebacker" data-stage-target="stageback">
<Icons name="arrow" className="engine-field-arrow" size="12px" />
<span className="engine-field-title">{stage.getTitle()}</span>
{renderTip(stage.getTip())}
</div>
);
}
return (
<div
ref={(ref) => {
this.shell = ref;
}}
className={className}
>
{stageBacker}
{tabs}
<div className="engine-stage-content">{content}</div>
</div>
);
}
}

View File

@ -0,0 +1,2 @@
export * from './settingField';
export * from './fields';

View File

@ -0,0 +1,30 @@
import { Component } from 'react';
export interface InlineTipProps {
position: string;
theme?: 'green' | 'black';
children: React.ReactNode;
}
export default class InlineTip extends Component<InlineTipProps> {
public static displayName = 'InlineTip';
public static defaultProps = {
position: 'auto',
theme: 'black',
};
public render(): React.ReactNode {
const { position, theme, children } = this.props;
return (
<div
style={{ display: 'none' }}
data-role='tip'
data-position={position}
data-theme={theme}
>
{children}
</div>
);
}
}

View File

@ -0,0 +1,186 @@
import VariableSetter from './variableSetter';
import context from '../context';
import { VE_HOOKS } from '../base/const';
import {
AccordionField,
BlockField,
EntryField,
InlineField,
PlainField,
PopupField
} from "./fields";
import { ComponentClass, Component, isValidElement, createElement } from 'react';
import { createSetterContent, getSetter } from '@ali/lowcode-editor-core';
function isReactClass(obj: any): obj is ComponentClass<any> {
return (
obj &&
obj.prototype &&
(obj.prototype.isReactComponent || obj.prototype instanceof Component)
);
}
interface IExtraProps {
stageName?: string;
isGroup?: boolean;
isExpand?: boolean;
propName?: string;
toggleExpand?: () => any;
onExpandChange?: () => any;
}
const FIELD_TYPE_MAP: any = {
accordion: AccordionField,
block: BlockField,
entry: EntryField,
inline: InlineField,
plain: PlainField,
popup: PopupField,
tab: AccordionField
};
export class SettingField extends Component {
public readonly props: {
prop: any;
selected?: boolean;
forceDisplay?: string;
className?: string;
children?: JSX.Element | string;
compact?: boolean;
key?: string;
addonProps?: object;
};
/**
* VariableSetter placeholder
*/
public variableSetter: any;
constructor(props: any) {
super(props);
this.variableSetter = getSetter('VariableSetter')?.component || VariableSetter;
}
public render() {
const { prop, selected, addonProps } = this.props;
const display = this.props.forceDisplay || prop.getDisplay();
if (display === "none") {
return null;
}
// 标准的属性,即每一个 Field 在 VE 下都拥有的属性
const standardProps = {
className: this.props.className,
compact: this.props.compact,
isSupportMultiSetter: this.supportMultiSetter(),
isSupportVariable: prop.isSupportVariable(),
isUseVariable: prop.isUseVariable(),
prop,
setUseVariable: () => prop.setUseVariable(!prop.isUseVariable()),
tip: prop.getTip(),
title: prop.getTitle()
};
// 部分 Field 所需要的额外 fieldProps
const extraProps = {};
const ctx = context;
const plugin = ctx.getPlugin(VE_HOOKS.VE_SETTING_FIELD_PROVIDER);
let Field;
if (typeof plugin === "function") {
Field = plugin(display, FIELD_TYPE_MAP, prop);
}
if (!Field) {
Field = FIELD_TYPE_MAP[display] || PlainField;
}
this._prepareProps(display, extraProps);
if (display === "entry") {
return <Field {...{ ...standardProps, ...extraProps }} />;
}
let setter;
const props: any = {
prop,
selected,
};
const fieldProps = { ...standardProps, ...extraProps };
if (prop.isUseVariable() && !this.variableSetter.isPopup) {
props.placeholder = "请输入表达式: ${var}";
props.key = `${prop.getId()}-variable`;
setter = createElement(this.variableSetter, props);
return <Field {...fieldProps}>{setter}</Field>;
}
// for composited prop
if (prop.getVisibleItems) {
setter = prop
.getVisibleItems()
.map((item: any) => (
<SettingField {...{ key: item.getId(), prop: item, selected }} />
));
return <Field {...fieldProps}>{setter}</Field>;
}
setter = prop.getSetter();
if (
typeof setter === "object" &&
"componentName" in setter &&
!(isValidElement(setter) || isReactClass(setter))
) {
const { componentName: setterType, props: setterProps } = setter as any;
setter = createSetterContent(setterType, {
...addonProps,
...setterProps,
...props
});
} else {
setter = createSetterContent(setter, {
...addonProps,
...props
});
}
return <Field {...fieldProps}>{setter}</Field>;
}
private supportMultiSetter() {
const { prop } = this.props;
const setter = prop && prop.getConfig && prop.getConfig("setter");
return prop.isSupportVariable() || Array.isArray(setter);
}
private _prepareProps(displayType: string, extraProps: IExtraProps): void {
const { prop } = this.props;
extraProps.propName = prop.isGroup()
? "组合属性,无属性名称"
: prop.getName();
switch (displayType) {
case "title":
break;
case "block":
Object.assign(extraProps, { isGroup: prop.isGroup() });
break;
case "accordion":
Object.assign(extraProps, {
headDIY: true,
isExpand: prop.isExpand(),
isGroup: prop.isGroup(),
onExpandChange: () => prop.onExpandChange(() => this.forceUpdate()),
toggleExpand: () => {
prop.toggleExpand();
}
});
break;
case "entry":
Object.assign(extraProps, { stageName: prop.getName() });
break;
default:
break;
}
}
}

View File

@ -0,0 +1,43 @@
@import '~@ali/ve-less-variables/index.less';
.engine-input-control {
box-sizing: border-box;
font-size: 12px;
font-family: Consolas, "Courier New", Courier, FreeMono, monospace;
color: var(--color-text, @dark-alpha-3);
background: var(--color-field-background, @white-alpha-1);
border: 1px solid var(--color-field-border, @normal-alpha-5);
flex: 1;
border-radius: @global-border-radius;
max-height: 200px;
&:hover {
border-color: var(--color-field-border-hover, @normal-alpha-4);
}
&.engine-focused {
border-color: var(--color-field-border-active, @normal-alpha-3);
}
textarea {
resize: none;
}
>.engine-input {
box-sizing: border-box;
padding: 6px;
display: block;
font-size: 12px;
line-height: 16px;
color: var(--color-text, @dark-alpha-3);
width: 100%;
border: 0;
margin: 0;
background: transparent;
outline: none;
&::-webkit-input-placeholder {
color: var(--color-field-placeholder, @normal-alpha-5);
}
}
}

View File

@ -0,0 +1,85 @@
import './variableSetter.less';
import { Component } from 'react';
class Input extends Component {
public props: {
value: string;
placeholder: string;
onChange: (val: any) => any;
};
public state: { focused: boolean };
constructor(props: object) {
super(props);
this.state = {
focused: false,
};
}
public componentDidMount() {
this.adjustTextAreaHeight();
}
private domRef: HTMLTextAreaElement | null = null;
public adjustTextAreaHeight() {
if (!this.domRef) {
return;
}
this.domRef.style.height = '1px';
const calculatedHeight = this.domRef.scrollHeight;
this.domRef.style.height = calculatedHeight >= 200 ? '200px' : calculatedHeight + 'px';
}
public render() {
const { value, placeholder, onChange } = this.props;
return (
<div
className={`engine-variable-setter-input engine-input-control${this.state.focused ? ' engine-focused' : ''}`}
>
<textarea
ref={(r) => {
this.domRef = r;
}}
className="engine-input"
value={value || ''}
placeholder={placeholder || ''}
onChange={(e) => {
onChange(e.target.value || '');
}}
onBlur={() => this.setState({ focused: false })}
onFocus={() => this.setState({ focused: true })}
onKeyUp={this.adjustTextAreaHeight.bind(this)}
></textarea>
</div>
);
}
}
export default class VariableSetter extends Component<{
prop: any;
placeholder: string;
}> {
public willDetach: () => any;
public componentWillMount() {
this.willDetach = this.props.prop.onValueChange(() => this.forceUpdate());
}
public componentWillUnmount() {
if (this.willDetach) {
this.willDetach();
}
}
public render() {
const prop = this.props.prop;
return (
<Input
value={prop.getVariableValue()}
placeholder={this.props.placeholder}
onChange={(val: string) => prop.setVariableValue(val)}
/>
);
}
}

View File

@ -0,0 +1,20 @@
@import '~@ali/ve-less-variables/index.less';
.engine-field-variable-switcher {
cursor: pointer;
opacity: 0.6;
margin-left: 2px;
&.engine-active {
opacity: 1;
background: var(--color-brand, @brand-color-1);
color: #fff !important;
border-radius: 3px;
margin-left: 4px;
svg {
height: 22px !important;
width: 22px !important;
}
}
}

View File

@ -0,0 +1,57 @@
import VariableSetter from './variableSetter';
import Icons from '@ali/ve-icons';
import { IVEFieldProps } from './field';
import './variableSwitcher.less';
import { Component } from 'react';
import { getSetter } from '@ali/lowcode-editor-core';
interface IState {
visible: boolean;
}
export default class VariableSwitcher extends Component<IVEFieldProps, IState> {
private ref: HTMLElement | null = null;
private VariableSetter: any;
constructor(props: IVEFieldProps) {
super(props);
this.VariableSetter = getSetter('VariableSetter')?.component || VariableSetter;
this.state = {
visible: false,
};
}
public render() {
const { isUseVariable, prop } = this.props;
const { visible } = this.state;
const isSupportVariable = prop.isSupportVariable();
const tip = !isUseVariable ? '绑定变量' : prop.getVariableValue();
if (!isSupportVariable) {
return null;
}
return (
<div>
<Icons.Tip
name='var'
size='24px'
position='bottom center'
className={`engine-field-variable-switcher ${isUseVariable ? 'engine-active' : ''}`}
data-tip={tip}
onClick={(e: Event) => {
e.stopPropagation();
if (this.VariableSetter.isPopup) {
this.VariableSetter.show({
prop,
});
} else {
prop.setUseVariable(!isUseVariable);
}
}}>
</Icons.Tip>
</div>
);
}
}

View File

@ -17,7 +17,7 @@ import Trunk from './bundle/trunk';
import Prototype from './bundle/prototype';
import Bundle from './bundle/bundle';
import Pages from './pages';
import Field from './field';
import * as Field from './fields';
import Prop from './prop';
import Env from './env';
import DragEngine from './drag-engine';
@ -26,6 +26,11 @@ import { designer, editor } from './editor';
import './vision.less';
function init(container?: Element) {
//TODO: dirty fix
// 之前的组件库依赖了这个样式临时fix一下。
// 取决于预览模式是否保留。
document.documentElement.classList.add('engine-design-mode');
if (!container) {
container = document.createElement('div');
document.body.appendChild(container);
@ -35,6 +40,7 @@ function init(container?: Element) {
render(
createElement(Workbench, {
skeleton,
className: 'engine-main',
}),
container,
);

View File

@ -1,6 +1,6 @@
import { skeleton, editor } from './editor';
import { ReactElement } from 'react';
import { IWidgetBaseConfig } from '@ali/lowcode-editor-skeleton';
import { IWidgetBaseConfig, Skeleton } from '@ali/lowcode-editor-skeleton';
import { uniqueId } from '@ali/lowcode-utils';
export interface IContentItemConfig {
@ -85,6 +85,7 @@ function upgradeConfig(config: OldPaneConfig): IWidgetBaseConfig & { area: strin
}
if (!isAction) {
newConfig.panelProps = {
title,
hideTitleBar,
help: tip,
width,
@ -156,6 +157,10 @@ const dockPane = Object.assign(skeleton.leftArea, {
* compatible *VE.dockPane.activeDock*
*/
activeDock(item: any) {
if (!item) {
skeleton.leftFloatArea?.current?.hide();
return;
}
const name = item.name || item;
skeleton.getPanel(name)?.active();
},

View File

@ -46,6 +46,23 @@ html.engine-blur #engine {
-webkit-filter: blur(4px);
}
.engine-main {
width: 100%;
height: 100%;
position: relative;
.ve-icon-button {
> .ve-icon-contents {
color: var(--color-text, rgba(51,51,51,.6));
}
&:hover, &.active {
> .ve-icon-contents {
color: var(--color-text-light, rgba(51,51,51,.8));
}
}
}
}
.engine-empty {
background: #f2f3f5;
color: #a7b1bd;