mirror of
https://github.com/alibaba/lowcode-engine.git
synced 2026-02-27 20:30:28 +00:00
support mixed setter
This commit is contained in:
parent
27db010b8a
commit
a15bfd8517
@ -15,6 +15,7 @@ export type RegisteredSetter = {
|
||||
* for MixedSetter to manual change to this setter
|
||||
*/
|
||||
initialValue?: any | ((field: any) => any);
|
||||
recommend?: boolean;
|
||||
};
|
||||
const settersMap = new Map<string, RegisteredSetter & {
|
||||
type: string;
|
||||
|
||||
@ -53,7 +53,7 @@ export class Field extends Component<FieldProps> {
|
||||
check();
|
||||
observer.observe(body, {
|
||||
childList: true,
|
||||
subtree: false,
|
||||
subtree: true,
|
||||
attributes: true,
|
||||
attributeFilter: ['class'],
|
||||
});
|
||||
|
||||
@ -80,9 +80,14 @@
|
||||
}
|
||||
}
|
||||
|
||||
.lc-setter-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
&.lc-block-field {
|
||||
position: relative;
|
||||
>.lc-field-body>.lc-block-setter>.lc-block-setter-actions {
|
||||
>.lc-field-body>.lc-block-setter>.lc-setter-actions {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
top: 0;
|
||||
|
||||
16
packages/plugin-settings-pane/src/icons/convert.tsx
Normal file
16
packages/plugin-settings-pane/src/icons/convert.tsx
Normal file
@ -0,0 +1,16 @@
|
||||
import { SVGIcon, IconProps } from "@ali/lowcode-globals";
|
||||
|
||||
export function IconConvert(props: IconProps) {
|
||||
return (
|
||||
<SVGIcon viewBox="0 0 1024 1024" {...props}>
|
||||
<path d="M508.16 889.6C291.84 889.6 115.2 714.24 115.2 497.92 115.2 281.6 291.84 106.24 509.44 106.24c43.52 0 85.76 6.4 124.16 20.48l-10.24 30.72c-35.84-11.52-72.96-17.92-113.92-17.92-199.68 0-362.24 161.28-362.24 359.68s162.56 358.4 360.96 358.4 359.68-161.28 359.68-359.68c0-66.56-17.92-131.84-51.2-185.6L844.8 294.4c37.12 60.16 56.32 130.56 56.32 203.52-1.28 216.32-176.64 391.68-392.96 391.68z" />
|
||||
<path d="M627.2 140.8m-15.36 0a15.36 15.36 0 1 0 30.72 0 15.36 15.36 0 1 0-30.72 0Z" />
|
||||
<path d="M832 304.64m-15.36 0a15.36 15.36 0 1 0 30.72 0 15.36 15.36 0 1 0-30.72 0Z" />
|
||||
<path d="M348.16 497.92m-35.84 0a35.84 35.84 0 1 0 71.68 0 35.84 35.84 0 1 0-71.68 0Z" fill="#35A2D4" />
|
||||
<path d="M508.16 497.92m-35.84 0a35.84 35.84 0 1 0 71.68 0 35.84 35.84 0 1 0-71.68 0Z" fill="#35A2D4" />
|
||||
<path d="M668.16 497.92m-35.84 0a35.84 35.84 0 1 0 71.68 0 35.84 35.84 0 1 0-71.68 0Z" fill="#35A2D4" />
|
||||
</SVGIcon>
|
||||
);
|
||||
}
|
||||
|
||||
IconConvert.displayName = 'Convert';
|
||||
@ -1,10 +1,29 @@
|
||||
import React, { PureComponent, Component } from 'react';
|
||||
import React, { Component, isValidElement } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { Dropdown, Button, Menu, Icon } from '@alifd/next';
|
||||
import { getSetter, getSettersMap, SetterConfig, computed, obx, CustomView, DynamicProps, DynamicSetter, TitleContent, isSetterConfig, Title, createSetterContent } from '@ali/lowcode-globals';
|
||||
import { SettingField } from 'plugin-settings-pane/src/settings/main';
|
||||
import { Dropdown, Button, Menu } from '@alifd/next';
|
||||
import {
|
||||
getSetter,
|
||||
getSettersMap,
|
||||
SetterConfig,
|
||||
computed,
|
||||
obx,
|
||||
CustomView,
|
||||
DynamicProps,
|
||||
DynamicSetter,
|
||||
TitleContent,
|
||||
isSetterConfig,
|
||||
Title,
|
||||
createSetterContent,
|
||||
observer,
|
||||
isDynamicSetter,
|
||||
shallowIntl,
|
||||
EmbedTip,
|
||||
isI18nData,
|
||||
} from '@ali/lowcode-globals';
|
||||
import { SettingField } from '../../settings/setting-field';
|
||||
import { IconConvert } from '../../icons/convert';
|
||||
|
||||
import './index.scss';
|
||||
import './style.less';
|
||||
|
||||
export interface SetterItem {
|
||||
name: string;
|
||||
@ -12,7 +31,8 @@ export interface SetterItem {
|
||||
setter: string | DynamicSetter | CustomView;
|
||||
props?: object | DynamicProps;
|
||||
condition?: (field: SettingField) => boolean;
|
||||
initialValue?: (field: SettingField) => any;
|
||||
initialValue?: any | ((field: SettingField) => any);
|
||||
list: boolean;
|
||||
}
|
||||
|
||||
function nomalizeSetters(setters?: Array<string | SetterConfig | CustomView | DynamicSetter>): SetterItem[] {
|
||||
@ -28,6 +48,7 @@ function nomalizeSetters(setters?: Array<string | SetterConfig | CustomView | Dy
|
||||
setter: name,
|
||||
condition: setter.condition,
|
||||
initialValue: setter.initialValue,
|
||||
list: setter.recommend || false,
|
||||
});
|
||||
});
|
||||
return normalized;
|
||||
@ -42,9 +63,10 @@ function nomalizeSetters(setters?: Array<string | SetterConfig | CustomView | Dy
|
||||
names.push(got);
|
||||
return got;
|
||||
}
|
||||
return setters.map(setter => {
|
||||
return setters.map((setter) => {
|
||||
const config: any = {
|
||||
setter,
|
||||
list: true,
|
||||
};
|
||||
if (isSetterConfig(setter)) {
|
||||
config.setter = setter.componentName;
|
||||
@ -76,149 +98,158 @@ function nomalizeSetters(setters?: Array<string | SetterConfig | CustomView | Dy
|
||||
});
|
||||
}
|
||||
|
||||
@observer
|
||||
export default class MixedSetter extends Component<{
|
||||
field: SettingField;
|
||||
setters?: Array<string | SetterConfig | CustomView | DynamicSetter>;
|
||||
onSetterChange?: (field: SettingField, name: string) => void;
|
||||
onChange?: (val: any) => void;
|
||||
value?: any;
|
||||
className?: string;
|
||||
}> {
|
||||
private setters = nomalizeSetters(this.props.setters);
|
||||
@obx.ref private used?: string;
|
||||
@computed private getCurrentSetter() {
|
||||
const { field } = this.props;
|
||||
if (this.used != null) {
|
||||
const selected = this.used;
|
||||
if (selected.condition) {
|
||||
if (selected.condition(field)) {
|
||||
return selected;
|
||||
let firstMatched: SetterItem | undefined;
|
||||
for (const setter of this.setters) {
|
||||
const matched = !setter.condition || setter.condition(field);
|
||||
if (matched) {
|
||||
if (setter.name === this.used) {
|
||||
return setter;
|
||||
}
|
||||
if (!firstMatched) {
|
||||
firstMatched = setter;
|
||||
}
|
||||
} else {
|
||||
return selected;
|
||||
}
|
||||
}
|
||||
return this.setters.find(item => {
|
||||
if (!item.condition) {
|
||||
return true;
|
||||
}
|
||||
return item.condition(field);
|
||||
});
|
||||
return firstMatched;
|
||||
}
|
||||
|
||||
|
||||
private useSetter: (id: string) => {
|
||||
private useSetter = (name: string) => {
|
||||
if (name === this.used) {
|
||||
return;
|
||||
}
|
||||
const { field, onChange } = this.props;
|
||||
const newValue = setter.initialValue?.(field);
|
||||
this.used = setter;
|
||||
onChange && onChange(newValue);
|
||||
const setter = this.setters.find((item) => item.name === name);
|
||||
this.used = name;
|
||||
if (setter) {
|
||||
let newValue: any = setter.initialValue;
|
||||
if (newValue && typeof newValue === 'function') {
|
||||
newValue = newValue(field);
|
||||
}
|
||||
onChange && onChange(newValue);
|
||||
}
|
||||
};
|
||||
|
||||
private shell: HTMLDivElement | null = null;
|
||||
private checkIsBlockField() {
|
||||
if (this.shell) {
|
||||
const setter = this.shell.firstElementChild;
|
||||
if (setter && setter.classList.contains('lc-block-setter')) {
|
||||
this.shell.classList.add('lc-block-setter');
|
||||
} else {
|
||||
this.shell.classList.remove('lc-block-setter');
|
||||
}
|
||||
}
|
||||
}
|
||||
componentDidUpdate() {
|
||||
this.checkIsBlockField();
|
||||
}
|
||||
componentDidMount() {
|
||||
this.checkIsBlockField();
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
style = {},
|
||||
className,
|
||||
types = [],
|
||||
defaultType,
|
||||
...restProps
|
||||
} = this.props;
|
||||
this.typeMap = {};
|
||||
let realTypes: any[] = [];
|
||||
types.forEach( (el: { name: any; props: any; }) => {
|
||||
const { name, props } = el;
|
||||
const Setter = getSetter(name);
|
||||
if (Setter) {
|
||||
this.typeMap[name] = {
|
||||
label: name,
|
||||
component: Setter.component,
|
||||
props,
|
||||
const { className, field, setters, onSetterChange, ...restProps } = this.props;
|
||||
|
||||
const currentSetter = this.getCurrentSetter();
|
||||
const isTwoType = this.setters.length < 3;
|
||||
|
||||
let setterContent: any;
|
||||
const triggerTitle: any = {
|
||||
tip: {
|
||||
type: 'i18n',
|
||||
'zh-CN': '切换格式',
|
||||
'en-US': 'Switch Format',
|
||||
},
|
||||
icon: <IconConvert size={24} />,
|
||||
};
|
||||
if (currentSetter) {
|
||||
const { setter, title, props } = currentSetter;
|
||||
let setterProps: any = {};
|
||||
let setterType: any;
|
||||
if (isDynamicSetter(setter)) {
|
||||
setterType = setter(field);
|
||||
} else {
|
||||
setterType = setter;
|
||||
}
|
||||
if (props) {
|
||||
setterProps = props;
|
||||
if (typeof setterProps === 'function') {
|
||||
setterProps = setterProps(field);
|
||||
}
|
||||
}
|
||||
realTypes.push(name);
|
||||
})
|
||||
let moreBtnNode = null;
|
||||
//如果只有2种,且有变量表达式,则直接展示变量按钮
|
||||
if (realTypes.length > 1) {
|
||||
let isTwoType = !!(realTypes.length === 2 && ~realTypes.indexOf('ExpressionSetter'));
|
||||
let btnProps = {
|
||||
size: 'small',
|
||||
text: true,
|
||||
style: {
|
||||
position: 'absolute',
|
||||
left: '100%',
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
margin: 'auto 0 auto 8px',
|
||||
padding: 0,
|
||||
width: 16,
|
||||
height: 16,
|
||||
lineHeight: '16px',
|
||||
textAlign: 'center'
|
||||
|
||||
setterContent = createSetterContent(setterType, {
|
||||
...shallowIntl(setterProps),
|
||||
field,
|
||||
...restProps,
|
||||
});
|
||||
if (title) {
|
||||
if (typeof title !== 'object' || isI18nData(title) || isValidElement(title)) {
|
||||
triggerTitle.tip = title;
|
||||
} else {
|
||||
triggerTitle.tip = title.tip || title.label;
|
||||
}
|
||||
};
|
||||
if (isTwoType) {
|
||||
btnProps.onClick = this.changeType.bind(this, realTypes.indexOf(this.state.type) ? realTypes[0] : realTypes[1]);
|
||||
}
|
||||
} else {
|
||||
// 未匹配的 null 值,显示 NullValue 空值
|
||||
// 未匹配的 其它 值,显示 InvalidValue 非法值
|
||||
let triggerNode = (
|
||||
<Button {...btnProps} size={isTwoType ? 'large' : 'small'}>
|
||||
<Icon type={isTwoType ? 'edit' : 'ellipsis'} />
|
||||
</Button>
|
||||
);
|
||||
if (isTwoType) {
|
||||
moreBtnNode = triggerNode;
|
||||
if (restProps.value == null) {
|
||||
setterContent = <span>NullValue</span>;
|
||||
} else {
|
||||
let MenuItems: {} | null | undefined = [];
|
||||
realTypes.map(type => {
|
||||
if (this.typeMap[type]) {
|
||||
MenuItems.push(<Menu.Item key={type}></Menu.Item>);
|
||||
} else {
|
||||
console.error(
|
||||
this.i18n('typeError', {
|
||||
type
|
||||
})
|
||||
);
|
||||
}
|
||||
});
|
||||
let MenuNode = (
|
||||
<Menu
|
||||
selectMode="single"
|
||||
hasSelectedIcon={false}
|
||||
selectedKeys={this.used}
|
||||
onItemClick={this.useSetter}
|
||||
>
|
||||
{this.setters.map((setter) => {
|
||||
return <Menu.Item key={setter.name}>
|
||||
<Title title={setter.title} />
|
||||
</Menu.Item>
|
||||
})}
|
||||
</Menu>
|
||||
);
|
||||
|
||||
moreBtnNode = (
|
||||
<Dropdown trigger={triggerNode} triggerType="click">
|
||||
<Menu
|
||||
selectMode="single"
|
||||
hasSelectedIcon={false}
|
||||
selectedKeys={this.used}
|
||||
onItemClick={this.useSetter}
|
||||
>
|
||||
{this.setters.map((setter) => {
|
||||
return <Menu.Item key={setter.name}>
|
||||
<Title title={setter.title} />
|
||||
</Menu.Item>
|
||||
})}
|
||||
</Menu>
|
||||
</Dropdown>
|
||||
);
|
||||
setterContent = <span>InvalidValue</span>;
|
||||
}
|
||||
}
|
||||
let TargetNode = this.typeMap[this.state.type]?.component || 'div';
|
||||
let targetProps = this.typeMap[this.state.type]?.props || {};
|
||||
let tarStyle = { position: 'relative', ...style };
|
||||
let classes = classNames(className, 'lowcode-setter-mixin');
|
||||
const usedName = currentSetter?.name || this.used;
|
||||
let moreBtnNode = (
|
||||
<Title
|
||||
title={triggerTitle}
|
||||
onClick={
|
||||
isTwoType
|
||||
? () => {
|
||||
if (this.setters[0]?.name === usedName) {
|
||||
this.useSetter(this.setters[1]?.name);
|
||||
} else {
|
||||
this.useSetter(this.setters[0]?.name);
|
||||
}
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
);
|
||||
if (!isTwoType) {
|
||||
moreBtnNode = (
|
||||
<Dropdown trigger={moreBtnNode} triggerType="click" align="tr br">
|
||||
<Menu selectMode="single" hasSelectedIcon={true} selectedKeys={usedName} onItemClick={this.useSetter}>
|
||||
{this.setters.filter(setter => setter.list || setter.name === usedName).map((setter) => {
|
||||
return (
|
||||
<Menu.Item key={setter.name}>
|
||||
<Title title={setter.title} />
|
||||
</Menu.Item>
|
||||
);
|
||||
})}
|
||||
</Menu>
|
||||
</Dropdown>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div style={tarStyle} className={classes} >
|
||||
{createSetterContent()}
|
||||
{moreBtnNode}
|
||||
<div ref={(shell) => (this.shell = shell)} className={classNames('lc-setter-mixed', className)}>
|
||||
{setterContent}
|
||||
|
||||
<div className="lc-setter-actions">{moreBtnNode}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -0,0 +1,11 @@
|
||||
.lc-setter-mixed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
.lc-setter-actions {
|
||||
margin-left: 5px;
|
||||
}
|
||||
&.lc-block-setter {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,29 @@
|
||||
import { registerSetter } from '@ali/lowcode-globals';
|
||||
import { registerSetter, isPlainObject } from '@ali/lowcode-globals';
|
||||
import ArraySetter from './array-setter';
|
||||
import ObjectSetter from './object-setter';
|
||||
import MixedSetter from './mixed-setter';
|
||||
|
||||
registerSetter('ArraySetter', ArraySetter);
|
||||
registerSetter('ObjectSetter', ObjectSetter);
|
||||
registerSetter('ArraySetter', {
|
||||
component: ArraySetter,
|
||||
defaultProps: {},
|
||||
title: 'ArraySetter', // TODO
|
||||
condition: (field: any) => {
|
||||
const v = field.getValue();
|
||||
return v == null || Array.isArray(v);
|
||||
},
|
||||
initialValue: [],
|
||||
recommend: true,
|
||||
});
|
||||
registerSetter('ObjectSetter', {
|
||||
component: ObjectSetter,
|
||||
// todo: defaultProps
|
||||
defaultProps: {},
|
||||
title: 'ObjectSetter', // TODO
|
||||
condition: (field: any) => {
|
||||
const v = field.getValue();
|
||||
return v == null || isPlainObject(v);
|
||||
},
|
||||
initialValue: {},
|
||||
recommend: true,
|
||||
});
|
||||
registerSetter('MixedSetter', MixedSetter);
|
||||
|
||||
@ -404,11 +404,10 @@ export class SettingPropEntry implements SettingTarget {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
/*
|
||||
getDefaultValue() {
|
||||
return this.extraProps.defaultValue;
|
||||
}
|
||||
|
||||
/*
|
||||
getConfig<K extends keyof IPropConfig>(configName?: K): IPropConfig[K] | IPropConfig {
|
||||
if (configName) {
|
||||
return this.config[configName];
|
||||
|
||||
@ -38,8 +38,6 @@ export class SettingField extends SettingPropEntry implements SettingTarget {
|
||||
constructor(readonly parent: SettingTarget, config: FieldConfig) {
|
||||
super(parent, config.name, config.type);
|
||||
|
||||
console.info(config);
|
||||
|
||||
const { title, items, setter, extraProps, ...rest } = config;
|
||||
this._title = title;
|
||||
this._setter = setter;
|
||||
|
||||
@ -25,6 +25,7 @@ class SettingFieldView extends Component<{ field: SettingField }> {
|
||||
return null;
|
||||
}
|
||||
const { setter } = field;
|
||||
|
||||
let setterProps: any = {};
|
||||
let setterType: any;
|
||||
if (Array.isArray(setter)) {
|
||||
|
||||
@ -370,10 +370,12 @@ export function upgradePropConfig(config: OldPropConfig) {
|
||||
const setters = Array.isArray(primarySetter) ? primarySetter.concat('ExpressionSetter') : [primarySetter, 'ExpressionSetter'];
|
||||
primarySetter = {
|
||||
componentName: 'MixedSetter',
|
||||
setters,
|
||||
onSetterChange: (field: Field, name: string) => {
|
||||
if (useVariableChange) {
|
||||
useVariableChange.call(field, { isUseVariable: name === 'ExpressionSetter' });
|
||||
props: {
|
||||
setters,
|
||||
onSetterChange: (field: Field, name: string) => {
|
||||
if (useVariableChange) {
|
||||
useVariableChange.call(field, { isUseVariable: name === 'ExpressionSetter' });
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -7,6 +7,7 @@ const { editor } = Engine;
|
||||
Engine.init();
|
||||
|
||||
load();
|
||||
Engine.Env.setEnv('RE_VERSION', "5.0.1");
|
||||
|
||||
async function load() {
|
||||
await loadAssets();
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user