forked from p96170835/amis
select search 框放到 popOver 中并支持新增\编辑\删除
This commit is contained in:
parent
e2d93eae04
commit
9d4b983d9e
|
@ -8,10 +8,7 @@
|
||||||
background: $Form-select-bg;
|
background: $Form-select-bg;
|
||||||
border-radius: $Form-select-borderRadius;
|
border-radius: $Form-select-borderRadius;
|
||||||
height: $Form-selectOption-height;
|
height: $Form-selectOption-height;
|
||||||
$paddingY: (
|
$paddingY: ($Form-selectOption-height - $Form-input-lineHeight * $Form-input-fontSize - $Form-select-borderWidth * 2)/2;
|
||||||
$Form-selectOption-height - $Form-input-lineHeight *
|
|
||||||
$Form-input-fontSize - $Form-select-borderWidth * 2
|
|
||||||
)/2;
|
|
||||||
padding: $paddingY 0 $paddingY $Form-select-paddingX;
|
padding: $paddingY 0 $paddingY $Form-select-paddingX;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
color: $Form-select-color;
|
color: $Form-select-color;
|
||||||
|
@ -46,17 +43,7 @@
|
||||||
user-select: none;
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
&-input {
|
|
||||||
cursor: pointer;
|
|
||||||
display: inline-block;
|
|
||||||
position: relative;
|
|
||||||
z-index: 2;
|
|
||||||
outline: none;
|
|
||||||
border: none;
|
|
||||||
background: transparent;
|
|
||||||
line-height: $Form-input-lineHeight;
|
|
||||||
height: $Form-input-lineHeight * $Form-input-fontSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-value {
|
&-value {
|
||||||
line-height: $Form-input-lineHeight * $Form-input-fontSize;
|
line-height: $Form-input-lineHeight * $Form-input-fontSize;
|
||||||
|
@ -64,6 +51,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
&--searchable {
|
&--searchable {
|
||||||
|
|
||||||
.#{$ns}Select-placeholder,
|
.#{$ns}Select-placeholder,
|
||||||
.#{$ns}Select-value {
|
.#{$ns}Select-value {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
@ -79,22 +67,21 @@
|
||||||
.#{$ns}Select-valueWrap {
|
.#{$ns}Select-valueWrap {
|
||||||
margin-bottom: -$gap-xs;
|
margin-bottom: -$gap-xs;
|
||||||
|
|
||||||
> input {
|
>input {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
width: px2rem(100px);
|
width: px2rem(100px);
|
||||||
margin-bottom: $gap-xs;
|
margin-bottom: $gap-xs;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.#{$ns}Select-values + .#{$ns}Select-input {
|
.#{$ns}Select-values+.#{$ns}Select-input {
|
||||||
transform: translateY(0);
|
transform: translateY(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
.#{$ns}Select-value {
|
.#{$ns}Select-value {
|
||||||
position: static;
|
position: static;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
line-height: $Form-input-lineHeight * $Form-input-fontSize -
|
line-height: $Form-input-lineHeight * $Form-input-fontSize - px2rem(2px);
|
||||||
px2rem(2px);
|
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
font-size: $Form-selectValue-fontSize;
|
font-size: $Form-selectValue-fontSize;
|
||||||
|
@ -145,19 +132,18 @@
|
||||||
transform: rotate(180deg);
|
transform: rotate(180deg);
|
||||||
}
|
}
|
||||||
|
|
||||||
&-menuOuter {
|
// &-menuOuter {
|
||||||
position: absolute;
|
// position: absolute;
|
||||||
background: $Form-select-menu-bg;
|
// background: $Form-select-menu-bg;
|
||||||
color: $Form-select-menu-color;
|
// color: $Form-select-menu-color;
|
||||||
border: $Form-select-outer-borderWidth solid
|
// border: $Form-select-outer-borderWidth solid $Form-input-onFocused-borderColor;
|
||||||
$Form-input-onFocused-borderColor;
|
// left: px2rem(-1px);
|
||||||
left: px2rem(-1px);
|
// right: px2rem(-1px);
|
||||||
right: px2rem(-1px);
|
// min-width: 100%;
|
||||||
min-width: 100%;
|
// top: $Form-select-outer-top;
|
||||||
top: $Form-select-outer-top;
|
// z-index: 10;
|
||||||
z-index: 10;
|
// box-shadow: $Form-select-outer-boxShadow;
|
||||||
box-shadow: $Form-select-outer-boxShadow;
|
// }
|
||||||
}
|
|
||||||
|
|
||||||
&-menu {
|
&-menu {
|
||||||
max-height: px2rem(300px);
|
max-height: px2rem(300px);
|
||||||
|
@ -165,24 +151,37 @@
|
||||||
user-select: none;
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
&-checkAll {
|
&-input {
|
||||||
padding: (
|
cursor: pointer;
|
||||||
$Form-select-menu-height - $Form-input-lineHeight *
|
outline: none;
|
||||||
$Form-input-fontSize - px2rem(2px)
|
border: none;
|
||||||
)/2 $Form-select-paddingX;
|
margin: 0 $Form-select-paddingX;
|
||||||
border-bottom: px2rem(1px) solid $Form-select-checkall-bottomBorder;
|
padding: ($Form-input-height - $Form-input-lineHeight * $Form-input-fontSize)/2 0;
|
||||||
min-width: px2rem(100px);
|
line-height: $Form-input-lineHeight;
|
||||||
|
border-bottom: 1px solid $borderColor;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
label {
|
>svg {
|
||||||
display: block;
|
fill: #999;
|
||||||
|
width: px2rem(14px);
|
||||||
|
height: px2rem(14px);
|
||||||
|
margin-right: px2rem(5px);
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
>input {
|
||||||
|
outline: none;
|
||||||
|
border: none;
|
||||||
|
flex-grow: 1;
|
||||||
|
background: transparent;
|
||||||
|
font-size: px2rem(12px);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&-option {
|
&-option {
|
||||||
padding: (
|
cursor: pointer;
|
||||||
$Form-select-menu-height - $Form-input-lineHeight *
|
padding: ($Form-select-menu-height - $Form-input-lineHeight * $Form-input-fontSize)/2 $Form-select-paddingX;
|
||||||
$Form-input-fontSize - px2rem(2px)
|
|
||||||
)/2 $Form-select-paddingX;
|
|
||||||
|
|
||||||
&.is-active {
|
&.is-active {
|
||||||
color: $Form-select-menu-onActive-color;
|
color: $Form-select-menu-onActive-color;
|
||||||
|
@ -202,12 +201,46 @@
|
||||||
&--placeholder {
|
&--placeholder {
|
||||||
color: $Form-input-placeholderColor;
|
color: $Form-input-placeholderColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
>label {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
>a {
|
||||||
|
float: right;
|
||||||
|
margin-left: px2rem(5px);
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-highlight>a {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-noResult {
|
||||||
|
color: $Form-select-placeholderColor;
|
||||||
|
line-height: $Form-input-lineHeight;
|
||||||
|
user-select: none;
|
||||||
|
margin: 5px 0 0;
|
||||||
|
padding: ($Form-select-menu-height - $Form-input-lineHeight * $Form-input-fontSize)/2 $Form-select-paddingX;
|
||||||
}
|
}
|
||||||
|
|
||||||
&-option-hl {
|
&-option-hl {
|
||||||
color: $red;
|
color: $red;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&-addBtn {
|
||||||
|
display: block;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: ($Form-select-menu-height - $Form-input-lineHeight * $Form-input-fontSize)/2 $Form-select-paddingX;
|
||||||
|
|
||||||
|
>svg {
|
||||||
|
width: px2rem(14px);
|
||||||
|
height: px2rem(14px);
|
||||||
|
margin-right: $Checkbox-gap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&.is-focused,
|
&.is-focused,
|
||||||
&.is-opened {
|
&.is-opened {
|
||||||
border-color: $Form-input-onFocused-borderColor;
|
border-color: $Form-input-onFocused-borderColor;
|
||||||
|
@ -243,28 +276,33 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.#{$ns}Select-popover {
|
.#{$ns}Select-popover {
|
||||||
margin-top: -$Form-select-borderWidth;
|
margin-top: px2rem(2px);
|
||||||
background: $Form-select-menu-bg;
|
background: $Form-select-menu-bg;
|
||||||
color: $Form-select-menu-color;
|
color: $Form-select-menu-color;
|
||||||
border: $Form-select-outer-borderWidth solid
|
border: $Form-select-outer-borderWidth solid $Form-input-onFocused-borderColor;
|
||||||
$Form-input-onFocused-borderColor;
|
|
||||||
box-shadow: $Form-select-outer-boxShadow;
|
box-shadow: $Form-select-outer-boxShadow;
|
||||||
border-top-left-radius: 0;
|
border-top-left-radius: 0;
|
||||||
border-top-right-radius: 0;
|
border-top-right-radius: 0;
|
||||||
min-width: px2rem(100px);
|
min-width: px2rem(100px);
|
||||||
|
z-index: 2;
|
||||||
|
|
||||||
|
&.#{$ns}PopOver--leftTopLeftBottom {
|
||||||
|
margin-top: px2rem(-2px);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.#{$ns}SelectControl:not(.is-inline) > .#{$ns}Select {
|
.#{$ns}SelectControl:not(.is-inline)>.#{$ns}Select {
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 需要能撑开
|
// 需要能撑开
|
||||||
@include media-breakpoint-up(sm) {
|
@include media-breakpoint-up(sm) {
|
||||||
.#{$ns}Form-control--sizeXs > .#{$ns}Select,
|
|
||||||
.#{$ns}Form-control--sizeSm > .#{$ns}Select,
|
.#{$ns}Form-control--sizeXs>.#{$ns}Select,
|
||||||
.#{$ns}Form-control--sizeMd > .#{$ns}Select,
|
.#{$ns}Form-control--sizeSm>.#{$ns}Select,
|
||||||
.#{$ns}Form-control--sizeLg > .#{$ns}Select {
|
.#{$ns}Form-control--sizeMd>.#{$ns}Select,
|
||||||
|
.#{$ns}Form-control--sizeLg>.#{$ns}Select {
|
||||||
min-width: 100%;
|
min-width: 100%;
|
||||||
display: inline-flex !important;
|
display: inline-flex !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -102,6 +102,7 @@ $Form-select-outer-boxShadow: px2rem(2px) px2rem(4px) px2rem(8px) rgba(0, 0, 0,
|
||||||
$Form-select-menu-color: #333;
|
$Form-select-menu-color: #333;
|
||||||
$Form-select-menu-onHover-color: #000;
|
$Form-select-menu-onHover-color: #000;
|
||||||
$Form-select-menu-onHover-bg: #eaf6fe;
|
$Form-select-menu-onHover-bg: #eaf6fe;
|
||||||
|
$Form-select-menu-height: px2rem(24px);
|
||||||
|
|
||||||
$InputGroup-select-borderWidth: px2rem(1px);
|
$InputGroup-select-borderWidth: px2rem(1px);
|
||||||
$InputGroup-select-bg: #f6f7fb;
|
$InputGroup-select-bg: #f6f7fb;
|
||||||
|
|
|
@ -11,8 +11,8 @@ import 'react-datetime/css/react-datetime.css';
|
||||||
import Overlay from './Overlay';
|
import Overlay from './Overlay';
|
||||||
import PopOver from './PopOver';
|
import PopOver from './PopOver';
|
||||||
import Downshift, {ControllerStateAndHelpers} from 'downshift';
|
import Downshift, {ControllerStateAndHelpers} from 'downshift';
|
||||||
import cx from 'classnames';
|
|
||||||
import {closeIcon, Icon} from './icons';
|
import {closeIcon, Icon} from './icons';
|
||||||
|
// @ts-ignore
|
||||||
import matchSorter from 'match-sorter';
|
import matchSorter from 'match-sorter';
|
||||||
import {noop} from '../utils/helper';
|
import {noop} from '../utils/helper';
|
||||||
import find = require('lodash/find');
|
import find = require('lodash/find');
|
||||||
|
@ -46,6 +46,14 @@ export interface OptionProps {
|
||||||
clearable?: boolean;
|
clearable?: boolean;
|
||||||
placeholder?: string;
|
placeholder?: string;
|
||||||
autoFill?: {[propName: string]: any};
|
autoFill?: {[propName: string]: any};
|
||||||
|
creatable?: boolean;
|
||||||
|
onAdd?: () => void;
|
||||||
|
addControls?: Array<any>;
|
||||||
|
editable?: boolean;
|
||||||
|
editControls?: Array<any>;
|
||||||
|
onEdit?: (value: Option) => void;
|
||||||
|
removable?: boolean;
|
||||||
|
onDelete?: (value: Option) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type OptionValue = string | number | null | undefined | Option;
|
export type OptionValue = string | number | null | undefined | Option;
|
||||||
|
@ -143,11 +151,14 @@ export function normalizeOptions(
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
interface SelectProps {
|
const DownshiftChangeTypes = Downshift.stateChangeTypes;
|
||||||
|
|
||||||
|
interface SelectProps extends OptionProps {
|
||||||
classPrefix: string;
|
classPrefix: string;
|
||||||
classnames: ClassNamesFn;
|
classnames: ClassNamesFn;
|
||||||
className?: string;
|
className?: string;
|
||||||
creatable: boolean;
|
creatable: boolean;
|
||||||
|
createBtnLabel: string;
|
||||||
multiple: boolean;
|
multiple: boolean;
|
||||||
valueField: string;
|
valueField: string;
|
||||||
labelField: string;
|
labelField: string;
|
||||||
|
@ -167,9 +178,7 @@ interface SelectProps {
|
||||||
inline: boolean;
|
inline: boolean;
|
||||||
disabled: boolean;
|
disabled: boolean;
|
||||||
popOverContainer?: any;
|
popOverContainer?: any;
|
||||||
promptTextCreator: (label: string) => string;
|
|
||||||
onChange: (value: void | string | Option | Array<Option>) => void;
|
onChange: (value: void | string | Option | Array<Option>) => void;
|
||||||
onNewOptionClick: (value: Option) => void;
|
|
||||||
onFocus?: Function;
|
onFocus?: Function;
|
||||||
onBlur?: Function;
|
onBlur?: Function;
|
||||||
checkAll?: boolean;
|
checkAll?: boolean;
|
||||||
|
@ -191,17 +200,16 @@ export class Select extends React.Component<SelectProps, SelectState> {
|
||||||
multiple: false,
|
multiple: false,
|
||||||
clearable: true,
|
clearable: true,
|
||||||
creatable: false,
|
creatable: false,
|
||||||
|
createBtnLabel: '新增选项',
|
||||||
searchPromptText: '输入内容进行检索',
|
searchPromptText: '输入内容进行检索',
|
||||||
loadingPlaceholder: '加载中..',
|
loadingPlaceholder: '加载中..',
|
||||||
noResultsText: '没有结果',
|
noResultsText: '未找到任何结果',
|
||||||
clearAllText: '移除所有',
|
clearAllText: '移除所有',
|
||||||
clearValueText: '移除',
|
clearValueText: '移除',
|
||||||
placeholder: '请选择',
|
placeholder: '请选择',
|
||||||
valueField: 'value',
|
valueField: 'value',
|
||||||
labelField: 'label',
|
labelField: 'label',
|
||||||
spinnerClassName: 'fa fa-spinner fa-spin fa-1x fa-fw',
|
spinnerClassName: 'fa fa-spinner fa-spin fa-1x fa-fw',
|
||||||
promptTextCreator: (label: string) => `新增:${label}`,
|
|
||||||
onNewOptionClick: noop,
|
|
||||||
inline: false,
|
inline: false,
|
||||||
disabled: false,
|
disabled: false,
|
||||||
checkAll: false,
|
checkAll: false,
|
||||||
|
@ -229,6 +237,9 @@ export class Select extends React.Component<SelectProps, SelectState> {
|
||||||
this.handleKeyPress = this.handleKeyPress.bind(this);
|
this.handleKeyPress = this.handleKeyPress.bind(this);
|
||||||
this.getTarget = this.getTarget.bind(this);
|
this.getTarget = this.getTarget.bind(this);
|
||||||
this.toggleCheckAll = this.toggleCheckAll.bind(this);
|
this.toggleCheckAll = this.toggleCheckAll.bind(this);
|
||||||
|
this.handleAddClick = this.handleAddClick.bind(this);
|
||||||
|
this.handleEditClick = this.handleEditClick.bind(this);
|
||||||
|
this.handleDeleteClick = this.handleDeleteClick.bind(this);
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
isOpen: false,
|
isOpen: false,
|
||||||
|
@ -244,22 +255,22 @@ export class Select extends React.Component<SelectProps, SelectState> {
|
||||||
loadOptions,
|
loadOptions,
|
||||||
options,
|
options,
|
||||||
multiple,
|
multiple,
|
||||||
checkAll,
|
|
||||||
defaultCheckAll,
|
defaultCheckAll,
|
||||||
onChange,
|
onChange,
|
||||||
simpleValue
|
simpleValue
|
||||||
} = this.props;
|
} = this.props;
|
||||||
let {selection} = this.state;
|
let {selection} = this.state;
|
||||||
|
|
||||||
if (multiple && checkAll && defaultCheckAll && options.length) {
|
if (multiple && defaultCheckAll && options.length) {
|
||||||
selection = union(options, selection);
|
selection = union(options, selection);
|
||||||
this.setState(
|
this.setState({
|
||||||
{
|
selection: selection
|
||||||
selection: selection
|
});
|
||||||
},
|
|
||||||
() =>
|
// 因为等 State 设置完后再 onChange,会让 form 再 didMount 中的
|
||||||
onChange(simpleValue ? selection.map(item => item.value) : selection)
|
// onInit 出去的数据没有包含这部分,所以从 state 回调中拿出来了
|
||||||
);
|
// 存在风险
|
||||||
|
onChange(simpleValue ? selection.map(item => item.value) : selection);
|
||||||
}
|
}
|
||||||
|
|
||||||
loadOptions && loadOptions('');
|
loadOptions && loadOptions('');
|
||||||
|
@ -280,9 +291,13 @@ export class Select extends React.Component<SelectProps, SelectState> {
|
||||||
|
|
||||||
open() {
|
open() {
|
||||||
this.props.disabled ||
|
this.props.disabled ||
|
||||||
this.setState({
|
this.setState(
|
||||||
isOpen: true
|
{
|
||||||
});
|
isOpen: true,
|
||||||
|
highlightedIndex: -1
|
||||||
|
},
|
||||||
|
() => setTimeout(this.focus, 500)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
close() {
|
close() {
|
||||||
|
@ -301,9 +316,13 @@ export class Select extends React.Component<SelectProps, SelectState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.props.disabled ||
|
this.props.disabled ||
|
||||||
this.setState({
|
this.setState(
|
||||||
isOpen: !this.state.isOpen
|
{
|
||||||
});
|
isOpen: !this.state.isOpen,
|
||||||
|
highlightedIndex: -1
|
||||||
|
},
|
||||||
|
this.state.isOpen ? undefined : () => setTimeout(this.focus, 500)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
onFocus(e: any) {
|
onFocus(e: any) {
|
||||||
|
@ -389,14 +408,9 @@ export class Select extends React.Component<SelectProps, SelectState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
handleChange(selectItem: any) {
|
handleChange(selectItem: any) {
|
||||||
const {onChange, multiple, onNewOptionClick, simpleValue} = this.props;
|
const {onChange, multiple, simpleValue} = this.props;
|
||||||
let {selection} = this.state;
|
let {selection} = this.state;
|
||||||
|
|
||||||
if (selectItem.isNew) {
|
|
||||||
delete selectItem.isNew;
|
|
||||||
onNewOptionClick(selectItem);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (multiple) {
|
if (multiple) {
|
||||||
selection = selection.concat();
|
selection = selection.concat();
|
||||||
const idx = selection.indexOf(selectItem);
|
const idx = selection.indexOf(selectItem);
|
||||||
|
@ -417,27 +431,26 @@ export class Select extends React.Component<SelectProps, SelectState> {
|
||||||
const loadOptions = this.props.loadOptions;
|
const loadOptions = this.props.loadOptions;
|
||||||
let doLoad = false;
|
let doLoad = false;
|
||||||
|
|
||||||
if (changes.isOpen !== void 0) {
|
|
||||||
update.isOpen = changes.isOpen;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (changes.highlightedIndex !== void 0) {
|
|
||||||
update.highlightedIndex = changes.highlightedIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (changes.type) {
|
switch (changes.type) {
|
||||||
case Downshift.stateChangeTypes.keyDownEnter:
|
case DownshiftChangeTypes.keyDownEnter:
|
||||||
case Downshift.stateChangeTypes.clickItem:
|
case DownshiftChangeTypes.clickItem:
|
||||||
update = {
|
update = {
|
||||||
...update,
|
...update,
|
||||||
inputValue: '',
|
inputValue: '',
|
||||||
isOpen: multiple && checkAll ? true : false,
|
isOpen: multiple ? true : false,
|
||||||
isFocused: multiple && checkAll ? true : false
|
isFocused: multiple && checkAll ? true : false
|
||||||
};
|
};
|
||||||
doLoad = true;
|
doLoad = true;
|
||||||
break;
|
break;
|
||||||
case Downshift.stateChangeTypes.changeInput:
|
case DownshiftChangeTypes.changeInput:
|
||||||
update.highlightedIndex = 0;
|
update.highlightedIndex = 0;
|
||||||
|
case DownshiftChangeTypes.keyDownArrowDown:
|
||||||
|
case DownshiftChangeTypes.keyDownArrowUp:
|
||||||
|
case DownshiftChangeTypes.itemMouseEnter:
|
||||||
|
update = {
|
||||||
|
...update,
|
||||||
|
...changes
|
||||||
|
};
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -462,30 +475,38 @@ export class Select extends React.Component<SelectProps, SelectState> {
|
||||||
onChange('');
|
onChange('');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleAddClick() {
|
||||||
|
const {onAdd} = this.props;
|
||||||
|
onAdd && onAdd();
|
||||||
|
}
|
||||||
|
|
||||||
|
handleEditClick(e: Event, item: any) {
|
||||||
|
const {onEdit} = this.props;
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
onEdit && onEdit(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleDeleteClick(e: Event, item: any) {
|
||||||
|
const {onDelete} = this.props;
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
onDelete && onDelete(item);
|
||||||
|
}
|
||||||
|
|
||||||
renderValue({inputValue, isOpen}: ControllerStateAndHelpers<any>) {
|
renderValue({inputValue, isOpen}: ControllerStateAndHelpers<any>) {
|
||||||
const {
|
const {
|
||||||
multiple,
|
multiple,
|
||||||
placeholder,
|
placeholder,
|
||||||
classPrefix: ns,
|
classPrefix: ns,
|
||||||
labelField,
|
labelField,
|
||||||
searchable,
|
|
||||||
creatable,
|
|
||||||
disabled
|
disabled
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const selection = this.state.selection;
|
const selection = this.state.selection;
|
||||||
|
|
||||||
if (
|
|
||||||
searchable &&
|
|
||||||
!creatable &&
|
|
||||||
inputValue &&
|
|
||||||
(multiple ? !selection.length : true)
|
|
||||||
) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!selection.length) {
|
if (!selection.length) {
|
||||||
return creatable && inputValue ? null : (
|
return (
|
||||||
<div key="placeholder" className={`${ns}Select-placeholder`}>
|
<div key="placeholder" className={`${ns}Select-placeholder`}>
|
||||||
{placeholder}
|
{placeholder}
|
||||||
</div>
|
</div>
|
||||||
|
@ -505,7 +526,7 @@ export class Select extends React.Component<SelectProps, SelectState> {
|
||||||
{item[labelField || 'label']}
|
{item[labelField || 'label']}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
) : inputValue && isOpen ? null : (
|
) : (
|
||||||
<div className={`${ns}Select-value`} key={index}>
|
<div className={`${ns}Select-value`} key={index}>
|
||||||
{item.label}
|
{item.label}
|
||||||
</div>
|
</div>
|
||||||
|
@ -513,13 +534,16 @@ export class Select extends React.Component<SelectProps, SelectState> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderOuter({
|
renderOuter(
|
||||||
selectedItem,
|
{
|
||||||
getItemProps,
|
selectedItem,
|
||||||
highlightedIndex,
|
getItemProps,
|
||||||
inputValue,
|
highlightedIndex,
|
||||||
isOpen
|
inputValue,
|
||||||
}: ControllerStateAndHelpers<any>) {
|
isOpen
|
||||||
|
}: ControllerStateAndHelpers<any>,
|
||||||
|
getInputProps: any
|
||||||
|
) {
|
||||||
const {
|
const {
|
||||||
popOverContainer,
|
popOverContainer,
|
||||||
options,
|
options,
|
||||||
|
@ -528,11 +552,16 @@ export class Select extends React.Component<SelectProps, SelectState> {
|
||||||
noResultsText,
|
noResultsText,
|
||||||
loadOptions,
|
loadOptions,
|
||||||
creatable,
|
creatable,
|
||||||
promptTextCreator,
|
|
||||||
multiple,
|
multiple,
|
||||||
classnames: cx,
|
classnames: cx,
|
||||||
checkAll,
|
checkAll,
|
||||||
checkAllLabel
|
checkAllLabel,
|
||||||
|
searchable,
|
||||||
|
createBtnLabel,
|
||||||
|
disabled,
|
||||||
|
searchPromptText,
|
||||||
|
editable,
|
||||||
|
removable
|
||||||
} = this.props;
|
} = this.props;
|
||||||
const {selection} = this.state;
|
const {selection} = this.state;
|
||||||
|
|
||||||
|
@ -545,39 +574,37 @@ export class Select extends React.Component<SelectProps, SelectState> {
|
||||||
})
|
})
|
||||||
: options.concat();
|
: options.concat();
|
||||||
|
|
||||||
if (multiple) {
|
if (multiple && checkAll) {
|
||||||
if (checkAll) {
|
const optionsValues = options.map(option => option.value);
|
||||||
const optionsValues = options.map(option => option.value);
|
const selectionValues = selection.map(select => select.value);
|
||||||
const selectionValues = selection.map(select => select.value);
|
checkedAll = optionsValues.every(
|
||||||
checkedAll = optionsValues.every(
|
option => selectionValues.indexOf(option) > -1
|
||||||
option => selectionValues.indexOf(option) > -1
|
);
|
||||||
);
|
checkedPartial = optionsValues.some(
|
||||||
checkedPartial = optionsValues.some(
|
option => selectionValues.indexOf(option) > -1
|
||||||
option => selectionValues.indexOf(option) > -1
|
);
|
||||||
);
|
|
||||||
} else {
|
|
||||||
filtedOptions = filtedOptions.filter(
|
|
||||||
(option: any) => !~selectedItem.indexOf(option)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
inputValue &&
|
|
||||||
creatable &&
|
|
||||||
!find(options, item => item[labelField || 'label'] == inputValue)
|
|
||||||
) {
|
|
||||||
filtedOptions.unshift({
|
|
||||||
[labelField]: inputValue,
|
|
||||||
[valueField]: inputValue,
|
|
||||||
isNew: true
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const menu = (
|
const menu = (
|
||||||
<div ref={this.menu} className={cx('Select-menu')}>
|
<div ref={this.menu} className={cx('Select-menu')}>
|
||||||
{multiple && checkAll ? (
|
{searchable ? (
|
||||||
<div className={cx('Select-checkAll')}>
|
<div className={cx(`Select-input`)}>
|
||||||
|
<Icon icon="search" className="icon" />
|
||||||
|
<input
|
||||||
|
{...getInputProps({
|
||||||
|
onFocus: this.onFocus,
|
||||||
|
onBlur: this.onBlur,
|
||||||
|
disabled: disabled,
|
||||||
|
placeholder: searchPromptText,
|
||||||
|
onChange: this.handleInputChange,
|
||||||
|
ref: this.inputRef
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
|
||||||
|
{multiple && checkAll && filtedOptions.length ? (
|
||||||
|
<div className={cx('Select-option')}>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={checkedPartial}
|
checked={checkedPartial}
|
||||||
partial={checkedPartial && !checkedAll}
|
partial={checkedPartial && !checkedAll}
|
||||||
|
@ -587,11 +614,12 @@ export class Select extends React.Component<SelectProps, SelectState> {
|
||||||
</Checkbox>
|
</Checkbox>
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
{filtedOptions.length ? (
|
{filtedOptions.length ? (
|
||||||
filtedOptions.map((item, index) => {
|
filtedOptions.map((item, index) => {
|
||||||
const checked = checkAll
|
const checked = checkAll
|
||||||
? selection.some((o: Option) => o.value == item.value)
|
? selection.some((o: Option) => o.value == item.value)
|
||||||
: false;
|
: !!~selectedItem.indexOf(item);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
@ -609,15 +637,32 @@ export class Select extends React.Component<SelectProps, SelectState> {
|
||||||
(Array.isArray(selectedItem) && ~selectedItem.indexOf(item))
|
(Array.isArray(selectedItem) && ~selectedItem.indexOf(item))
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
{checkAll ? (
|
{removable ? (
|
||||||
|
<a data-tooltip="移除" data-position="left">
|
||||||
|
<Icon
|
||||||
|
icon="minus"
|
||||||
|
className="icon"
|
||||||
|
onClick={(e: any) => this.handleDeleteClick(e, item)}
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
) : null}
|
||||||
|
{editable ? (
|
||||||
|
<a data-tooltip="编辑" data-position="left">
|
||||||
|
<Icon
|
||||||
|
icon="pencil"
|
||||||
|
className="icon"
|
||||||
|
onClick={(e: any) => this.handleEditClick(e, item)}
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
) : null}
|
||||||
|
|
||||||
|
{checkAll || multiple ? (
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={checked}
|
checked={checked}
|
||||||
trueValue={item.value}
|
trueValue={item.value}
|
||||||
onChange={() => this.handleChange(item)}
|
onChange={() => this.handleChange(item)}
|
||||||
>
|
>
|
||||||
{item.isNew
|
{item.disabled
|
||||||
? promptTextCreator(item.label as string)
|
|
||||||
: item.disabled
|
|
||||||
? item[labelField]
|
? item[labelField]
|
||||||
: highlight(
|
: highlight(
|
||||||
item[labelField],
|
item[labelField],
|
||||||
|
@ -625,8 +670,6 @@ export class Select extends React.Component<SelectProps, SelectState> {
|
||||||
cx('Select-option-hl')
|
cx('Select-option-hl')
|
||||||
)}
|
)}
|
||||||
</Checkbox>
|
</Checkbox>
|
||||||
) : item.isNew ? (
|
|
||||||
promptTextCreator(item.label as string)
|
|
||||||
) : (
|
) : (
|
||||||
<span>
|
<span>
|
||||||
{item.disabled
|
{item.disabled
|
||||||
|
@ -643,32 +686,56 @@ export class Select extends React.Component<SelectProps, SelectState> {
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
) : (
|
) : (
|
||||||
<div className={cx('Select-option Select-option--placeholder')}>
|
<div className={cx('Select-noResult')}>{noResultsText}</div>
|
||||||
{noResultsText}
|
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{creatable && !disabled ? (
|
||||||
|
<a className={cx('Select-addBtn')} onClick={this.handleAddClick}>
|
||||||
|
<Icon icon="plus" className="icon" />
|
||||||
|
{createBtnLabel}
|
||||||
|
</a>
|
||||||
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
if (popOverContainer) {
|
return (
|
||||||
return (
|
<Overlay
|
||||||
<Overlay
|
container={popOverContainer || this.getTarget}
|
||||||
container={popOverContainer}
|
target={this.getTarget}
|
||||||
placement="left-bottom-left-top"
|
show
|
||||||
target={this.getTarget}
|
>
|
||||||
show
|
<PopOver
|
||||||
|
overlay
|
||||||
|
className={cx('Select-popover')}
|
||||||
|
style={{width: this.target ? this.target.offsetWidth : 'auto'}}
|
||||||
|
onHide={this.close}
|
||||||
>
|
>
|
||||||
<PopOver
|
{menu}
|
||||||
className={cx('Select-popover')}
|
</PopOver>
|
||||||
style={{width: this.target ? this.target.offsetWidth : 'auto'}}
|
</Overlay>
|
||||||
>
|
);
|
||||||
{menu}
|
|
||||||
</PopOver>
|
// if (popOverContainer) {
|
||||||
</Overlay>
|
// return (
|
||||||
);
|
// <Overlay
|
||||||
} else {
|
// container={popOverContainer}
|
||||||
return <div className={cx('Select-menuOuter')}>{menu}</div>;
|
// placement="left-bottom-left-top"
|
||||||
}
|
// target={this.getTarget}
|
||||||
|
// show
|
||||||
|
// >
|
||||||
|
// <PopOver
|
||||||
|
// overlay
|
||||||
|
// className={cx('Select-popover')}
|
||||||
|
// style={{width: this.target ? this.target.offsetWidth : 'auto'}}
|
||||||
|
// onHide={this.close}
|
||||||
|
// >
|
||||||
|
// {menu}
|
||||||
|
// </PopOver>
|
||||||
|
// </Overlay>
|
||||||
|
// );
|
||||||
|
// } else {
|
||||||
|
// return <div className={cx('Select-menuOuter')}>{menu}</div>;
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
@ -697,14 +764,14 @@ export class Select extends React.Component<SelectProps, SelectState> {
|
||||||
inputValue={inputValue}
|
inputValue={inputValue}
|
||||||
onChange={this.handleChange}
|
onChange={this.handleChange}
|
||||||
onStateChange={this.handleStateChange}
|
onStateChange={this.handleStateChange}
|
||||||
onOuterClick={this.close}
|
// onOuterClick={this.close}
|
||||||
itemToString={item => (item ? item[labelField] : '')}
|
itemToString={item => (item ? item[labelField] : '')}
|
||||||
>
|
>
|
||||||
{(options: ControllerStateAndHelpers<any>) => {
|
{(options: ControllerStateAndHelpers<any>) => {
|
||||||
const {isOpen, getInputProps} = options;
|
const {isOpen, getInputProps} = options;
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
tabIndex={searchable || disabled ? -1 : 0}
|
tabIndex={disabled ? -1 : 0}
|
||||||
onKeyPress={this.handleKeyPress}
|
onKeyPress={this.handleKeyPress}
|
||||||
onClick={this.toggle}
|
onClick={this.toggle}
|
||||||
onFocus={this.onFocus}
|
onFocus={this.onFocus}
|
||||||
|
@ -724,22 +791,6 @@ export class Select extends React.Component<SelectProps, SelectState> {
|
||||||
>
|
>
|
||||||
<div className={cx(`Select-valueWrap`)}>
|
<div className={cx(`Select-valueWrap`)}>
|
||||||
{this.renderValue(options)}
|
{this.renderValue(options)}
|
||||||
{searchable && !disabled ? (
|
|
||||||
<input
|
|
||||||
{...getInputProps({
|
|
||||||
className: cx(`Select-input`),
|
|
||||||
onFocus: this.onFocus,
|
|
||||||
onBlur: this.onBlur,
|
|
||||||
onKeyDown: event => {
|
|
||||||
if (event.key === 'Backspace' && !inputValue) {
|
|
||||||
this.removeItem(value.length - 1);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onChange: this.handleInputChange,
|
|
||||||
ref: this.inputRef
|
|
||||||
})}
|
|
||||||
/>
|
|
||||||
) : null}
|
|
||||||
</div>
|
</div>
|
||||||
{clearable && !disabled && value && value.length ? (
|
{clearable && !disabled && value && value.length ? (
|
||||||
<a onClick={this.clearValue} className={cx('Select-clear')}>
|
<a onClick={this.clearValue} className={cx('Select-clear')}>
|
||||||
|
@ -753,7 +804,7 @@ export class Select extends React.Component<SelectProps, SelectState> {
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
<span className={cx('Select-arrow')} />
|
<span className={cx('Select-arrow')} />
|
||||||
{isOpen ? this.renderOuter(options) : null}
|
{isOpen ? this.renderOuter(options, getInputProps) : null}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
|
|
|
@ -48,6 +48,9 @@ import SuccessIcon from '../icons/success.svg';
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import FailIcon from '../icons/fail.svg';
|
import FailIcon from '../icons/fail.svg';
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
import SearchIcon from '../icons/search.svg';
|
||||||
|
|
||||||
// 兼容原来的用法,后续不直接试用。
|
// 兼容原来的用法,后续不直接试用。
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
export const closeIcon = <CloseIcon />;
|
export const closeIcon = <CloseIcon />;
|
||||||
|
@ -103,6 +106,7 @@ registerIcon('upload', UploadIcon);
|
||||||
registerIcon('file', FileIcon);
|
registerIcon('file', FileIcon);
|
||||||
registerIcon('success', SuccessIcon);
|
registerIcon('success', SuccessIcon);
|
||||||
registerIcon('fail', FailIcon);
|
registerIcon('fail', FailIcon);
|
||||||
|
registerIcon('search', SearchIcon);
|
||||||
|
|
||||||
export function Icon({
|
export function Icon({
|
||||||
icon,
|
icon,
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 18 18" version="1.1">
|
||||||
|
<path d="M2,8 C2,4.691 4.691,2 8,2 C11.309,2 14,4.691 14,8 C14,11.309 11.309,14 8,14 C4.691,14 2,11.309 2,8 L2,8 Z M18,16.586 L14.314,12.9 C15.367,11.545 16,9.849 16,8 C16,3.582 12.418,0 8,0 C3.582,0 0,3.582 0,8 C0,12.418 3.582,16 8,16 C9.849,16 11.545,15.367 12.9,14.314 L16.586,18 L18,16.586 Z"></path>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 438 B |
|
@ -135,7 +135,7 @@ export default class CRUD extends React.Component<CRUDProps, any> {
|
||||||
control: any;
|
control: any;
|
||||||
lastQuery: any;
|
lastQuery: any;
|
||||||
dataInvalid: boolean = false;
|
dataInvalid: boolean = false;
|
||||||
timer: number;
|
timer: NodeJS.Timeout;
|
||||||
mounted: boolean;
|
mounted: boolean;
|
||||||
constructor(props: CRUDProps) {
|
constructor(props: CRUDProps) {
|
||||||
super(props);
|
super(props);
|
||||||
|
@ -331,7 +331,7 @@ export default class CRUD extends React.Component<CRUDProps, any> {
|
||||||
env.jumpTo(filter(action.redirect, data), action);
|
env.jumpTo(filter(action.redirect, data), action);
|
||||||
|
|
||||||
return store
|
return store
|
||||||
.saveRemote(action.api, data, {
|
.saveRemote(action.api!, data, {
|
||||||
successMessage:
|
successMessage:
|
||||||
(action.messages && action.messages.success) ||
|
(action.messages && action.messages.success) ||
|
||||||
(messages && messages.saveSuccess),
|
(messages && messages.saveSuccess),
|
||||||
|
|
|
@ -118,16 +118,7 @@ export default class DropDownButton extends React.Component<
|
||||||
|
|
||||||
if (popOverContainer) {
|
if (popOverContainer) {
|
||||||
return (
|
return (
|
||||||
<Overlay
|
<Overlay container={popOverContainer} target={() => this.target} show>
|
||||||
container={popOverContainer}
|
|
||||||
placement={
|
|
||||||
align === 'right'
|
|
||||||
? 'right-bottom-right-top'
|
|
||||||
: 'left-bottom-left-top'
|
|
||||||
}
|
|
||||||
target={() => this.target}
|
|
||||||
show
|
|
||||||
>
|
|
||||||
<PopOver
|
<PopOver
|
||||||
overlay
|
overlay
|
||||||
onHide={this.close}
|
onHide={this.close}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import {IFormStore, IFormItemStore} from '../../store/form';
|
import {IFormStore, IFormItemStore} from '../../store/form';
|
||||||
import debouce = require('lodash/debounce');
|
import debouce = require('lodash/debounce');
|
||||||
|
|
||||||
|
|
|
@ -10,9 +10,10 @@ import {
|
||||||
RendererConfig,
|
RendererConfig,
|
||||||
HocStoreFactory
|
HocStoreFactory
|
||||||
} from '../../factory';
|
} from '../../factory';
|
||||||
import {anyChanged, ucFirst, getWidthRate} from '../../utils/helper';
|
import {anyChanged, ucFirst, getWidthRate, autobind} from '../../utils/helper';
|
||||||
import {observer} from 'mobx-react';
|
import {observer} from 'mobx-react';
|
||||||
import {FormHorizontal, FormSchema} from '.';
|
import {FormHorizontal, FormSchema} from '.';
|
||||||
|
import {Schema} from '../../types';
|
||||||
|
|
||||||
export interface FormItemBasicConfig extends Partial<RendererConfig> {
|
export interface FormItemBasicConfig extends Partial<RendererConfig> {
|
||||||
type?: string;
|
type?: string;
|
||||||
|
@ -33,13 +34,11 @@ export interface FormItemBasicConfig extends Partial<RendererConfig> {
|
||||||
validate?: (values: any, value: any) => string | boolean;
|
validate?: (values: any, value: any) => string | boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface FormItemState {
|
// 自己接收到属性。
|
||||||
isFocused: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface FormItemProps extends RendererProps {
|
export interface FormItemProps extends RendererProps {
|
||||||
name?: string;
|
name?: string;
|
||||||
formStore?: IFormStore;
|
formStore?: IFormStore;
|
||||||
|
formItem?: IFormItemStore;
|
||||||
formInited: boolean;
|
formInited: boolean;
|
||||||
formMode: 'normal' | 'horizontal' | 'inline' | 'row' | 'default';
|
formMode: 'normal' | 'horizontal' | 'inline' | 'row' | 'default';
|
||||||
formHorizontal: FormHorizontal;
|
formHorizontal: FormHorizontal;
|
||||||
|
@ -89,8 +88,10 @@ export interface FormItemProps extends RendererProps {
|
||||||
error?: string;
|
error?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type FormControlProps = RendererProps &
|
// 下发下去的属性
|
||||||
Exclude<
|
export type FormControlProps = RendererProps & {
|
||||||
|
onOpenDialog: (schema: Schema, data: any) => Promise<any>;
|
||||||
|
} & Exclude<
|
||||||
FormItemProps,
|
FormItemProps,
|
||||||
| 'inputClassName'
|
| 'inputClassName'
|
||||||
| 'renderControl'
|
| 'renderControl'
|
||||||
|
@ -114,29 +115,15 @@ export interface FormItemConfig extends FormItemBasicConfig {
|
||||||
component: FormControlComponent;
|
component: FormControlComponent;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class FormItemWrap extends React.Component<
|
export class FormItemWrap extends React.Component<FormItemProps> {
|
||||||
FormItemProps,
|
|
||||||
FormItemState
|
|
||||||
> {
|
|
||||||
reaction: any;
|
reaction: any;
|
||||||
|
|
||||||
constructor(props: FormItemProps) {
|
|
||||||
super(props);
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
isFocused: false
|
|
||||||
};
|
|
||||||
|
|
||||||
this.handleFocus = this.handleFocus.bind(this);
|
|
||||||
this.handleBlur = this.handleBlur.bind(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillMount() {
|
componentWillMount() {
|
||||||
const {formItem: model} = this.props;
|
const {formItem: model} = this.props;
|
||||||
|
|
||||||
if (model) {
|
if (model) {
|
||||||
this.reaction = reaction(
|
this.reaction = reaction(
|
||||||
() => model.errors.join(''),
|
() => `${model.errors.join('')}${model.isFocused}${model.dialogOpen}`,
|
||||||
() => this.forceUpdate()
|
() => this.forceUpdate()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -146,20 +133,51 @@ export class FormItemWrap extends React.Component<
|
||||||
this.reaction && this.reaction();
|
this.reaction && this.reaction();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@autobind
|
||||||
handleFocus(e: any) {
|
handleFocus(e: any) {
|
||||||
this.setState({
|
const {formItem: model} = this.props;
|
||||||
isFocused: true
|
model && model.focus();
|
||||||
});
|
|
||||||
this.props.onFocus && this.props.onFocus(e);
|
this.props.onFocus && this.props.onFocus(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@autobind
|
||||||
handleBlur(e: any) {
|
handleBlur(e: any) {
|
||||||
this.setState({
|
const {formItem: model} = this.props;
|
||||||
isFocused: false
|
model && model.blur();
|
||||||
});
|
|
||||||
this.props.onBlur && this.props.onBlur(e);
|
this.props.onBlur && this.props.onBlur(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@autobind
|
||||||
|
async handleOpenDialog(schema: Schema, data: any) {
|
||||||
|
const {formItem: model} = this.props;
|
||||||
|
if (!model) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Promise(resolve =>
|
||||||
|
model.openDialog(schema, data, (result?: any) => resolve(result))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@autobind
|
||||||
|
handleDialogConfirm([values]: Array<any>) {
|
||||||
|
const {formItem: model} = this.props;
|
||||||
|
if (!model) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
model.closeDialog(values);
|
||||||
|
}
|
||||||
|
|
||||||
|
@autobind
|
||||||
|
handleDialogClose() {
|
||||||
|
const {formItem: model} = this.props;
|
||||||
|
if (!model) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
model.closeDialog();
|
||||||
|
}
|
||||||
|
|
||||||
renderControl() {
|
renderControl() {
|
||||||
const {
|
const {
|
||||||
inputClassName,
|
inputClassName,
|
||||||
|
@ -179,6 +197,7 @@ export class FormItemWrap extends React.Component<
|
||||||
const controlSize = size || defaultSize;
|
const controlSize = size || defaultSize;
|
||||||
return renderControl({
|
return renderControl({
|
||||||
...rest,
|
...rest,
|
||||||
|
onOpenDialog: this.handleOpenDialog,
|
||||||
type,
|
type,
|
||||||
classnames: cx,
|
classnames: cx,
|
||||||
formItem: model,
|
formItem: model,
|
||||||
|
@ -299,7 +318,7 @@ export class FormItemWrap extends React.Component<
|
||||||
})
|
})
|
||||||
: null}
|
: null}
|
||||||
|
|
||||||
{hint && this.state.isFocused
|
{hint && model && model.isFocused
|
||||||
? render('hint', hint, {
|
? render('hint', hint, {
|
||||||
className: cx(`Form-hint`)
|
className: cx(`Form-hint`)
|
||||||
})
|
})
|
||||||
|
@ -395,7 +414,7 @@ export class FormItemWrap extends React.Component<
|
||||||
})
|
})
|
||||||
: null}
|
: null}
|
||||||
|
|
||||||
{hint && this.state.isFocused
|
{hint && model && model.isFocused
|
||||||
? render('hint', hint, {
|
? render('hint', hint, {
|
||||||
className: cx(`Form-hint`)
|
className: cx(`Form-hint`)
|
||||||
})
|
})
|
||||||
|
@ -490,7 +509,7 @@ export class FormItemWrap extends React.Component<
|
||||||
})
|
})
|
||||||
: null}
|
: null}
|
||||||
|
|
||||||
{hint && this.state.isFocused
|
{hint && model && model.isFocused
|
||||||
? render('hint', hint, {
|
? render('hint', hint, {
|
||||||
className: cx(`Form-hint`)
|
className: cx(`Form-hint`)
|
||||||
})
|
})
|
||||||
|
@ -588,7 +607,7 @@ export class FormItemWrap extends React.Component<
|
||||||
: null}
|
: null}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{hint && this.state.isFocused
|
{hint && model && model.isFocused
|
||||||
? render('hint', hint, {
|
? render('hint', hint, {
|
||||||
className: cx(`Form-hint`)
|
className: cx(`Form-hint`)
|
||||||
})
|
})
|
||||||
|
@ -612,19 +631,38 @@ export class FormItemWrap extends React.Component<
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {formMode, inputOnly, wrap} = this.props;
|
const {formMode, inputOnly, wrap, render, formItem: model} = this.props;
|
||||||
|
|
||||||
if (wrap === false || inputOnly) {
|
if (wrap === false || inputOnly) {
|
||||||
return this.renderControl();
|
return this.renderControl();
|
||||||
}
|
}
|
||||||
|
|
||||||
return formMode === 'inline'
|
return (
|
||||||
? this.renderInline()
|
<>
|
||||||
: formMode === 'horizontal'
|
{formMode === 'inline'
|
||||||
? this.renderHorizontal()
|
? this.renderInline()
|
||||||
: formMode === 'row'
|
: formMode === 'horizontal'
|
||||||
? this.renderRow()
|
? this.renderHorizontal()
|
||||||
: this.renderNormal();
|
: formMode === 'row'
|
||||||
|
? this.renderRow()
|
||||||
|
: this.renderNormal()}
|
||||||
|
{model
|
||||||
|
? render(
|
||||||
|
'modal',
|
||||||
|
{
|
||||||
|
type: 'dialog',
|
||||||
|
...model.dialogSchema
|
||||||
|
},
|
||||||
|
{
|
||||||
|
show: model.dialogOpen,
|
||||||
|
onClose: this.handleDialogClose,
|
||||||
|
onConfirm: this.handleDialogConfirm,
|
||||||
|
data: model.dialogData
|
||||||
|
}
|
||||||
|
)
|
||||||
|
: null}
|
||||||
|
</>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -796,6 +834,7 @@ export function registerFormItem(config: FormItemConfig): RendererConfig {
|
||||||
return (
|
return (
|
||||||
<Control
|
<Control
|
||||||
{...rest}
|
{...rest}
|
||||||
|
onOpenDialog={this.handleOpenDialog}
|
||||||
size={config.sizeMutable !== false ? undefined : size}
|
size={config.sizeMutable !== false ? undefined : size}
|
||||||
onFocus={this.handleFocus}
|
onFocus={this.handleFocus}
|
||||||
onBlur={this.handleBlur}
|
onBlur={this.handleBlur}
|
||||||
|
|
|
@ -308,12 +308,7 @@ export default class NestedSelectControl extends React.Component<
|
||||||
|
|
||||||
if (popOverContainer) {
|
if (popOverContainer) {
|
||||||
return (
|
return (
|
||||||
<Overlay
|
<Overlay container={popOverContainer} target={() => this.target} show>
|
||||||
container={popOverContainer}
|
|
||||||
placement="left-bottom-left-top right-bottom-right-top"
|
|
||||||
target={() => this.target}
|
|
||||||
show
|
|
||||||
>
|
|
||||||
<PopOver
|
<PopOver
|
||||||
className={cx('NestedSelect-popover')}
|
className={cx('NestedSelect-popover')}
|
||||||
style={{minWidth: this.target.offsetWidth}}
|
style={{minWidth: this.target.offsetWidth}}
|
||||||
|
|
|
@ -1,11 +1,6 @@
|
||||||
import {Api, Schema} from '../../types';
|
import {Api, Schema} from '../../types';
|
||||||
import {
|
import {isEffectiveApi, isApiOutdated} from '../../utils/api';
|
||||||
buildApi,
|
import {anyChanged, autobind, createObject} from '../../utils/helper';
|
||||||
isEffectiveApi,
|
|
||||||
isValidApi,
|
|
||||||
isApiOutdated
|
|
||||||
} from '../../utils/api';
|
|
||||||
import {anyChanged, autobind} from '../../utils/helper';
|
|
||||||
import {reaction} from 'mobx';
|
import {reaction} from 'mobx';
|
||||||
import {FormControlProps, registerFormItem, FormItemBasicConfig} from './Item';
|
import {FormControlProps, registerFormItem, FormItemBasicConfig} from './Item';
|
||||||
import {IFormItemStore} from '../../store/formItem';
|
import {IFormItemStore} from '../../store/formItem';
|
||||||
|
@ -13,8 +8,9 @@ export type OptionsControlComponent = React.ComponentType<FormControlProps>;
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {resolveVariableAndFilter} from '../../utils/tpl-builtin';
|
import {resolveVariableAndFilter} from '../../utils/tpl-builtin';
|
||||||
import {evalExpression} from '../../utils/tpl';
|
|
||||||
import {Option, OptionProps, normalizeOptions} from '../../components/Select';
|
import {Option, OptionProps, normalizeOptions} from '../../components/Select';
|
||||||
|
import {filter} from '../../utils/tpl';
|
||||||
|
import findIndex from 'lodash/findIndex';
|
||||||
|
|
||||||
export {Option};
|
export {Option};
|
||||||
|
|
||||||
|
@ -26,6 +22,7 @@ export interface OptionsConfig extends OptionsBasicConfig {
|
||||||
component: React.ComponentType<OptionsControlProps>;
|
component: React.ComponentType<OptionsControlProps>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 下发给注册进来的组件的属性。
|
||||||
export interface OptionsControlProps extends FormControlProps, OptionProps {
|
export interface OptionsControlProps extends FormControlProps, OptionProps {
|
||||||
source?: Api;
|
source?: Api;
|
||||||
name?: string;
|
name?: string;
|
||||||
|
@ -35,22 +32,24 @@ export interface OptionsControlProps extends FormControlProps, OptionProps {
|
||||||
setOptions: (value: Array<any>) => void;
|
setOptions: (value: Array<any>) => void;
|
||||||
setLoading: (value: boolean) => void;
|
setLoading: (value: boolean) => void;
|
||||||
reloadOptions: () => void;
|
reloadOptions: () => void;
|
||||||
addable?: boolean;
|
creatable?: boolean;
|
||||||
onAdd?: () => void;
|
onAdd?: () => void;
|
||||||
|
addControls?: Array<any>;
|
||||||
editable?: boolean;
|
editable?: boolean;
|
||||||
|
editControls?: Array<any>;
|
||||||
onEdit?: (value: Option) => void;
|
onEdit?: (value: Option) => void;
|
||||||
removable?: boolean;
|
removable?: boolean;
|
||||||
onDelete?: (value: Option) => void;
|
onDelete?: (value: Option) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 自己接收的属性。
|
||||||
export interface OptionsProps extends FormControlProps, OptionProps {
|
export interface OptionsProps extends FormControlProps, OptionProps {
|
||||||
sourcce?: Api;
|
source?: Api;
|
||||||
|
creatable?: boolean;
|
||||||
addApi?: Api;
|
addApi?: Api;
|
||||||
addMode?: 'dialog' | 'normal';
|
addControls?: Array<any>;
|
||||||
addDialog?: Schema;
|
|
||||||
editApi?: Api;
|
editApi?: Api;
|
||||||
editMode?: 'dialog' | 'normal';
|
editControls?: Array<any>;
|
||||||
editDialog?: Schema;
|
|
||||||
deleteApi?: Api;
|
deleteApi?: Api;
|
||||||
deleteConfirmText?: string;
|
deleteConfirmText?: string;
|
||||||
}
|
}
|
||||||
|
@ -58,7 +57,6 @@ export interface OptionsProps extends FormControlProps, OptionProps {
|
||||||
export function registerOptionsControl(config: OptionsConfig) {
|
export function registerOptionsControl(config: OptionsConfig) {
|
||||||
const Control = config.component;
|
const Control = config.component;
|
||||||
|
|
||||||
// @observer
|
|
||||||
class FormOptionsItem extends React.Component<OptionsProps, any> {
|
class FormOptionsItem extends React.Component<OptionsProps, any> {
|
||||||
static displayName = `OptionsControl(${config.type})`;
|
static displayName = `OptionsControl(${config.type})`;
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
|
@ -70,6 +68,7 @@ export function registerOptionsControl(config: OptionsConfig) {
|
||||||
multiple: false,
|
multiple: false,
|
||||||
placeholder: '请选择',
|
placeholder: '请选择',
|
||||||
resetValue: '',
|
resetValue: '',
|
||||||
|
deleteConfirmText: '确定要删除?',
|
||||||
...Control.defaultProps
|
...Control.defaultProps
|
||||||
};
|
};
|
||||||
static propsList: any = (Control as any).propsList
|
static propsList: any = (Control as any).propsList
|
||||||
|
@ -113,10 +112,10 @@ export function registerOptionsControl(config: OptionsConfig) {
|
||||||
|
|
||||||
let loadOptions: boolean = initFetch !== false;
|
let loadOptions: boolean = initFetch !== false;
|
||||||
|
|
||||||
if (/^\$(?:([a-z0-9_.]+)|{.+})$/.test(source) && formItem) {
|
if (/^\$(?:([a-z0-9_.]+)|{.+})$/.test(source as string) && formItem) {
|
||||||
formItem.setOptions(
|
formItem.setOptions(
|
||||||
normalizeOptions(
|
normalizeOptions(
|
||||||
resolveVariableAndFilter(source, data, '| raw') || []
|
resolveVariableAndFilter(source as string, data, '| raw') || []
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
loadOptions = false;
|
loadOptions = false;
|
||||||
|
@ -207,7 +206,7 @@ export function registerOptionsControl(config: OptionsConfig) {
|
||||||
) {
|
) {
|
||||||
if (/^\$(?:([a-z0-9_.]+)|{.+})$/.test(props.source as string)) {
|
if (/^\$(?:([a-z0-9_.]+)|{.+})$/.test(props.source as string)) {
|
||||||
const prevOptions = resolveVariableAndFilter(
|
const prevOptions = resolveVariableAndFilter(
|
||||||
prevProps.source,
|
prevProps.source as string,
|
||||||
prevProps.data,
|
prevProps.data,
|
||||||
'| raw'
|
'| raw'
|
||||||
);
|
);
|
||||||
|
@ -439,8 +438,189 @@ export function registerOptionsControl(config: OptionsConfig) {
|
||||||
formItem && formItem.setLoading(value);
|
formItem && formItem.setLoading(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@autobind
|
||||||
|
async handleOptionAdd() {
|
||||||
|
let {
|
||||||
|
addControls,
|
||||||
|
disabled,
|
||||||
|
labelField,
|
||||||
|
onOpenDialog,
|
||||||
|
addApi,
|
||||||
|
source,
|
||||||
|
data,
|
||||||
|
valueField,
|
||||||
|
formItem: model,
|
||||||
|
createBtnLabel
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
if (disabled || !model) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Array.isArray(addControls) || !addControls.length) {
|
||||||
|
addControls = [
|
||||||
|
{
|
||||||
|
type: 'text',
|
||||||
|
name: labelField || 'label',
|
||||||
|
label: false,
|
||||||
|
placeholder: '请输入名称'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
let result: any = await onOpenDialog(
|
||||||
|
{
|
||||||
|
type: 'dialog',
|
||||||
|
title: createBtnLabel || '新增选项',
|
||||||
|
body: {
|
||||||
|
type: 'form',
|
||||||
|
api: addApi,
|
||||||
|
controls: addControls
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result) {
|
||||||
|
// 没走服务端的。
|
||||||
|
if (!result.__saved) {
|
||||||
|
result = {
|
||||||
|
...result,
|
||||||
|
[valueField || 'value']: result[labelField || 'label']
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
source
|
||||||
|
? this.reload()
|
||||||
|
: model.setOptions(model.options.concat({...result}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@autobind
|
||||||
|
async handleOptionEdit(value: any) {
|
||||||
|
let {
|
||||||
|
editControls,
|
||||||
|
disabled,
|
||||||
|
labelField,
|
||||||
|
onOpenDialog,
|
||||||
|
editApi,
|
||||||
|
source,
|
||||||
|
data,
|
||||||
|
formItem: model,
|
||||||
|
valueField
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
if (disabled || !model) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Array.isArray(editControls) || !editControls.length) {
|
||||||
|
editControls = [
|
||||||
|
{
|
||||||
|
type: 'text',
|
||||||
|
name: labelField || 'label',
|
||||||
|
label: false,
|
||||||
|
placeholder: '请输入名称'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
let result = await onOpenDialog(
|
||||||
|
{
|
||||||
|
type: 'dialog',
|
||||||
|
title: '编辑选项',
|
||||||
|
body: {
|
||||||
|
type: 'form',
|
||||||
|
api: editApi,
|
||||||
|
controls: editControls
|
||||||
|
}
|
||||||
|
},
|
||||||
|
createObject(data, value)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (source) {
|
||||||
|
this.reload();
|
||||||
|
} else {
|
||||||
|
const options = model.options.concat();
|
||||||
|
const idx = findIndex(
|
||||||
|
options,
|
||||||
|
item => item[valueField || 'value'] == result[valueField || 'value']
|
||||||
|
);
|
||||||
|
|
||||||
|
if (~idx) {
|
||||||
|
options.splice(idx, 1, {
|
||||||
|
...options[idx],
|
||||||
|
...result
|
||||||
|
});
|
||||||
|
model.setOptions(options);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@autobind
|
||||||
|
async handleOptionDelete(value: any) {
|
||||||
|
let {
|
||||||
|
deleteConfirmText,
|
||||||
|
disabled,
|
||||||
|
data,
|
||||||
|
deleteApi,
|
||||||
|
env,
|
||||||
|
formItem: model,
|
||||||
|
source,
|
||||||
|
valueField
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
if (disabled || !model) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ctx = createObject(data, value);
|
||||||
|
const confirmed = deleteConfirmText
|
||||||
|
? await env.confirm(filter(deleteConfirmText, ctx))
|
||||||
|
: true;
|
||||||
|
if (!confirmed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = await env.fetcher(deleteApi!, ctx);
|
||||||
|
|
||||||
|
if (!result.ok) {
|
||||||
|
env.notify('error', result.msg || '删除失败,请重试');
|
||||||
|
} else if (source) {
|
||||||
|
this.reload();
|
||||||
|
} else {
|
||||||
|
const options = model.options.concat();
|
||||||
|
const idx = findIndex(
|
||||||
|
options,
|
||||||
|
item => item[valueField || 'value'] == value[valueField || 'value']
|
||||||
|
);
|
||||||
|
|
||||||
|
if (~idx) {
|
||||||
|
options.splice(idx, 1);
|
||||||
|
model.setOptions(options);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
env.notify('error', e.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {value, formItem} = this.props;
|
const {
|
||||||
|
value,
|
||||||
|
formItem,
|
||||||
|
addApi,
|
||||||
|
editApi,
|
||||||
|
deleteApi,
|
||||||
|
creatable,
|
||||||
|
editable
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Control
|
<Control
|
||||||
|
@ -455,6 +635,12 @@ export function registerOptionsControl(config: OptionsConfig) {
|
||||||
setOptions={this.setOptions}
|
setOptions={this.setOptions}
|
||||||
syncOptions={this.syncOptions}
|
syncOptions={this.syncOptions}
|
||||||
reloadOptions={this.reload}
|
reloadOptions={this.reload}
|
||||||
|
creatable={creatable || isEffectiveApi(addApi)}
|
||||||
|
editable={editable || isEffectiveApi(editApi)}
|
||||||
|
removable={isEffectiveApi(deleteApi)}
|
||||||
|
onAdd={this.handleOptionAdd}
|
||||||
|
onEdit={this.handleOptionEdit}
|
||||||
|
onDelete={this.handleOptionDelete}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +33,6 @@ export default class SelectControl extends React.Component<SelectProps, any> {
|
||||||
leading: false
|
leading: false
|
||||||
});
|
});
|
||||||
this.inputRef = this.inputRef.bind(this);
|
this.inputRef = this.inputRef.bind(this);
|
||||||
this.handleNewOptionClick = this.handleNewOptionClick.bind(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inputRef(ref: any) {
|
inputRef(ref: any) {
|
||||||
|
@ -161,16 +160,6 @@ export default class SelectControl extends React.Component<SelectProps, any> {
|
||||||
return combinedOptions;
|
return combinedOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
handleNewOptionClick(option: any) {
|
|
||||||
const {setOptions, options} = this.props;
|
|
||||||
|
|
||||||
let mergedOptions: Array<any> = options.concat();
|
|
||||||
mergedOptions.push({
|
|
||||||
...option
|
|
||||||
});
|
|
||||||
setOptions(mergedOptions);
|
|
||||||
}
|
|
||||||
|
|
||||||
reload() {
|
reload() {
|
||||||
const reload = this.props.reloadOptions;
|
const reload = this.props.reloadOptions;
|
||||||
reload && reload();
|
reload && reload();
|
||||||
|
@ -211,7 +200,6 @@ export default class SelectControl extends React.Component<SelectProps, any> {
|
||||||
ref={this.inputRef}
|
ref={this.inputRef}
|
||||||
value={selectedOptions}
|
value={selectedOptions}
|
||||||
options={options}
|
options={options}
|
||||||
onNewOptionClick={this.handleNewOptionClick}
|
|
||||||
loadOptions={
|
loadOptions={
|
||||||
isEffectiveApi(autoComplete) ? this.loadRemote : undefined
|
isEffectiveApi(autoComplete) ? this.loadRemote : undefined
|
||||||
}
|
}
|
||||||
|
|
|
@ -395,7 +395,6 @@ export default class TreeSelectControl extends React.Component<
|
||||||
return (
|
return (
|
||||||
<Overlay
|
<Overlay
|
||||||
container={popOverContainer || (() => this.container.current)}
|
container={popOverContainer || (() => this.container.current)}
|
||||||
placement="left-bottom-left-top right-bottom-right-top"
|
|
||||||
target={() => this.target.current}
|
target={() => this.target.current}
|
||||||
show
|
show
|
||||||
>
|
>
|
||||||
|
|
|
@ -421,7 +421,6 @@ export const HocQuickEdit = (config: Partial<QuickEditConfig> = {}) => (
|
||||||
return (
|
return (
|
||||||
<Overlay
|
<Overlay
|
||||||
container={popOverContainer}
|
container={popOverContainer}
|
||||||
placement="left-top right-top left-bottom right-bottom"
|
|
||||||
target={() => this.target}
|
target={() => this.target}
|
||||||
onHide={this.closeQuickEdit}
|
onHide={this.closeQuickEdit}
|
||||||
show
|
show
|
||||||
|
|
|
@ -5,17 +5,12 @@ import {Api, Payload, fetchOptions} from '../types';
|
||||||
import {ComboStore, IComboStore, IUniqueGroup} from './combo';
|
import {ComboStore, IComboStore, IUniqueGroup} from './combo';
|
||||||
import {evalExpression} from '../utils/tpl';
|
import {evalExpression} from '../utils/tpl';
|
||||||
import findIndex = require('lodash/findIndex');
|
import findIndex = require('lodash/findIndex');
|
||||||
import {
|
import {isArrayChilrenModified, isObject, createObject} from '../utils/helper';
|
||||||
isArrayChilrenModified,
|
|
||||||
hasOwnProperty,
|
|
||||||
isObject,
|
|
||||||
createObject
|
|
||||||
} from '../utils/helper';
|
|
||||||
import {flattenTree} from '../utils/helper';
|
import {flattenTree} from '../utils/helper';
|
||||||
import {IRendererStore} from '.';
|
import {IRendererStore} from '.';
|
||||||
import {normalizeOptions} from '../components/Select';
|
import {normalizeOptions} from '../components/Select';
|
||||||
import find = require('lodash/find');
|
import find = require('lodash/find');
|
||||||
import {iRendererStore} from './iRenderer';
|
import {SimpleMap} from '../utils/SimpleMap';
|
||||||
|
|
||||||
interface IOption {
|
interface IOption {
|
||||||
value?: string | number | null;
|
value?: string | number | null;
|
||||||
|
@ -34,6 +29,7 @@ const ErrorDetail = types.model('ErrorDetail', {
|
||||||
export const FormItemStore = types
|
export const FormItemStore = types
|
||||||
.model('FormItemStore', {
|
.model('FormItemStore', {
|
||||||
identifier: types.identifier,
|
identifier: types.identifier,
|
||||||
|
isFocused: false,
|
||||||
type: '',
|
type: '',
|
||||||
unique: false,
|
unique: false,
|
||||||
loading: false,
|
loading: false,
|
||||||
|
@ -81,41 +77,6 @@ export const FormItemStore = types
|
||||||
return self.errorData.map(item => item.msg);
|
return self.errorData.map(item => item.msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
// function selectedOptions(options:Array<Option>=(self.options as any).toJS()) {
|
|
||||||
// return value2array(getValue(), {
|
|
||||||
// multiple: self.multiple,
|
|
||||||
// delimiter: self.delimiter,
|
|
||||||
// valueField: self.valueField,
|
|
||||||
// options: options
|
|
||||||
// })
|
|
||||||
// }
|
|
||||||
|
|
||||||
// function filteredOptions(data:object):Array<IOption> {
|
|
||||||
// let options:Array<IOption> = self.options;
|
|
||||||
// options = options.filter(item => {
|
|
||||||
// let filtered = getExprProperties(item, data);
|
|
||||||
// return filtered.visible !== false && !filtered.hidden;
|
|
||||||
// });
|
|
||||||
|
|
||||||
// let parentStore = getForm().parentStore;
|
|
||||||
// if (parentStore && parentStore.storeType === ComboStore.name) {
|
|
||||||
// let combo = parentStore as IComboStore;
|
|
||||||
// let group = combo.uniques.get(self.name) as IUniqueGroup;
|
|
||||||
// let selectedOptions:Array<any> = [];
|
|
||||||
// group && group.items.forEach(item => {
|
|
||||||
// if (self !== item) {
|
|
||||||
// selectedOptions.push(...item.selectedOptions().map(item => item.value))
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
|
|
||||||
// if (selectedOptions.length) {
|
|
||||||
// options = options.filter(option => !~selectedOptions.indexOf(option.value))
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return options;
|
|
||||||
// }
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
get form(): any {
|
get form(): any {
|
||||||
return getForm();
|
return getForm();
|
||||||
|
@ -144,9 +105,6 @@ export const FormItemStore = types
|
||||||
return getLastOptionValue();
|
return getLastOptionValue();
|
||||||
},
|
},
|
||||||
|
|
||||||
// selectedOptions,
|
|
||||||
// filteredOptions,
|
|
||||||
|
|
||||||
getSelectedOptions(value: any = getValue()) {
|
getSelectedOptions(value: any = getValue()) {
|
||||||
if (value === getValue()) {
|
if (value === getValue()) {
|
||||||
return self.selectedOptions;
|
return self.selectedOptions;
|
||||||
|
@ -212,6 +170,9 @@ export const FormItemStore = types
|
||||||
})
|
})
|
||||||
|
|
||||||
.actions(self => {
|
.actions(self => {
|
||||||
|
const form = self.form as IFormStore;
|
||||||
|
const dialogCallbacks = new SimpleMap<(result?: any) => void>();
|
||||||
|
|
||||||
function config({
|
function config({
|
||||||
required,
|
required,
|
||||||
unique,
|
unique,
|
||||||
|
@ -241,8 +202,6 @@ export const FormItemStore = types
|
||||||
type?: string;
|
type?: string;
|
||||||
id?: string;
|
id?: string;
|
||||||
}) {
|
}) {
|
||||||
const form = self.form as IFormStore;
|
|
||||||
|
|
||||||
if (typeof rules === 'string') {
|
if (typeof rules === 'string') {
|
||||||
rules = str2rules(rules);
|
rules = str2rules(rules);
|
||||||
}
|
}
|
||||||
|
@ -278,6 +237,14 @@ export const FormItemStore = types
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function focus() {
|
||||||
|
self.isFocused = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function blur() {
|
||||||
|
self.isFocused = false;
|
||||||
|
}
|
||||||
|
|
||||||
function changeValue(value: any, isPrintine: boolean = false) {
|
function changeValue(value: any, isPrintine: boolean = false) {
|
||||||
if (typeof value === 'undefined' || value === '__undefined') {
|
if (typeof value === 'undefined' || value === '__undefined') {
|
||||||
self.form.deleteValueByName(self.name);
|
self.form.deleteValueByName(self.name);
|
||||||
|
@ -366,7 +333,7 @@ export const FormItemStore = types
|
||||||
options?: fetchOptions,
|
options?: fetchOptions,
|
||||||
clearValue?: boolean,
|
clearValue?: boolean,
|
||||||
onChange?: (value: any) => void
|
onChange?: (value: any) => void
|
||||||
) => Promise<any> = flow(function* getInitData(
|
) => Promise<Payload | null> = flow(function* getInitData(
|
||||||
api: string,
|
api: string,
|
||||||
data: object,
|
data: object,
|
||||||
options?: fetchOptions,
|
options?: fetchOptions,
|
||||||
|
@ -442,8 +409,9 @@ export const FormItemStore = types
|
||||||
console.error(e.stack);
|
console.error(e.stack);
|
||||||
getRoot(self) &&
|
getRoot(self) &&
|
||||||
(getRoot(self) as IRendererStore).notify('error', e.message);
|
(getRoot(self) as IRendererStore).notify('error', e.message);
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
});
|
} as any);
|
||||||
|
|
||||||
function syncOptions(originOptions?: Array<any>) {
|
function syncOptions(originOptions?: Array<any>) {
|
||||||
if (!self.options.length && typeof self.value === 'undefined') {
|
if (!self.options.length && typeof self.value === 'undefined') {
|
||||||
|
@ -528,7 +496,7 @@ export const FormItemStore = types
|
||||||
unMatched = {
|
unMatched = {
|
||||||
[self.valueField || 'value']: item,
|
[self.valueField || 'value']: item,
|
||||||
[self.labelField || 'label']: item,
|
[self.labelField || 'label']: item,
|
||||||
__unmatched: true
|
'__unmatched': true
|
||||||
};
|
};
|
||||||
|
|
||||||
const orgin: any =
|
const orgin: any =
|
||||||
|
@ -595,27 +563,30 @@ export const FormItemStore = types
|
||||||
clearError();
|
clearError();
|
||||||
}
|
}
|
||||||
|
|
||||||
function openDialog(schema: any, ctx: any, additonal?: object) {
|
function openDialog(
|
||||||
let proto = ctx.__super ? ctx.__super : self.form.data;
|
schema: any,
|
||||||
|
data: any = form.data,
|
||||||
if (additonal) {
|
callback?: (ret?: any) => void
|
||||||
proto = createObject(proto, additonal);
|
) {
|
||||||
}
|
|
||||||
|
|
||||||
const data = createObject(proto, {
|
|
||||||
...ctx
|
|
||||||
});
|
|
||||||
|
|
||||||
self.dialogSchema = schema;
|
self.dialogSchema = schema;
|
||||||
self.dialogData = data;
|
self.dialogData = data;
|
||||||
self.dialogOpen = true;
|
self.dialogOpen = true;
|
||||||
|
callback && dialogCallbacks.set(self.dialogData, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
function closeDialog() {
|
function closeDialog(result?: any) {
|
||||||
|
const callback = dialogCallbacks.get(self.dialogData);
|
||||||
self.dialogOpen = false;
|
self.dialogOpen = false;
|
||||||
|
|
||||||
|
if (callback) {
|
||||||
|
dialogCallbacks.delete(self.dialogData);
|
||||||
|
setTimeout(() => callback(result), 200);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
focus,
|
||||||
|
blur,
|
||||||
config,
|
config,
|
||||||
changeValue,
|
changeValue,
|
||||||
validate,
|
validate,
|
||||||
|
|
|
@ -2,6 +2,7 @@ import {types, getRoot, Instance} from 'mobx-state-tree';
|
||||||
import {extendObject, createObject} from '../utils/helper';
|
import {extendObject, createObject} from '../utils/helper';
|
||||||
import {IRendererStore} from './index';
|
import {IRendererStore} from './index';
|
||||||
import {dataMapping} from '../utils/tpl-builtin';
|
import {dataMapping} from '../utils/tpl-builtin';
|
||||||
|
import {SimpleMap} from '../utils/SimpleMap';
|
||||||
|
|
||||||
export const iRendererStore = types
|
export const iRendererStore = types
|
||||||
.model('iRendererStore', {
|
.model('iRendererStore', {
|
||||||
|
@ -32,7 +33,7 @@ export const iRendererStore = types
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
.actions(self => {
|
.actions(self => {
|
||||||
const dialogCallbacks = new Map();
|
const dialogCallbacks = new SimpleMap<(result?: any) => void>();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
initData(data: object = {}) {
|
initData(data: object = {}) {
|
||||||
|
@ -97,10 +98,7 @@ export const iRendererStore = types
|
||||||
self.dialogData = data;
|
self.dialogData = data;
|
||||||
}
|
}
|
||||||
self.dialogOpen = true;
|
self.dialogOpen = true;
|
||||||
|
callback && dialogCallbacks.set(self.dialogData, callback);
|
||||||
if (callback) {
|
|
||||||
dialogCallbacks.set(self.dialogData, callback);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
closeDialog(result?: any) {
|
closeDialog(result?: any) {
|
||||||
|
|
Loading…
Reference in New Issue