下拉选择类组件新增事件动作&demo

This commit is contained in:
xujiahao01 2022-02-15 16:15:19 +08:00
parent 85dd91640f
commit a76d34a414
7 changed files with 436 additions and 79 deletions

View File

@ -1,3 +1,61 @@
const change = {
actions: [
{
actionType: 'dialog',
args: {
val: '${event.data.value}'
},
dialog: {
title: '派发change事件',
data: {
val: '${val}'
},
body: [
{
type: 'tpl',
tpl: '值更新:${val}'
}
]
}
}
]
};
const blur = {
actions: [
{
actionType: 'toast',
msgType: 'info',
msg: '派发blur事件'
}
]
};
const focus = {
actions: [
{
actionType: 'toast',
msgType: 'info',
msg: '派发focus事件'
}
]
};
const options = [
{
label: '选项A',
value: 'A'
},
{
label: '选项B',
value: 'B'
},
{
label: '选项C',
value: 'C'
}
];
export default { export default {
type: 'page', type: 'page',
title: '下拉框组件事件', title: '下拉框组件事件',
@ -43,24 +101,234 @@ export default {
value: 'A,B,C', value: 'A,B,C',
multiple: true, multiple: true,
checkAll: true, checkAll: true,
options: [ options,
{ onEvent: {
label: '选项A', change,
value: 'A' blur,
}, focus
{ }
label: '选项B',
value: 'B'
},
{
label: '选项C',
value: 'C'
}
]
} }
] ]
} }
] ]
} },
] {
type: 'tpl',
tpl: 'inputTag标签选择器',
inline: false,
wrapperComponent: 'h2'
},
{
type: 'form',
debug: true,
body: [
{
type: 'group',
body: [
{
name: 'trigger2',
id: 'trigger2',
type: 'action',
label: 'clear触发器',
level: 'primary',
onEvent: {
click: {
actions: [
{
actionType: 'clear',
componentId: 'clear-input-tag',
description: '点击清空指定下拉框选中值'
}
]
}
}
},
{
name: 'clear-input-tag',
id: 'clear-input-tag',
type: 'input-tag',
label: 'clear动作测试',
mode: 'row',
value: 'A,B',
multiple: true,
options,
onEvent: {
change,
blur,
focus
}
},
]
}
]
},
{
type: 'tpl',
tpl: 'matrix-checkboxes矩阵勾选',
inline: false,
wrapperComponent: 'h2'
},
{
type: 'form',
debug: true,
body: [
{
type: 'group',
body: [
{
name: 'trigger3',
id: 'trigger3',
type: 'action',
label: 'clear触发器',
level: 'primary',
onEvent: {
click: {
actions: [
{
actionType: 'clear',
componentId: 'clear-matrix-checkboxes',
description: '点击清空指定下拉框选中值'
}
]
}
}
},
{
name: 'clear-matrix-checkboxes',
id: 'clear-matrix-checkboxes',
type: 'matrix-checkboxes',
rowLabel: "行标题说明",
columns: [
{
label: '列1'
},
{
label: '列2'
}
],
rows: [
{
label: '行1'
},
{
label: '行2'
}
],
onEvent: {
change
}
},
]
}
]
},
{
type: 'tpl',
tpl: 'radios单选框',
inline: false,
wrapperComponent: 'h2'
},
{
type: 'form',
debug: true,
body: [
{
type: 'group',
body: [
{
name: 'trigger4',
id: 'trigger4',
type: 'action',
label: 'clear触发器',
level: 'primary',
onEvent: {
click: {
actions: [
{
actionType: 'clear',
componentId: 'clear-radios',
description: '点击清空指定下拉框选中值'
}
]
}
}
},
{
name: 'clear-radios',
id: 'clear-radios',
type: "radios",
options,
onEvent: {
change
}
},
]
}
]
},
{
type: 'tpl',
tpl: 'options类',
inline: false,
wrapperComponent: 'h2'
},
{
type: 'form',
debug: true,
body: [
{
type: 'group',
body: [
{
name: 'trigger5',
id: 'trigger5',
type: 'action',
label: 'clear触发器',
level: 'primary',
onEvent: {
click: {
actions: [
{
actionType: 'clear',
componentId: 'clear-options',
description: '点击清空指定下拉框选中值'
}
]
}
}
},
{
name: 'clear-options',
id: 'clear-options',
type: 'checkboxes',
options,
onEvent: {
change
}
},
{
name: 'clear-options',
id: 'clear-options',
type: 'button-group-select',
options,
onEvent: {
change
}
},
{
name: 'clear-options',
id: 'clear-options',
type: 'list-select',
options,
onEvent: {
change
}
},
]
}
]
},
],
}; };

