From a15bfd85179644649703450fcdac35758c9ff082 Mon Sep 17 00:00:00 2001 From: kangwei Date: Tue, 21 Apr 2020 18:07:56 +0800 Subject: [PATCH] support mixed setter --- packages/globals/src/di/setter.ts | 1 + .../plugin-settings-pane/src/field/fields.tsx | 2 +- .../plugin-settings-pane/src/field/index.less | 7 +- .../src/icons/convert.tsx | 16 + .../src/setters/mixed-setter/index.tsx | 275 ++++++++++-------- .../src/setters/mixed-setter/style.less | 11 + .../src/setters/register.ts | 29 +- .../src/settings/setting-entry.ts | 3 +- .../src/settings/setting-field.ts | 2 - .../src/settings/settings-pane.tsx | 1 + .../src/bundle/upgrade-metadata.ts | 10 +- packages/vision-polyfill/src/demo/index.ts | 1 + 12 files changed, 223 insertions(+), 135 deletions(-) create mode 100644 packages/plugin-settings-pane/src/icons/convert.tsx create mode 100644 packages/plugin-settings-pane/src/setters/mixed-setter/style.less diff --git a/packages/globals/src/di/setter.ts b/packages/globals/src/di/setter.ts index 23344c15b..fb162022f 100644 --- a/packages/globals/src/di/setter.ts +++ b/packages/globals/src/di/setter.ts @@ -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 { check(); observer.observe(body, { childList: true, - subtree: false, + subtree: true, attributes: true, attributeFilter: ['class'], }); diff --git a/packages/plugin-settings-pane/src/field/index.less b/packages/plugin-settings-pane/src/field/index.less index fefda56e6..5fecb74d9 100644 --- a/packages/plugin-settings-pane/src/field/index.less +++ b/packages/plugin-settings-pane/src/field/index.less @@ -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; diff --git a/packages/plugin-settings-pane/src/icons/convert.tsx b/packages/plugin-settings-pane/src/icons/convert.tsx new file mode 100644 index 000000000..71ab8630e --- /dev/null +++ b/packages/plugin-settings-pane/src/icons/convert.tsx @@ -0,0 +1,16 @@ +import { SVGIcon, IconProps } from "@ali/lowcode-globals"; + +export function IconConvert(props: IconProps) { + return ( + + + + + + + + + ); +} + +IconConvert.displayName = 'Convert'; diff --git a/packages/plugin-settings-pane/src/setters/mixed-setter/index.tsx b/packages/plugin-settings-pane/src/setters/mixed-setter/index.tsx index 6eb0dabc5..763a7c1a9 100644 --- a/packages/plugin-settings-pane/src/setters/mixed-setter/index.tsx +++ b/packages/plugin-settings-pane/src/setters/mixed-setter/index.tsx @@ -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): SetterItem[] { @@ -28,6 +48,7 @@ function nomalizeSetters(setters?: Array { + 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; 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: , + }; + 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 = ( - - ); - if (isTwoType) { - moreBtnNode = triggerNode; + if (restProps.value == null) { + setterContent = NullValue; } else { - let MenuItems: {} | null | undefined = []; - realTypes.map(type => { - if (this.typeMap[type]) { - MenuItems.push(); - } else { - console.error( - this.i18n('typeError', { - type - }) - ); - } - }); - let MenuNode = ( - - {this.setters.map((setter) => { - return - - </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> ); } diff --git a/packages/plugin-settings-pane/src/setters/mixed-setter/style.less b/packages/plugin-settings-pane/src/setters/mixed-setter/style.less new file mode 100644 index 000000000..31f79b4c9 --- /dev/null +++ b/packages/plugin-settings-pane/src/setters/mixed-setter/style.less @@ -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; + } +} diff --git a/packages/plugin-settings-pane/src/setters/register.ts b/packages/plugin-settings-pane/src/setters/register.ts index 14c154404..13eb3f56f 100644 --- a/packages/plugin-settings-pane/src/setters/register.ts +++ b/packages/plugin-settings-pane/src/setters/register.ts @@ -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); diff --git a/packages/plugin-settings-pane/src/settings/setting-entry.ts b/packages/plugin-settings-pane/src/settings/setting-entry.ts index cd70724db..8d56ce56c 100644 --- a/packages/plugin-settings-pane/src/settings/setting-entry.ts +++ b/packages/plugin-settings-pane/src/settings/setting-entry.ts @@ -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]; diff --git a/packages/plugin-settings-pane/src/settings/setting-field.ts b/packages/plugin-settings-pane/src/settings/setting-field.ts index 2e6dfde28..229d575fc 100644 --- a/packages/plugin-settings-pane/src/settings/setting-field.ts +++ b/packages/plugin-settings-pane/src/settings/setting-field.ts @@ -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; diff --git a/packages/plugin-settings-pane/src/settings/settings-pane.tsx b/packages/plugin-settings-pane/src/settings/settings-pane.tsx index 4040468bd..0501a9710 100644 --- a/packages/plugin-settings-pane/src/settings/settings-pane.tsx +++ b/packages/plugin-settings-pane/src/settings/settings-pane.tsx @@ -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)) { diff --git a/packages/vision-polyfill/src/bundle/upgrade-metadata.ts b/packages/vision-polyfill/src/bundle/upgrade-metadata.ts index ce50f37ed..0c4ac65cf 100644 --- a/packages/vision-polyfill/src/bundle/upgrade-metadata.ts +++ b/packages/vision-polyfill/src/bundle/upgrade-metadata.ts @@ -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' }); + } } } }; diff --git a/packages/vision-polyfill/src/demo/index.ts b/packages/vision-polyfill/src/demo/index.ts index fca8c9a03..3f0ecbe70 100644 --- a/packages/vision-polyfill/src/demo/index.ts +++ b/packages/vision-polyfill/src/demo/index.ts @@ -7,6 +7,7 @@ const { editor } = Engine; Engine.init(); load(); +Engine.Env.setEnv('RE_VERSION', "5.0.1"); async function load() { await loadAssets();