zarm-web
Version:
基于 React 的桌面端UI库
418 lines (365 loc) • 10.6 kB
JavaScript
function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
import React, { Component } from 'react';
import Events from '../utils/events';
import { FormItemContext } from '../form/createContext';
import Option from './Option';
import Dropdown from '../dropdown';
import Menu from '../menu';
import InputWithTags from '../tag-input';
import LocaleReceiver from '../locale-provider/LocaleReceiver';
import SelectMultiple from './SelectMultiple';
import { isEmpty } from '../utils';
const EMPTY_STRING = '';
const EMPTY_STRING_VALUE = '$$EMPTY_STRING_VALUE';
/**
* placeholder
*/
class Select extends Component {
static mapEmptyStringToEmptyValue(values) {
if (Array.isArray(values)) {
return values.map(value => {
if (value === EMPTY_STRING) {
return EMPTY_STRING_VALUE;
}
return value;
});
}
if (values === EMPTY_STRING) {
return EMPTY_STRING_VALUE;
}
return values;
}
static getOptionMap(options) {
if (!Array.isArray(options)) {
options = [options];
}
const optionData = [];
const optionMap = {};
React.Children.map(options, option => {
if (option && typeof option === 'object' && option.type) {
const value = Select.mapEmptyStringToEmptyValue(option.props.value);
if (option.props && value) {
// handle optionMap
optionMap[value] = option; // handle OptionData
optionData.push({
key: option.key,
props: option.props,
value,
children: option.props.children
});
}
}
return optionMap;
});
return [optionMap, optionData];
}
static mapEmptyValueToEmptyString(selected) {
return selected.map(select => {
if (select === EMPTY_STRING_VALUE) {
return EMPTY_STRING;
}
return select;
});
}
constructor(props) {
super(props);
this.inputBox = void 0;
this.inputWithTags = void 0;
this.oldInputDivHeight = 0;
this.inputWithTagsRef = e => {
this.inputWithTags = e;
};
this.onDeleteTag = (_e, _key, _value, index) => {
const selected = Select.mapEmptyValueToEmptyString(this.state.value.slice());
selected.splice(index, 1);
const selectedData = selected.map((select, selectIndex) => {
const vdom = this.state.optionMap[select || EMPTY_STRING_VALUE];
const text = vdom ? vdom.props.children : '';
return {
text,
value: select,
index: selectIndex
};
});
this.props.onChange(selected, selectedData);
};
this.onSearchValueChange = value => {
const {
onSearchChange
} = this.props; // const textContent = (e.target as HTMLDivElement).textContent;
this.setState({
searchValue: value,
dropdown: true
}, () => {
if (typeof onSearchChange === 'function') {
onSearchChange(this.state.searchValue);
}
});
};
const _value2 = props.value === undefined ? props.defaultValue : props.value;
const state = {
value: String(_value2),
dropdown: false,
searchValue: '',
showPlacehoder: true,
optionMap: {},
optionData: []
};
const {
children
} = this.props;
if (props.multiple) {
if (!Array.isArray(_value2)) {
state.value = [String(_value2)];
} else {
state.value = _value2.map(val => String(val));
}
} else {
state.value = String(_value2);
}
state.value = Select.mapEmptyStringToEmptyValue(state.value);
const [optionMap, optionData] = Select.getOptionMap(children);
state.optionMap = optionMap;
state.optionData = optionData;
this.state = state;
}
componentDidMount() {
this.bindOuterHandlers();
}
componentWillReceiveProps(nextProps) {
const {
defaultValue,
children
} = this.props;
if ('value' in nextProps || nextProps.defaultValue !== defaultValue) {
let value = nextProps.value === undefined ? nextProps.defaultValue : nextProps.value;
if (nextProps.multiple) {
if (!Array.isArray(value)) {
value = [String(value)];
} else {
value = value.map(val => String(val));
}
} else {
value = String(value);
}
this.setState({
value: Select.mapEmptyStringToEmptyValue(value)
});
}
if (nextProps.children !== children) {
const [optionMap, optionData] = Select.getOptionMap(nextProps.children);
this.setState({
optionData,
optionMap
});
}
}
componentWillUnmount() {
this.unbindOuterHandlers();
}
onOptionChange(_, props, index) {
const {
multiple,
onChange
} = this.props;
const {
handleFieldChange
} = this.context;
const {
optionMap,
optionData,
value: stateValue
} = this.state;
if ('disabled' in props || props.isDisabled) {
return;
}
if (props.search || props.isSearch) {
this.setState({
searchValue: ''
});
this.inputBox.textContent = '';
}
if (!isEmpty(this.context)) {
handleFieldChange();
}
const value = String(props.value);
if (multiple) {
const selected = Select.mapEmptyValueToEmptyString(stateValue.slice());
const position = selected.indexOf(value);
if (position === -1) {
selected.push(value);
} else {
selected.splice(position, 1);
}
const selectedData = selected.map(select => {
const selectValue = select || EMPTY_STRING_VALUE;
const vdom = optionMap[selectValue];
const text = vdom ? vdom.props.children : '';
const indexs = optionData.findIndex(elem => String(elem.value) === String(selectValue));
return {
text,
value: select,
indexs
};
});
this.setState({
value: Select.mapEmptyStringToEmptyValue(selected)
}, () => {
onChange(selected, selectedData);
});
return;
}
const selected = {
index,
value,
text: Array.isArray(props.children) ? props.children.join() : props.children
};
this.setState({
value: Select.mapEmptyStringToEmptyValue(value)
}, () => {
this.setDropdown(false, () => onChange(selected));
});
}
setDropdown(isOpen, callback) {
this.setState({
dropdown: isOpen,
searchValue: ''
}, () => {
if (typeof callback === 'function') {
callback();
}
});
}
bindOuterHandlers() {
Events.on(document, 'keyup', e => this.handleKeyup(e));
}
unbindOuterHandlers() {
Events.off(document, 'keyup', e => this.handleKeyup(e));
}
handleKeyup(e) {
const {
dropdown
} = this.state;
if (dropdown === true && e.keyCode === 27) {
this.setDropdown(false);
}
}
render() {
const {
searchValue,
value,
optionMap,
optionData,
dropdown
} = this.state;
const {
prefixCls,
placeholder,
size,
tagTheme,
style,
zIndex,
multiple,
getPopupContainer,
locale,
remoteSearch
} = this.props;
const disabled = 'disabled' in this.props;
const radius = 'radius' in this.props;
const search = 'search' in this.props;
const placeholderText = placeholder || locale.placeholder;
let valueText;
if (multiple && Array.isArray(value)) {
valueText = value.reduce((prev, item) => {
if (optionMap[item]) {
prev.push({
key: item,
value: optionMap[item].props.children
});
}
return prev;
}, []);
} else {
const optionProps = optionMap[value];
if (optionProps) {
const optionChildren = optionProps.props.children;
Array.isArray(optionChildren) ? valueText = optionChildren.join() : valueText = optionChildren;
}
}
const children = [];
const filterCondition = (option, optionIndex) => {
if (search && searchValue) {
return String(option.props.children).includes(searchValue);
} // remoteSearch会走此处逻辑
return optionIndex > -1;
};
optionData.filter(filterCondition).forEach((elem, index) => {
const checked = Array.isArray(value) ? value.indexOf(String(elem.value)) > -1 : String(elem.value) === value;
children.push(React.createElement(Option, _extends({
key: elem.key || elem.value,
showCheckIcon: checked
}, elem.props, {
checked: checked,
onChange: e => {
this.onOptionChange(e, elem.props, index);
}
}), elem.children));
});
const menuStyle = {
maxHeight: 250,
overflow: 'auto'
};
const menus = children && children.length > 0 ? React.createElement(Menu, {
size: size,
style: menuStyle
}, children) : React.createElement("span", {
className: `${prefixCls}--notfound`
}, locale.noMatch);
return React.createElement(Dropdown, {
triggerBoxStyle: style,
disabled: disabled,
visible: dropdown,
isRadius: radius,
overlay: menus,
zIndex: zIndex,
getPopupContainer: getPopupContainer,
onVisibleChange: visible => {
if (visible === true) {
this.setState({
dropdown: visible,
searchValue: ''
});
} else {
this.setState({
dropdown: visible
});
}
}
}, React.createElement(InputWithTags, {
tagTheme: tagTheme,
radius: radius,
size: size,
disabled: disabled,
ref: this.inputWithTagsRef,
style: style,
searchValue: searchValue,
search: search,
remoteSearch: remoteSearch,
active: dropdown,
value: valueText,
placeholder: placeholderText,
onDeleteTag: this.onDeleteTag,
onSearchChange: this.onSearchValueChange
}));
}
}
Select.contextType = FormItemContext;
Select.defaultProps = {
prefixCls: 'za-select',
radius: true,
onSearchChange: () => {},
onChange: () => {}
};
Select.Option = Option;
Select.Multiple = SelectMultiple;
export default LocaleReceiver('Select')(Select);