View File

@ -550,7 +550,7 @@ export const examples = [
component: makeSchemaRenderer(UploadEventSchema) component: makeSchemaRenderer(UploadEventSchema)
}, },
{ {
label: '下拉', label: '下拉选择类',
path: '/examples/event/select', path: '/examples/event/select',
component: makeSchemaRenderer(SelectEventActionSchema) component: makeSchemaRenderer(SelectEventActionSchema)
}, },

View File

@ -9,11 +9,12 @@ import Downshift from 'downshift';
import find from 'lodash/find'; import find from 'lodash/find';
import {findDOMNode} from 'react-dom'; import {findDOMNode} from 'react-dom';
import ResultBox from '../../components/ResultBox'; import ResultBox from '../../components/ResultBox';
import {autobind, filterTree} from '../../utils/helper'; import {autobind, filterTree, createObject} from '../../utils/helper';
import Spinner from '../../components/Spinner'; import Spinner from '../../components/Spinner';
import Overlay from '../../components/Overlay'; import Overlay from '../../components/Overlay';
import PopOver from '../../components/PopOver'; import PopOver from '../../components/PopOver';
import ListMenu from '../../components/ListMenu'; import ListMenu from '../../components/ListMenu';
import {Action} from '../../types';
/** /**
* Tag * Tag
@ -80,46 +81,86 @@ export default class TagControl extends React.PureComponent<
} }
} }
addItem(option: Option) { doAction(action: Action, data: object, throwErrors: boolean) {
const {resetValue, onChange} = this.props;
if (action.actionType === 'clear') {
onChange(resetValue ?? '');
}
}
@autobind
async dispatchEvent(eventName: string, e: any = {}) {
const {dispatchEvent, options, data} = this.props;
const rendererEvent = await dispatchEvent(
eventName,
createObject(e, {
options,
...data
})
);
// 返回阻塞标识
return !!rendererEvent?.prevented;
}
@autobind
getValue(type: 'push' | 'pop' = 'pop', option: any = {}) {
const { const {
selectedOptions, selectedOptions,
onChange,
joinValues, joinValues,
extractValue, extractValue,
delimiter, delimiter,
valueField valueField
} = this.props; } = this.props;
const newValue = selectedOptions.concat();
if (type === 'push') {
newValue.push(option);
} else {
newValue.pop();
}
const newValueRes = joinValues
? newValue
.map(item => item[valueField || 'value'])
.join(delimiter || ',')
: extractValue
? newValue.map(item => item[valueField || 'value'])
: newValue;
return newValueRes;
}
async addItem(option: Option) {
const {
selectedOptions,
onChange
} = this.props;
const newValue = selectedOptions.concat(); const newValue = selectedOptions.concat();
if (find(newValue, item => item.value == option.value)) { if (find(newValue, item => item.value == option.value)) {
return; return;
} }
newValue.push(option); const newValueRes = this.getValue('push', option);
onChange( const isPrevented = await this.dispatchEvent('change', {
joinValues value: newValueRes
? newValue });
.map(item => item[valueField || 'value']) isPrevented || onChange(newValueRes);
.join(delimiter || ',')
: extractValue
? newValue.map(item => item[valueField || 'value'])
: newValue
);
} }
@autobind @autobind
handleFocus(e: any) { async handleFocus(e: any) {
this.setState({ this.setState({
isFocused: true, isFocused: true,
isOpened: true isOpened: true
}); });
this.props.onFocus?.(e); const isPrevented = await this.dispatchEvent('focus', e);
isPrevented || this.props.onFocus?.(e);
} }
@autobind @autobind
handleBlur(e: any) { async handleBlur(e: any) {
const { const {
selectedOptions, selectedOptions,
onChange, onChange,
@ -130,7 +171,9 @@ export default class TagControl extends React.PureComponent<
} = this.props; } = this.props;
const value = this.state.inputValue.trim(); const value = this.state.inputValue.trim();
this.props.onBlur?.(e);
const isPrevented = await this.dispatchEvent('blur', e);
isPrevented || this.props.onBlur?.(e);
this.setState( this.setState(
{ {
isFocused: false, isFocused: false,
@ -176,7 +219,7 @@ export default class TagControl extends React.PureComponent<
} }
@autobind @autobind
handleChange(value: Array<Option>) { async handleChange(value: Array<Option>) {
const {joinValues, extractValue, delimiter, valueField, onChange} = const {joinValues, extractValue, delimiter, valueField, onChange} =
this.props; this.props;
@ -190,7 +233,10 @@ export default class TagControl extends React.PureComponent<
newValue = newValue.join(delimiter || ','); newValue = newValue.join(delimiter || ',');
} }
onChange(newValue); const isPrevented = await this.dispatchEvent('change', {
value: newValue
});
isPrevented || onChange(newValue);
} }
@autobind @autobind
@ -200,51 +246,35 @@ export default class TagControl extends React.PureComponent<
} }
@autobind @autobind
handleKeyDown(evt: React.KeyboardEvent<HTMLInputElement>) { async handleKeyDown(evt: React.KeyboardEvent<HTMLInputElement>) {
const { const {
selectedOptions, selectedOptions,
onChange, onChange,
joinValues, delimiter
extractValue,
delimiter,
valueField
} = this.props; } = this.props;
const value = this.state.inputValue.trim(); const value = this.state.inputValue.trim();
if (selectedOptions.length && !value && evt.key == 'Backspace') { if (selectedOptions.length && !value && evt.key == 'Backspace') {
const newValue = selectedOptions.concat(); const newValueRes = this.getValue('pop');
newValue.pop(); const isPrevented = await this.dispatchEvent('change', {
value: newValueRes
onChange( });
joinValues isPrevented || onChange(newValueRes);
? newValue
.map(item => item[valueField || 'value'])
.join(delimiter || ',')
: extractValue
? newValue.map(item => item[valueField || 'value'])
: newValue
);
} else if (value && (evt.key === 'Enter' || evt.key === delimiter)) { } else if (value && (evt.key === 'Enter' || evt.key === delimiter)) {
evt.preventDefault(); evt.preventDefault();
evt.stopPropagation(); evt.stopPropagation();
const newValue = selectedOptions.concat(); const newValue = selectedOptions.concat();
if (!find(newValue, item => item.value == value)) { if (!find(newValue, item => item.value == value)) {
newValue.push({ const newValueRes = this.getValue('push', {
label: value, label: value,
value: value value: value
}); });
const isPrevented = await this.dispatchEvent('change', {
onChange( value: newValueRes
joinValues });
? newValue isPrevented || onChange(newValueRes);
.map(item => item[valueField || 'value'])
.join(delimiter || ',')
: extractValue
? newValue.map(item => item[valueField || 'value'])
: newValue
);
} }
this.setState({ this.setState({

View File

@ -7,8 +7,8 @@ import React from 'react';
import {FormBaseControl, FormControlProps, FormItem} from './Item'; import {FormBaseControl, FormControlProps, FormItem} from './Item';
import {buildApi, isValidApi, isEffectiveApi} from '../../utils/api'; import {buildApi, isValidApi, isEffectiveApi} from '../../utils/api';
import {Checkbox, Spinner} from '../../components'; import {Checkbox, Spinner} from '../../components';
import {autobind, setVariable} from '../../utils/helper'; import {autobind, setVariable, createObject} from '../../utils/helper';
import {ApiObject} from '../../types'; import {ApiObject, Action} from '../../types';
import {SchemaApi} from '../../Schema'; import {SchemaApi} from '../../Schema';
/** /**
@ -147,6 +147,13 @@ export default class MatrixCheckbox extends React.Component<
removeHook?.(this.initOptions, 'init'); removeHook?.(this.initOptions, 'init');
} }
doAction(action: Action, data: object, throwErrors: boolean) {
const {resetValue, onChange} = this.props;
if (action.actionType === 'clear') {
onChange(resetValue ?? '');
}
}
async initOptions(data: any) { async initOptions(data: any) {
await this.reload(); await this.reload();
const {formItem, name} = this.props; const {formItem, name} = this.props;
@ -225,9 +232,9 @@ export default class MatrixCheckbox extends React.Component<
}); });
} }
toggleItem(checked: boolean, x: number, y: number) { async toggleItem(checked: boolean, x: number, y: number) {
const {columns, rows} = this.state; const {columns, rows} = this.state;
const {multiple, singleSelectMode} = this.props; const {multiple, singleSelectMode, dispatchEvent, data} = this.props;
const value = this.props.value || buildDefaultValue(columns, rows); const value = this.props.value || buildDefaultValue(columns, rows);
@ -262,6 +269,13 @@ export default class MatrixCheckbox extends React.Component<
} }
} }
const rendererEvent = await dispatchEvent('change', createObject({
value: value.concat(),
}, data));
if (rendererEvent?.prevented) {
return;
}
this.props.onChange(value.concat()); this.props.onChange(value.concat());
} }

View File

@ -2,7 +2,7 @@
* @file SelectRadiosCheckboxes * @file SelectRadiosCheckboxes
* ListButtonGroup * ListButtonGroup
*/ */
import {Api, PlainObject, Schema} from '../../types'; import {Api, PlainObject, Schema, Action} from '../../types';
import {isEffectiveApi, isApiOutdated, isValidApi} from '../../utils/api'; import {isEffectiveApi, isApiOutdated, isValidApi} from '../../utils/api';
import {isAlive} from 'mobx-state-tree'; import {isAlive} from 'mobx-state-tree';
import { import {
@ -454,6 +454,27 @@ export function registerOptionsControl(config: OptionsConfig) {
this.toDispose = []; this.toDispose = [];
} }
async dispatchChangeEvent(eventData: any = '') {
const {dispatchEvent, options, data} = this.props;
const rendererEvent = await dispatchEvent(
'change',
{
value: eventData,
options,
...data
}
);
// 返回阻塞标识
return !!rendererEvent?.prevented;
}
doAction(action: Action, data: object, throwErrors: boolean) {
const {resetValue, onChange} = this.props;
if (action.actionType === 'clear') {
onChange(resetValue ?? '');
}
}
syncAutoFill(value: any) { syncAutoFill(value: any) {
const {autoFill, multiple, onBulkChange, data} = this.props; const {autoFill, multiple, onBulkChange, data} = this.props;
const formItem = this.props.formItem as IFormItemStore; const formItem = this.props.formItem as IFormItemStore;
@ -561,7 +582,7 @@ export function registerOptionsControl(config: OptionsConfig) {
} }
@autobind @autobind
handleToggle( async handleToggle(
option: Option, option: Option,
submitOnChange?: boolean, submitOnChange?: boolean,
changeImmediately?: boolean changeImmediately?: boolean
@ -577,7 +598,8 @@ export function registerOptionsControl(config: OptionsConfig) {
value value
); );
onChange && onChange(newValue, submitOnChange, changeImmediately); const isPrevented = await this.dispatchChangeEvent(newValue);
isPrevented || (onChange && onChange(newValue, submitOnChange, changeImmediately));
} }
/** /**
@ -632,7 +654,7 @@ export function registerOptionsControl(config: OptionsConfig) {
} }
@autobind @autobind
handleToggleAll() { async handleToggleAll() {
const {value, onChange, formItem} = this.props; const {value, onChange, formItem} = this.props;
if (!formItem) { if (!formItem) {
@ -644,7 +666,8 @@ export function registerOptionsControl(config: OptionsConfig) {
? [] ? []
: formItem.filteredOptions.concat(); : formItem.filteredOptions.concat();
const newValue = this.formatValueArray(valueArray); const newValue = this.formatValueArray(valueArray);
onChange && onChange(newValue); const isPrevented = await this.dispatchChangeEvent(newValue);
isPrevented || (onChange && onChange(newValue));
} }
toggleValue(option: Option, originValue?: any) { toggleValue(option: Option, originValue?: any) {

View File

@ -8,8 +8,9 @@ import {
Option, Option,
FormOptionsControl FormOptionsControl
} from './Options'; } from './Options';
import {autobind, isEmpty} from '../../utils/helper'; import {autobind, isEmpty, createObject} from '../../utils/helper';
import {dataMapping} from '../../utils/tpl-builtin'; import {dataMapping} from '../../utils/tpl-builtin';
import {Action} from '../../types';
/** /**
* Radio * Radio
@ -36,14 +37,32 @@ export default class RadiosControl extends React.Component<RadiosProps, any> {
columnsCount: 1 columnsCount: 1
}; };
doAction(action: Action, data: object, throwErrors: boolean) {
const {resetValue, onChange} = this.props;
if (action.actionType === 'clear') {
onChange(resetValue ?? '');
}
}
@autobind @autobind
handleChange(option: Option) { async handleChange(option: Option) {
const {joinValues, extractValue, valueField, onChange} = this.props; const {joinValues, extractValue, valueField, onChange, dispatchEvent, options, data} = this.props;
if (option && (joinValues || extractValue)) { if (option && (joinValues || extractValue)) {
option = option[valueField || 'value']; option = option[valueField || 'value'];
} }
const rendererEvent = await dispatchEvent('change', createObject(
{
value: option,
options,
},
data
));
if (rendererEvent?.prevented) {
return;
}
onChange && onChange(option); onChange && onChange(option);
} }

View File

@ -153,12 +153,13 @@ export default class SelectControl extends React.Component<SelectProps, any> {
async dispatchEvent(eventName: SelectRendererEvent, e: any = {}) { async dispatchEvent(eventName: SelectRendererEvent, e: any = {}) {
const event = 'on' + eventName.charAt(0).toUpperCase() + eventName.slice(1); const event = 'on' + eventName.charAt(0).toUpperCase() + eventName.slice(1);
const {dispatchEvent, options} = this.props; const {dispatchEvent, options, data} = this.props;
// 触发渲染器事件 // 触发渲染器事件
const rendererEvent = await dispatchEvent( const rendererEvent = await dispatchEvent(
eventName, eventName,
createObject(e, { createObject(e, {
options options,
...data
}) })
); );
if (rendererEvent?.prevented) { if (rendererEvent?.prevented) {
@ -178,6 +179,7 @@ export default class SelectControl extends React.Component<SelectProps, any> {
onChange, onChange,
setOptions, setOptions,
options, options,
data,
dispatchEvent dispatchEvent
} = this.props; } = this.props;
@ -224,7 +226,8 @@ export default class SelectControl extends React.Component<SelectProps, any> {
const rendererEvent = await dispatchEvent('change', { const rendererEvent = await dispatchEvent('change', {
value: newValue, value: newValue,
options options,
...data
}); });
if (rendererEvent?.prevented) { if (rendererEvent?.prevented) {
return; return;