forked from p96170835/amis
Select 多选模式 tip 没显示
This commit is contained in:
parent
0513a65659
commit
ac216279ca
|
@ -5,24 +5,24 @@
|
||||||
* @date 2017-11-07
|
* @date 2017-11-07
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import uncontrollable = require("uncontrollable");
|
import uncontrollable = require('uncontrollable');
|
||||||
import React from "react";
|
import React from 'react';
|
||||||
import "react-datetime/css/react-datetime.css";
|
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 { closeIcon, Icon } from "./icons";
|
import {closeIcon, Icon} from './icons';
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import matchSorter from "match-sorter";
|
import matchSorter from 'match-sorter';
|
||||||
import { noop, isObject } from "../utils/helper";
|
import {noop, isObject} from '../utils/helper';
|
||||||
import find = require("lodash/find");
|
import find = require('lodash/find');
|
||||||
import isPlainObject = require("lodash/isPlainObject");
|
import isPlainObject = require('lodash/isPlainObject');
|
||||||
import union = require("lodash/union");
|
import union = require('lodash/union');
|
||||||
import { highlight } from "../renderers/Form/Options";
|
import {highlight} from '../renderers/Form/Options';
|
||||||
import { findDOMNode } from "react-dom";
|
import {findDOMNode} from 'react-dom';
|
||||||
import { ClassNamesFn, themeable } from "../theme";
|
import {ClassNamesFn, themeable} from '../theme';
|
||||||
import Checkbox from "./Checkbox";
|
import Checkbox from './Checkbox';
|
||||||
import Input from "./Input";
|
import Input from './Input';
|
||||||
|
|
||||||
export interface Option {
|
export interface Option {
|
||||||
label?: string;
|
label?: string;
|
||||||
|
@ -68,8 +68,8 @@ export function value2array(
|
||||||
props: Partial<OptionProps>
|
props: Partial<OptionProps>
|
||||||
): Array<Option> {
|
): Array<Option> {
|
||||||
if (props.multi || props.multiple) {
|
if (props.multi || props.multiple) {
|
||||||
if (typeof value === "string") {
|
if (typeof value === 'string') {
|
||||||
value = value.split(props.delimiter || ",");
|
value = value.split(props.delimiter || ',');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Array.isArray(value)) {
|
if (!Array.isArray(value)) {
|
||||||
|
@ -98,9 +98,9 @@ export function expandValue(
|
||||||
const valueType = typeof value;
|
const valueType = typeof value;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
valueType !== "string" &&
|
valueType !== 'string' &&
|
||||||
valueType !== "number" &&
|
valueType !== 'number' &&
|
||||||
valueType !== "boolean"
|
valueType !== 'boolean'
|
||||||
) {
|
) {
|
||||||
return value as Option;
|
return value as Option;
|
||||||
}
|
}
|
||||||
|
@ -113,23 +113,23 @@ export function expandValue(
|
||||||
|
|
||||||
return find(
|
return find(
|
||||||
options,
|
options,
|
||||||
optionValueCompare(value, props.valueField || "value")
|
optionValueCompare(value, props.valueField || 'value')
|
||||||
) as Option;
|
) as Option;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function matchOptionValue(
|
export function matchOptionValue(
|
||||||
a: OptionValue,
|
a: OptionValue,
|
||||||
b: Option,
|
b: Option,
|
||||||
valueField: string = "value"
|
valueField: string = 'value'
|
||||||
) {
|
) {
|
||||||
return isObject(a)
|
return isObject(a)
|
||||||
? a === b[valueField || "value"]
|
? a === b[valueField || 'value']
|
||||||
: String(b[valueField || "value"]) === String(a);
|
: String(b[valueField || 'value']) === String(a);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function optionValueCompare(
|
export function optionValueCompare(
|
||||||
a: OptionValue,
|
a: OptionValue,
|
||||||
valueField: string = "value"
|
valueField: string = 'value'
|
||||||
) {
|
) {
|
||||||
return (b: Option) => matchOptionValue(a, b, valueField);
|
return (b: Option) => matchOptionValue(a, b, valueField);
|
||||||
}
|
}
|
||||||
|
@ -137,14 +137,14 @@ export function optionValueCompare(
|
||||||
export function normalizeOptions(
|
export function normalizeOptions(
|
||||||
options: string | {[propName: string]: string} | Array<string> | Options
|
options: string | {[propName: string]: string} | Array<string> | Options
|
||||||
): Options {
|
): Options {
|
||||||
if (typeof options === "string") {
|
if (typeof options === 'string') {
|
||||||
return options.split(",").map(item => ({
|
return options.split(',').map(item => ({
|
||||||
label: item,
|
label: item,
|
||||||
value: item
|
value: item
|
||||||
}));
|
}));
|
||||||
} else if (
|
} else if (
|
||||||
Array.isArray(options as Array<string>) &&
|
Array.isArray(options as Array<string>) &&
|
||||||
typeof (options as Array<string>)[0] === "string"
|
typeof (options as Array<string>)[0] === 'string'
|
||||||
) {
|
) {
|
||||||
return (options as Array<string>).map(item => ({
|
return (options as Array<string>).map(item => ({
|
||||||
label: item,
|
label: item,
|
||||||
|
@ -157,7 +157,7 @@ export function normalizeOptions(
|
||||||
value: item && item.value
|
value: item && item.value
|
||||||
};
|
};
|
||||||
|
|
||||||
if (typeof option.children !== "undefined") {
|
if (typeof option.children !== 'undefined') {
|
||||||
option.children = normalizeOptions(option.children);
|
option.children = normalizeOptions(option.children);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -224,22 +224,22 @@ export class Select extends React.Component<SelectProps, SelectState> {
|
||||||
multiple: false,
|
multiple: false,
|
||||||
clearable: true,
|
clearable: true,
|
||||||
creatable: false,
|
creatable: false,
|
||||||
createBtnLabel: "新增选项",
|
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',
|
||||||
inline: false,
|
inline: false,
|
||||||
disabled: false,
|
disabled: false,
|
||||||
checkAll: false,
|
checkAll: false,
|
||||||
checkAllLabel: "全选",
|
checkAllLabel: '全选',
|
||||||
defaultCheckAll: false,
|
defaultCheckAll: false,
|
||||||
overlayPlacement: "auto"
|
overlayPlacement: 'auto'
|
||||||
};
|
};
|
||||||
|
|
||||||
input: HTMLInputElement;
|
input: HTMLInputElement;
|
||||||
|
@ -269,7 +269,7 @@ export class Select extends React.Component<SelectProps, SelectState> {
|
||||||
this.state = {
|
this.state = {
|
||||||
isOpen: props.defaultOpen || false,
|
isOpen: props.defaultOpen || false,
|
||||||
isFocused: false,
|
isFocused: false,
|
||||||
inputValue: "",
|
inputValue: '',
|
||||||
highlightedIndex: -1,
|
highlightedIndex: -1,
|
||||||
selection: value2array(props.value, props)
|
selection: value2array(props.value, props)
|
||||||
};
|
};
|
||||||
|
@ -298,7 +298,7 @@ export class Select extends React.Component<SelectProps, SelectState> {
|
||||||
onChange(simpleValue ? selection.map(item => item.value) : selection);
|
onChange(simpleValue ? selection.map(item => item.value) : selection);
|
||||||
}
|
}
|
||||||
|
|
||||||
loadOptions && loadOptions("");
|
loadOptions && loadOptions('');
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillReceiveProps(nextProps: SelectProps) {
|
componentWillReceiveProps(nextProps: SelectProps) {
|
||||||
|
@ -482,13 +482,13 @@ export class Select extends React.Component<SelectProps, SelectState> {
|
||||||
if (Object.keys(update).length) {
|
if (Object.keys(update).length) {
|
||||||
this.setState(
|
this.setState(
|
||||||
update,
|
update,
|
||||||
doLoad && loadOptions ? () => loadOptions("") : undefined
|
doLoad && loadOptions ? () => loadOptions('') : undefined
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleKeyPress(e: React.KeyboardEvent) {
|
handleKeyPress(e: React.KeyboardEvent) {
|
||||||
if (e.key === " ") {
|
if (e.key === ' ') {
|
||||||
this.toggle();
|
this.toggle();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -497,7 +497,7 @@ export class Select extends React.Component<SelectProps, SelectState> {
|
||||||
const onChange = this.props.onChange;
|
const onChange = this.props.onChange;
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
onChange("");
|
onChange('');
|
||||||
}
|
}
|
||||||
|
|
||||||
handleAddClick() {
|
handleAddClick() {
|
||||||
|
@ -542,18 +542,18 @@ export class Select extends React.Component<SelectProps, SelectState> {
|
||||||
multiple ? (
|
multiple ? (
|
||||||
<div className={`${ns}Select-value`} key={index}>
|
<div className={`${ns}Select-value`} key={index}>
|
||||||
<span
|
<span
|
||||||
className={`${ns}Select-valueIcon ${disabled ? "is-disabled" : ""}`}
|
className={`${ns}Select-valueIcon ${disabled ? 'is-disabled' : ''}`}
|
||||||
onClick={this.removeItem.bind(this, index)}
|
onClick={this.removeItem.bind(this, index)}
|
||||||
>
|
>
|
||||||
×
|
×
|
||||||
</span>
|
</span>
|
||||||
<span className={`${ns}Select-valueLabel`}>
|
<span className={`${ns}Select-valueLabel`}>
|
||||||
{item[labelField || "label"]}
|
{item[labelField || 'label']}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className={`${ns}Select-value`} key={index}>
|
<div className={`${ns}Select-value`} key={index}>
|
||||||
{item[labelField || "label"]}
|
{item[labelField || 'label']}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
@ -595,7 +595,7 @@ export class Select extends React.Component<SelectProps, SelectState> {
|
||||||
let filtedOptions: Array<Option> =
|
let filtedOptions: Array<Option> =
|
||||||
inputValue && isOpen && !loadOptions
|
inputValue && isOpen && !loadOptions
|
||||||
? matchSorter(options, inputValue, {
|
? matchSorter(options, inputValue, {
|
||||||
keys: [labelField || "label", valueField || "value"]
|
keys: [labelField || 'label', valueField || 'value']
|
||||||
})
|
})
|
||||||
: options.concat();
|
: options.concat();
|
||||||
|
|
||||||
|
@ -612,11 +612,11 @@ export class Select extends React.Component<SelectProps, SelectState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
const menu = (
|
const menu = (
|
||||||
<div ref={this.menu} className={cx("Select-menu")}>
|
<div ref={this.menu} className={cx('Select-menu')}>
|
||||||
{searchable ? (
|
{searchable ? (
|
||||||
<div
|
<div
|
||||||
className={cx(`Select-input`, {
|
className={cx(`Select-input`, {
|
||||||
"is-focused": this.state.isFocused
|
'is-focused': this.state.isFocused
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<Icon icon="search" className="icon" />
|
<Icon icon="search" className="icon" />
|
||||||
|
@ -634,7 +634,7 @@ export class Select extends React.Component<SelectProps, SelectState> {
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
{multiple && checkAll && filtedOptions.length ? (
|
{multiple && checkAll && filtedOptions.length ? (
|
||||||
<div className={cx("Select-option")}>
|
<div className={cx('Select-option')}>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={checkedPartial}
|
checked={checkedPartial}
|
||||||
partial={checkedPartial && !checkedAll}
|
partial={checkedPartial && !checkedAll}
|
||||||
|
@ -654,7 +654,7 @@ export class Select extends React.Component<SelectProps, SelectState> {
|
||||||
<div
|
<div
|
||||||
{...getItemProps({
|
{...getItemProps({
|
||||||
key:
|
key:
|
||||||
typeof item.value === "string"
|
typeof item.value === 'string'
|
||||||
? `${item.label}-${item.value}`
|
? `${item.label}-${item.value}`
|
||||||
: index,
|
: index,
|
||||||
index,
|
index,
|
||||||
|
@ -662,9 +662,9 @@ export class Select extends React.Component<SelectProps, SelectState> {
|
||||||
disabled: item.disabled
|
disabled: item.disabled
|
||||||
})}
|
})}
|
||||||
className={cx(`Select-option`, {
|
className={cx(`Select-option`, {
|
||||||
"is-disabled": item.disabled,
|
'is-disabled': item.disabled,
|
||||||
"is-highlight": highlightedIndex === index,
|
'is-highlight': highlightedIndex === index,
|
||||||
"is-active": checked
|
'is-active': checked
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
{removable ? (
|
{removable ? (
|
||||||
|
@ -699,8 +699,10 @@ export class Select extends React.Component<SelectProps, SelectState> {
|
||||||
: highlight(
|
: highlight(
|
||||||
item[labelField],
|
item[labelField],
|
||||||
inputValue as string,
|
inputValue as string,
|
||||||
cx("Select-option-hl")
|
cx('Select-option-hl')
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{item.tip}
|
||||||
</Checkbox>
|
</Checkbox>
|
||||||
) : (
|
) : (
|
||||||
<span>
|
<span>
|
||||||
|
@ -709,7 +711,7 @@ export class Select extends React.Component<SelectProps, SelectState> {
|
||||||
: highlight(
|
: highlight(
|
||||||
item[labelField],
|
item[labelField],
|
||||||
inputValue as string,
|
inputValue as string,
|
||||||
cx("Select-option-hl")
|
cx('Select-option-hl')
|
||||||
)}
|
)}
|
||||||
{item.tip}
|
{item.tip}
|
||||||
</span>
|
</span>
|
||||||
|
@ -718,11 +720,11 @@ export class Select extends React.Component<SelectProps, SelectState> {
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
) : (
|
) : (
|
||||||
<div className={cx("Select-noResult")}>{noResultsText}</div>
|
<div className={cx('Select-noResult')}>{noResultsText}</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{creatable && !disabled ? (
|
{creatable && !disabled ? (
|
||||||
<a className={cx("Select-addBtn")} onClick={this.handleAddClick}>
|
<a className={cx('Select-addBtn')} onClick={this.handleAddClick}>
|
||||||
<Icon icon="plus" className="icon" />
|
<Icon icon="plus" className="icon" />
|
||||||
{createBtnLabel}
|
{createBtnLabel}
|
||||||
</a>
|
</a>
|
||||||
|
@ -739,8 +741,8 @@ export class Select extends React.Component<SelectProps, SelectState> {
|
||||||
>
|
>
|
||||||
<PopOver
|
<PopOver
|
||||||
overlay
|
overlay
|
||||||
className={cx("Select-popover")}
|
className={cx('Select-popover')}
|
||||||
style={{ minWidth: this.target ? this.target.offsetWidth : "auto" }}
|
style={{minWidth: this.target ? this.target.offsetWidth : 'auto'}}
|
||||||
onHide={this.close}
|
onHide={this.close}
|
||||||
>
|
>
|
||||||
{menu}
|
{menu}
|
||||||
|
@ -781,7 +783,7 @@ export class Select extends React.Component<SelectProps, SelectState> {
|
||||||
: this.handleChange
|
: this.handleChange
|
||||||
}
|
}
|
||||||
onStateChange={this.handleStateChange}
|
onStateChange={this.handleStateChange}
|
||||||
itemToString={item => (item ? item[labelField] : "")}
|
itemToString={item => (item ? item[labelField] : '')}
|
||||||
>
|
>
|
||||||
{(options: ControllerStateAndHelpers<any>) => {
|
{(options: ControllerStateAndHelpers<any>) => {
|
||||||
const {isOpen} = options;
|
const {isOpen} = options;
|
||||||
|
@ -798,9 +800,9 @@ export class Select extends React.Component<SelectProps, SelectState> {
|
||||||
[`Select--multi`]: multiple,
|
[`Select--multi`]: multiple,
|
||||||
[`Select--inline`]: inline,
|
[`Select--inline`]: inline,
|
||||||
[`Select--searchable`]: searchable,
|
[`Select--searchable`]: searchable,
|
||||||
"is-opened": isOpen,
|
'is-opened': isOpen,
|
||||||
"is-focused": this.state.isFocused,
|
'is-focused': this.state.isFocused,
|
||||||
"is-disabled": disabled
|
'is-disabled': disabled
|
||||||
},
|
},
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
|
@ -809,17 +811,17 @@ export class Select extends React.Component<SelectProps, SelectState> {
|
||||||
{this.renderValue(options)}
|
{this.renderValue(options)}
|
||||||
</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')}>
|
||||||
<Icon icon="close" className="icon" />
|
<Icon icon="close" className="icon" />
|
||||||
</a>
|
</a>
|
||||||
) : null}
|
) : null}
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<span className={cx("Select-spinner")}>
|
<span className={cx('Select-spinner')}>
|
||||||
<i className={spinnerClassName} />
|
<i className={spinnerClassName} />
|
||||||
</span>
|
</span>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
<span className={cx("Select-arrow")} />
|
<span className={cx('Select-arrow')} />
|
||||||
{isOpen ? this.renderOuter(options) : null}
|
{isOpen ? this.renderOuter(options) : null}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -831,6 +833,6 @@ export class Select extends React.Component<SelectProps, SelectState> {
|
||||||
|
|
||||||
export default themeable(
|
export default themeable(
|
||||||
uncontrollable(Select, {
|
uncontrollable(Select, {
|
||||||
value: "onChange"
|
value: 'onChange'
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
|
@ -12,8 +12,6 @@ import {RootCloseWrapper} from 'react-overlays';
|
||||||
import PopOver, {Offset} from '../components/PopOver';
|
import PopOver, {Offset} from '../components/PopOver';
|
||||||
import Overlay from '../components/Overlay';
|
import Overlay from '../components/Overlay';
|
||||||
|
|
||||||
const allowedPositions = ['center', 'top'];
|
|
||||||
|
|
||||||
export interface PopOverConfig {
|
export interface PopOverConfig {
|
||||||
saveImmediately?: boolean;
|
saveImmediately?: boolean;
|
||||||
mode?: 'dialog' | 'drawer' | 'popOver';
|
mode?: 'dialog' | 'drawer' | 'popOver';
|
||||||
|
@ -30,8 +28,8 @@ export interface PopOverConfig {
|
||||||
| 'fixed-right-top'
|
| 'fixed-right-top'
|
||||||
| 'fixed-left-bottom'
|
| 'fixed-left-bottom'
|
||||||
| 'fixed-right-bottom';
|
| 'fixed-right-bottom';
|
||||||
|
offset?: Offset;
|
||||||
[propName: string]: any;
|
[propName: string]: any;
|
||||||
offset: Offset;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PopOverProps extends RendererProps {
|
export interface PopOverProps extends RendererProps {
|
||||||
|
@ -159,6 +157,7 @@ export const HocPopOver = (config: Partial<PopOverConfig> = {}) => (
|
||||||
const isFixed = /^fixed\-/.test(position);
|
const isFixed = /^fixed\-/.test(position);
|
||||||
|
|
||||||
return isFixed ? (
|
return isFixed ? (
|
||||||
|
// @ts-ignore
|
||||||
<RootCloseWrapper
|
<RootCloseWrapper
|
||||||
disabled={!this.state.isOpened}
|
disabled={!this.state.isOpened}
|
||||||
onRootClose={this.closePopOver}
|
onRootClose={this.closePopOver}
|
||||||
|
@ -179,7 +178,7 @@ export const HocPopOver = (config: Partial<PopOverConfig> = {}) => (
|
||||||
<PopOver
|
<PopOver
|
||||||
classPrefix={ns}
|
classPrefix={ns}
|
||||||
className={cx('PopOverAble-popover')}
|
className={cx('PopOverAble-popover')}
|
||||||
offset={popOver.offset}
|
offset={(popOver as PopOverConfig).offset}
|
||||||
>
|
>
|
||||||
{content}
|
{content}
|
||||||
</PopOver>
|
</PopOver>
|
||||||
|
@ -189,13 +188,11 @@ export const HocPopOver = (config: Partial<PopOverConfig> = {}) => (
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
onQuickChange,
|
|
||||||
popOver,
|
popOver,
|
||||||
popOverEnabled,
|
popOverEnabled,
|
||||||
className,
|
className,
|
||||||
noHoc,
|
noHoc,
|
||||||
classnames: cx,
|
classnames: cx
|
||||||
render
|
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
if (!popOver || popOverEnabled === false || noHoc) {
|
if (!popOver || popOverEnabled === false || noHoc) {
|
||||||
|
|
Loading…
Reference in New Issue