lowcode-engine/packages/setters/src/mixin-setter.tsx
2020-03-27 14:01:27 +08:00

302 lines
9.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import { Dropdown, Button, Menu, Icon, Input, NumberPicker, Switch, Select, Radio, DatePicker } from '@alifd/next';
import { isJSExpression, generateI18n } from './locale/utils';
import Expression from './expression-setter';
import zhCN from './locale/zh-CN';
import './index.scss';
const { Group: RadioGroup } = Radio;
// const isJSExpression = (obj) => {
// if(typeof obj === 'object' && obj.type === 'JSExpression') {
// return true;
// }
// return false;
// }
export default class Mixin extends PureComponent {
static displayName = 'Mixin';
static propTypes = {
locale: PropTypes.string,
messages: PropTypes.object,
defaultType: PropTypes.string,
types: PropTypes.arrayOf(PropTypes.string),
onlyChangeType: PropTypes.bool,
inputProps: PropTypes.object,
expressionProps: PropTypes.object,
monacoEditorProps: PropTypes.object,
switchProps: PropTypes.object,
selectProps: PropTypes.object,
radioGroupProps: PropTypes.object,
};
static defaultProps = {
locale: 'zh-CN',
messages: zhCN,
types: ['StringSetter', 'ExpressionSetter', 'NumberSetter', 'BoolSetter', 'SelectSetter', 'RadioGroupSetter'],
};
constructor(props) {
super(props);
let type = judgeTypeHandler(props, {});
this.i18n = generateI18n(props.locale, props.messages);
this.state = {
preType: type,
type
};
}
static getDerivedStateFromProps(props, state) {
if ('value' in props) {
let curType = judgeTypeHandler(props, state);
if (curType !== state.preType) {
return {
type: curType
};
}
}
return null;
}
changeType(type) {
if (typeof type === 'object' || type === this.state.type) return;
let { onlyChangeType, value, onChange } = this.props;
if (onlyChangeType) {
this.setState({ type });
onChange && onChange(value);
} else {
let newValue = undefined;
if (this.typeMap[type]['props']) {
if (this.typeMap[type]['props']['value'] !== undefined) {
newValue = this.typeMap[type]['props']['value'];
} else if (this.typeMap[type]['props']['defaultValue'] !== undefined) {
newValue = this.typeMap[type]['props']['defaultValue'];
}
}
if (type === 'BoolSetter' && newValue === undefined) {
newValue = false; //给切换到switch默认值为false
}
this.setState({ type });
onChange && onChange(newValue);
}
}
render() {
const {
style = {},
className,
locale,
messages,
types = [],
defaultType,
// inputProps,
// expressionProps,
// monacoEditorProps,
// numberPickerProps,
// switchProps,
// selectProps,
// radioGroupProps,
...restProps
} = this.props;
this.typeMap = {
StringSetter: {
label: this.i18n('input'),
component: Input,
// props: inputProps
},
ExpressionSetter: {
label: this.i18n('expression'),
component: Expression,
// props: expressionProps
},
// MonacoEditor: {
// label: this.i18n('monacoEditor'),
// component: MonacoEditor,
// props: monacoEditorProps
// },
NumberSetter: {
label: this.i18n('numberPicker'),
component: NumberPicker,
},
BoolSetter: {
label: this.i18n('bool'),
component: Switch,
},
SelectSetter: {
label: this.i18n('select'),
component: Select,
},
RadioGroupSetter: {
label: this.i18n('radio'),
component: RadioGroup,
},
TextAreaSetter: {
label: this.i18n('textarea'),
component: Input.TextArea,
},
DateSetter: {
label: this.i18n('date'),
component: DatePicker,
},
DateYearSetter: {
label: this.i18n('dateYear'),
component: DatePicker,
},
DateMonthSetter: {
label: this.i18n('dateMonth'),
component: DatePicker,
},
DateRangeSetter: {
label: this.i18n('dateRange'),
component: DatePicker,
}
};
let realTypes = [];
types.forEach( el => {
const { name, props } = el;
if (this.typeMap[name]) {
this.typeMap[name].props = props;
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'
}
};
if (isTwoType) {
btnProps.onClick = this.changeType.bind(this, realTypes.indexOf(this.state.type) ? realTypes[0] : realTypes[1]);
}
let triggerNode = (
<Button {...btnProps} size={isTwoType ? 'large' : 'small'}>
<Icon type={isTwoType ? 'edit' : 'ellipsis'} />
</Button>
);
if (isTwoType) {
moreBtnNode = triggerNode;
} else {
let MenuItems = [];
realTypes.map(type => {
if (this.typeMap[type]) {
MenuItems.push(<Menu.Item key={type}>{this.typeMap[type]['label']}</Menu.Item>);
} else {
console.error(
this.i18n('typeError', {
type
})
);
}
});
let MenuNode = (
<Menu
selectMode="single"
hasSelectedIcon={false}
selectedKeys={this.state.type}
onItemClick={this.changeType.bind(this)}
>
{MenuItems}
</Menu>
);
moreBtnNode = (
<Dropdown trigger={triggerNode} triggerType="click">
{MenuNode}
</Dropdown>
);
}
}
let TargetNode = this.typeMap[this.state.type] ? this.typeMap[this.state.type]['component'] : 'div';
let targetProps = this.typeMap[this.state.type] ? this.typeMap[this.state.type]['props'] : {};
// 特殊处理Switch的值
if (['BoolSetter', 'RadioGroupSetter'].includes(this.state.type)) {
restProps.checked = this.props.checked !== undefined ? this.props.checked : this.props.value;
}
//判断如果Mixin内部有设置onChange, 则同时触发2处onChange
if (targetProps && targetProps.onChange && typeof targetProps.onChange === 'function') {
let tarOnChange = targetProps.onChange;
targetProps.onChange = function() {
tarOnChange.apply(null, arguments);
restProps.onChange && restProps.onChange.apply(null, arguments);
};
}
let tarStyle = { position: 'relative', ...style };
let classes = classNames(className, 'lowcode-setter-mixin');
return (
<div style={tarStyle} className={classes}>
<TargetNode {...restProps} {...targetProps} />
{moreBtnNode}
</div>
);
}
}
// 判断值类型
function judgeTypeHandler(props, state) {
let { defaultType, types, value } = props;
let selectProps: { dataSource: any[]; };
let radioGroupProps: { dataSource: any[]; };
let typeKeys: any[] = [];
types.forEach( el => {
typeKeys.push(el.name);
})
types.forEach((el: { name: string; props: {}; }) => {
if (el.name === 'SelectSetter') {selectProps === el.props;}
if (el.name === 'RadioGroupSetter') {radioGroupProps === el.props;}
})
if (!defaultType || !typeKeys) return;
// 如果defaultType不在typeKeys列表中默认返回typeKeys的第一项
if (!typeKeys.includes(defaultType)) return typeKeys[0];
if (isJSExpression(value)) return 'ExpressionSetter';
if (value && typeof value === 'string') {
if (~typeKeys.indexOf('SelectSetter') && selectProps && selectProps.dataSource) {
let hasOption = selectProps.dataSource.some(item => {
if (typeof item === 'string' && item === value) return true;
if (typeof item === 'object' && item.value === value) return true;
});
if (hasOption) return 'SelectSetter';
}
if (~typeKeys.indexOf('RadioGroupSetter') && radioGroupProps && radioGroupProps.dataSource) {
let hasOption = radioGroupProps.dataSource.some(item => {
if (typeof item === 'object' && item.value === value) return true;
});
if (hasOption) return 'RadioGroupSetter';
}
if (~typeKeys.indexOf('StringSetter')) return 'StringSetter';
}
if (typeof value === 'number') {
if (~typeKeys.indexOf('SelectSetter') && selectProps && selectProps.dataSource) {
let hasOption = selectProps.dataSource.some(item => {
if (typeof item === 'object' && item.value === value) return true;
});
if (hasOption) return 'Select';
}
if (~typeKeys.indexOf('RadioGroupSetter') && radioGroupProps && radioGroupProps.dataSource) {
let hasOption = radioGroupProps.dataSource.some(item => {
if (typeof item === 'object' && item.value === value) return true;
});
if (hasOption) return 'RadioGroupSetter';
}
if (~typeKeys.indexOf('NumberSetter')) return 'NumberSetter';
}
if (~typeKeys.indexOf('NumberSetter') && typeof value === 'number') return 'NumberSetter';
if (~typeKeys.indexOf('BoolSetter') && (value === false || value === true)) return 'BoolSetter';
if (Array.isArray(value)) {
if (~typeKeys.indexOf('SelectSetter') && typeof value[0] === 'string') return 'SelectSetter';
}
return state.type || defaultType;
}