mirror of
https://github.com/alibaba/lowcode-engine.git
synced 2026-02-28 04:40:32 +00:00
Merge branches 'polyfill/vision', 'polyfill/vision' and 'polyfill/vision' of gitlab.alibaba-inc.com:ali-lowcode/ali-lowcode-engine into polyfill/vision
This commit is contained in:
commit
550fdde7f7
@ -33,6 +33,9 @@
|
|||||||
"@ali/ve-trunk-pane": "^5.1.0-beta.14",
|
"@ali/ve-trunk-pane": "^5.1.0-beta.14",
|
||||||
"@ali/vs-variable-setter": "^3.1.0",
|
"@ali/vs-variable-setter": "^3.1.0",
|
||||||
"@ali/vu-legao-design-fetch-context": "^1.0.3",
|
"@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",
|
"@alifd/next": "^1.19.12",
|
||||||
"@alife/theme-lowcode-dark": "^0.1.0",
|
"@alife/theme-lowcode-dark": "^0.1.0",
|
||||||
"@alife/theme-lowcode-light": "^0.1.0",
|
"@alife/theme-lowcode-light": "^0.1.0",
|
||||||
|
|||||||
@ -5,6 +5,9 @@ import Engine, { Panes } from '@ali/visualengine';
|
|||||||
import { ActionUtil as actionUtil } from '@ali/visualengine-utils';
|
import { ActionUtil as actionUtil } from '@ali/visualengine-utils';
|
||||||
import getTrunkPane from '@ali/ve-trunk-pane';
|
import getTrunkPane from '@ali/ve-trunk-pane';
|
||||||
import DatapoolPane from '@ali/ve-datapool-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 I18nPane from '@ali/ve-i18n-pane';
|
||||||
import I18nManagePane from '@ali/ve-i18n-manage-pane';
|
import I18nManagePane from '@ali/ve-i18n-manage-pane';
|
||||||
import ActionPane from '@ali/ve-action-pane';
|
import ActionPane from '@ali/ve-action-pane';
|
||||||
@ -322,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() {
|
async function init() {
|
||||||
Engine.Env.setEnv('RE_VERSION', '7.2.0');
|
Engine.Env.setEnv('RE_VERSION', '7.2.0');
|
||||||
Engine.Env.setSupportFeatures({
|
Engine.Env.setSupportFeatures({
|
||||||
@ -335,6 +430,7 @@ async function init() {
|
|||||||
initI18nPane();
|
initI18nPane();
|
||||||
initActionPane();
|
initActionPane();
|
||||||
initDemoPanes();
|
initDemoPanes();
|
||||||
|
initHistoryPane();
|
||||||
|
|
||||||
Engine.init();
|
Engine.init();
|
||||||
}
|
}
|
||||||
|
|||||||
19
packages/demo/src/vision/module.d.ts
vendored
19
packages/demo/src/vision/module.d.ts
vendored
@ -1,8 +1,11 @@
|
|||||||
declare module "@ali/visualengine";
|
declare module '@ali/visualengine';
|
||||||
declare module "@ali/visualengine-utils";
|
declare module '@ali/visualengine-utils';
|
||||||
declare module "@ali/ve-trunk-pane";
|
declare module '@ali/ve-trunk-pane';
|
||||||
declare module "@ali/vs-variable-setter";
|
declare module '@ali/vs-variable-setter';
|
||||||
declare module "@ali/ve-datapool-pane";
|
declare module '@ali/ve-datapool-pane';
|
||||||
declare module "@ali/ve-i18n-manage-pane";
|
declare module '@ali/ve-history-pane';
|
||||||
declare module "@ali/ve-action-pane";
|
declare module '@ali/ve-page-history-pane';
|
||||||
declare module "@ali/vu-legao-design-fetch-context";
|
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';
|
||||||
|
|||||||
@ -12,8 +12,8 @@ import { uniqueId } from '@ali/lowcode-utils';
|
|||||||
|
|
||||||
export type GetDataType<T, NodeType> = T extends undefined
|
export type GetDataType<T, NodeType> = T extends undefined
|
||||||
? NodeType extends {
|
? NodeType extends {
|
||||||
schema: infer R;
|
schema: infer R;
|
||||||
}
|
}
|
||||||
? R
|
? R
|
||||||
: any
|
: any
|
||||||
: T;
|
: T;
|
||||||
@ -465,6 +465,14 @@ export class DocumentModel {
|
|||||||
getRoot() {
|
getRoot() {
|
||||||
return this.rootNode;
|
return this.rootNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 兼容vision
|
||||||
|
*/
|
||||||
|
getHistory(): History {
|
||||||
|
return this.history;
|
||||||
|
}
|
||||||
|
|
||||||
get root() {
|
get root() {
|
||||||
return this.rootNode;
|
return this.rootNode;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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;
|
|
||||||
150
packages/vision-preset/src/fields/field.tsx
Normal file
150
packages/vision-preset/src/fields/field.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
272
packages/vision-preset/src/fields/fields.less
Normal file
272
packages/vision-preset/src/fields/fields.less
Normal 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;
|
||||||
|
}
|
||||||
376
packages/vision-preset/src/fields/fields.tsx
Normal file
376
packages/vision-preset/src/fields/fields.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
2
packages/vision-preset/src/fields/index.ts
Normal file
2
packages/vision-preset/src/fields/index.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export * from './settingField';
|
||||||
|
export * from './fields';
|
||||||
30
packages/vision-preset/src/fields/inlinetip.tsx
Normal file
30
packages/vision-preset/src/fields/inlinetip.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
186
packages/vision-preset/src/fields/settingField.tsx
Normal file
186
packages/vision-preset/src/fields/settingField.tsx
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
43
packages/vision-preset/src/fields/variableSetter.less
Normal file
43
packages/vision-preset/src/fields/variableSetter.less
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
85
packages/vision-preset/src/fields/variableSetter.tsx
Normal file
85
packages/vision-preset/src/fields/variableSetter.tsx
Normal 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)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
20
packages/vision-preset/src/fields/variableSwitcher.less
Normal file
20
packages/vision-preset/src/fields/variableSwitcher.less
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
57
packages/vision-preset/src/fields/variableSwitcher.tsx
Normal file
57
packages/vision-preset/src/fields/variableSwitcher.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -17,7 +17,7 @@ import Trunk from './bundle/trunk';
|
|||||||
import Prototype from './bundle/prototype';
|
import Prototype from './bundle/prototype';
|
||||||
import Bundle from './bundle/bundle';
|
import Bundle from './bundle/bundle';
|
||||||
import Pages from './pages';
|
import Pages from './pages';
|
||||||
import Field from './field';
|
import * as Field from './fields';
|
||||||
import Prop from './prop';
|
import Prop from './prop';
|
||||||
import Env from './env';
|
import Env from './env';
|
||||||
import DragEngine from './drag-engine';
|
import DragEngine from './drag-engine';
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user