Select 多选模式 tip 没显示

This commit is contained in:
2betop 2020-01-06 18:56:30 +08:00
parent 0513a65659
commit ac216279ca
2 changed files with 98 additions and 99 deletions

View File

@ -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;
@ -46,7 +46,7 @@ export interface OptionProps {
delimiter?: string; delimiter?: string;
clearable?: boolean; clearable?: boolean;
placeholder?: string; placeholder?: string;
autoFill?: { [propName: string]: any }; autoFill?: {[propName: string]: any};
creatable?: boolean; creatable?: boolean;
onAdd?: ( onAdd?: (
idx?: number | Array<number>, idx?: number | Array<number>,
@ -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,14 +98,14 @@ 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;
} }
let { options } = props; let {options} = props;
if (!options) { if (!options) {
return null; return null;
@ -113,38 +113,38 @@ 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);
} }
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);
} }
@ -165,7 +165,7 @@ export function normalizeOptions(
}); });
} else if (isPlainObject(options)) { } else if (isPlainObject(options)) {
return Object.keys(options).map(key => ({ return Object.keys(options).map(key => ({
label: (options as { [propName: string]: string })[key] as string, label: (options as {[propName: string]: string})[key] as string,
value: key value: key
})); }));
} }
@ -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)
}; };
@ -284,7 +284,7 @@ export class Select extends React.Component<SelectProps, SelectState> {
onChange, onChange,
simpleValue simpleValue
} = this.props; } = this.props;
let { selection } = this.state; let {selection} = this.state;
if (multiple && defaultCheckAll && options.length) { if (multiple && defaultCheckAll && options.length) {
selection = union(options, selection); selection = union(options, selection);
@ -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) {
@ -395,8 +395,8 @@ export class Select extends React.Component<SelectProps, SelectState> {
} }
toggleCheckAll() { toggleCheckAll() {
const { options, onChange, simpleValue } = this.props; const {options, onChange, simpleValue} = this.props;
let { selection } = this.state; let {selection} = this.state;
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);
const checkedAll = optionsValues.every( const checkedAll = optionsValues.every(
@ -408,11 +408,11 @@ export class Select extends React.Component<SelectProps, SelectState> {
} }
removeItem(index: number, e?: React.MouseEvent<HTMLElement>) { removeItem(index: number, e?: React.MouseEvent<HTMLElement>) {
const { onChange, simpleValue, disabled } = this.props; const {onChange, simpleValue, disabled} = this.props;
if (disabled) { if (disabled) {
return; return;
} }
let { selection: value } = this.state; let {selection: value} = this.state;
e && e.stopPropagation(); e && e.stopPropagation();
value = Array.isArray(value) ? value.concat() : [value]; value = Array.isArray(value) ? value.concat() : [value];
@ -422,7 +422,7 @@ export class Select extends React.Component<SelectProps, SelectState> {
} }
handleInputChange(evt: React.ChangeEvent<HTMLInputElement>) { handleInputChange(evt: React.ChangeEvent<HTMLInputElement>) {
const { loadOptions } = this.props; const {loadOptions} = this.props;
this.setState( this.setState(
{ {
@ -433,8 +433,8 @@ export class Select extends React.Component<SelectProps, SelectState> {
} }
handleChange(selectItem: any) { handleChange(selectItem: any) {
const { onChange, multiple, simpleValue } = this.props; const {onChange, multiple, simpleValue} = this.props;
let { selection } = this.state; let {selection} = this.state;
if (multiple) { if (multiple) {
const selectionValues = selection.map(item => item.value); const selectionValues = selection.map(item => item.value);
@ -452,7 +452,7 @@ export class Select extends React.Component<SelectProps, SelectState> {
} }
handleStateChange(changes: any) { handleStateChange(changes: any) {
const { multiple, checkAll } = this.props; const {multiple, checkAll} = this.props;
let update: any = {}; let update: any = {};
const loadOptions = this.props.loadOptions; const loadOptions = this.props.loadOptions;
let doLoad = false; let doLoad = false;
@ -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,29 +497,29 @@ 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() {
const { onAdd } = this.props; const {onAdd} = this.props;
onAdd && onAdd(); onAdd && onAdd();
} }
handleEditClick(e: Event, item: any) { handleEditClick(e: Event, item: any) {
const { onEdit } = this.props; const {onEdit} = this.props;
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
onEdit && onEdit(item); onEdit && onEdit(item);
} }
handleDeleteClick(e: Event, item: any) { handleDeleteClick(e: Event, item: any) {
const { onDelete } = this.props; const {onDelete} = this.props;
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
onDelete && onDelete(item); onDelete && onDelete(item);
} }
renderValue({ inputValue, isOpen }: ControllerStateAndHelpers<any>) { renderValue({inputValue, isOpen}: ControllerStateAndHelpers<any>) {
const { const {
multiple, multiple,
placeholder, placeholder,
@ -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>
) )
); );
@ -588,14 +588,14 @@ export class Select extends React.Component<SelectProps, SelectState> {
removable, removable,
overlayPlacement overlayPlacement
} = this.props; } = this.props;
const { selection } = this.state; const {selection} = this.state;
let checkedAll = false; let checkedAll = false;
let checkedPartial = false; let checkedPartial = false;
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,10 +783,10 @@ 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;
return ( return (
<div <div
tabIndex={disabled ? -1 : 0} tabIndex={disabled ? -1 : 0}
@ -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'
}) })
); );

View File

@ -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) {