rc-tree-select
Version:
tree-select ui component for react
641 lines (575 loc) • 20.4 kB
JavaScript
'use strict';
Object.defineProperty(exports, '__esModule', {
value: true
});
var _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; };
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) arr2[i] = arr[i]; return arr2; } else { return Array.from(arr); } }
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
var _react = require('react');
var _react2 = _interopRequireDefault(_react);
var _reactDom = require('react-dom');
var _reactDom2 = _interopRequireDefault(_reactDom);
var _rcUtil = require('rc-util');
var _classnames = require('classnames');
var _classnames2 = _interopRequireDefault(_classnames);
var _rcAnimate = require('rc-animate');
var _rcAnimate2 = _interopRequireDefault(_rcAnimate);
var _util = require('./util');
var _SelectTrigger = require('./SelectTrigger');
var _SelectTrigger2 = _interopRequireDefault(_SelectTrigger);
function noop() {}
function filterFn(input, child) {
return String((0, _util.getPropValue)(child, this.props.treeNodeFilterProp)).indexOf(input) > -1;
}
function saveRef(name, component) {
this[name] = component;
}
var Select = _react2['default'].createClass({
displayName: 'Select',
propTypes: {
children: _react.PropTypes.any,
multiple: _react.PropTypes.bool,
filterTreeNode: _react.PropTypes.any,
showSearch: _react.PropTypes.bool,
disabled: _react.PropTypes.bool,
showArrow: _react.PropTypes.bool,
tags: _react.PropTypes.bool,
transitionName: _react.PropTypes.string,
animation: _react.PropTypes.string,
choiceTransitionName: _react.PropTypes.string,
onClick: _react.PropTypes.func,
onChange: _react.PropTypes.func,
onSelect: _react.PropTypes.func,
onSearch: _react.PropTypes.func,
searchPlaceholder: _react.PropTypes.string,
placeholder: _react.PropTypes.any,
value: _react.PropTypes.oneOfType([_react.PropTypes.array, _react.PropTypes.string]),
defaultValue: _react.PropTypes.oneOfType([_react.PropTypes.array, _react.PropTypes.string]),
label: _react.PropTypes.oneOfType([_react.PropTypes.array, _react.PropTypes.any]),
defaultLabel: _react.PropTypes.oneOfType([_react.PropTypes.array, _react.PropTypes.any]),
dropdownStyle: _react.PropTypes.object,
maxTagTextLength: _react.PropTypes.number,
treeIcon: _react.PropTypes.bool,
treeLine: _react.PropTypes.bool,
treeDefaultExpandAll: _react.PropTypes.bool,
treeCheckable: _react.PropTypes.oneOfType([_react.PropTypes.bool, _react.PropTypes.node]),
treeNodeLabelProp: _react.PropTypes.string,
treeNodeFilterProp: _react.PropTypes.string,
loadData: _react.PropTypes.func
},
getDefaultProps: function getDefaultProps() {
return {
prefixCls: 'rc-tree-select',
filterTreeNode: filterFn,
showSearch: true,
allowClear: false,
placeholder: '',
searchPlaceholder: '',
defaultValue: [],
onClick: noop,
onChange: noop,
onSelect: noop,
onSearch: noop,
showArrow: true,
dropdownMatchSelectWidth: true,
dropdownStyle: {},
notFoundContent: 'Not Found',
treeIcon: false,
treeLine: false,
treeDefaultExpandAll: false,
treeCheckable: false,
treeNodeFilterProp: 'value',
treeNodeLabelProp: 'title'
};
},
getInitialState: function getInitialState() {
var props = this.props;
var value = [];
if ('value' in props) {
value = (0, _util.toArray)(props.value);
} else {
value = (0, _util.toArray)(props.defaultValue);
}
if (this.props.treeCheckable) {
value = (0, _util.getTreeNodesStates)(this.props.children, value).checkedKeys;
}
var label = this.getLabelFromProps(props, value, 1);
var inputValue = '';
if (props.combobox) {
inputValue = value[0] || '';
}
this.saveInputRef = saveRef.bind(this, 'inputInstance');
return { value: value, inputValue: inputValue, label: label };
},
componentWillReceiveProps: function componentWillReceiveProps(nextProps) {
if ('value' in nextProps) {
var value = (0, _util.toArray)(nextProps.value);
if (nextProps.treeCheckable) {
value = (0, _util.getTreeNodesStates)(nextProps.children, value).checkedKeys;
}
var label = this.getLabelFromProps(nextProps, value);
this.setState({
value: value,
label: label
});
if (nextProps.combobox) {
this.setState({
inputValue: value[0] || ''
});
}
}
},
componentDidUpdate: function componentDidUpdate() {
var state = this.state;
var props = this.props;
if (state.open && (0, _util.isMultipleOrTags)(props)) {
var inputNode = this.getInputDOMNode();
if (inputNode.value) {
inputNode.style.width = '';
inputNode.style.width = inputNode.scrollWidth + 'px';
} else {
inputNode.style.width = '';
}
}
},
componentWillUnmount: function componentWillUnmount() {
if (this.dropdownContainer) {
_reactDom2['default'].unmountComponentAtNode(this.dropdownContainer);
document.body.removeChild(this.dropdownContainer);
this.dropdownContainer = null;
}
},
onInputChange: function onInputChange(event) {
var val = event.target.value;
var props = this.props;
this.setState({
inputValue: val,
open: true
});
if ((0, _util.isCombobox)(props)) {
this.fireChange([val], [val]);
}
props.onSearch(val);
},
onDropdownVisibleChange: function onDropdownVisibleChange(open) {
this.setOpenState(open);
},
// combobox ignore
onKeyDown: function onKeyDown(event) {
var props = this.props;
if (props.disabled) {
return;
}
var keyCode = event.keyCode;
if (this.state.open && !this.getInputDOMNode()) {
this.onInputKeyDown(event);
} else if (keyCode === _rcUtil.KeyCode.ENTER || keyCode === _rcUtil.KeyCode.DOWN) {
this.setOpenState(true);
event.preventDefault();
}
},
onInputKeyDown: function onInputKeyDown(event) {
var props = this.props;
var state = this.state;
var keyCode = event.keyCode;
if ((0, _util.isMultipleOrTags)(props) && !event.target.value && keyCode === _rcUtil.KeyCode.BACKSPACE) {
var value = state.value.concat();
if (value.length) {
var label = state.label.concat();
value.pop();
label.pop();
this.fireChange(value, label);
}
return;
}
if (keyCode === _rcUtil.KeyCode.DOWN) {
if (!state.open) {
this.openIfHasChildren();
event.preventDefault();
event.stopPropagation();
return;
}
} else if (keyCode === _rcUtil.KeyCode.ESC) {
if (state.open) {
this.setOpenState(false);
event.preventDefault();
event.stopPropagation();
}
return;
}
if (state.open) {
var menu = this.refs.trigger.getInnerMenu();
if (menu && menu.onKeyDown(event)) {
event.preventDefault();
event.stopPropagation();
}
}
},
onSelect: function onSelect(selectedKeys, info) {
var _this = this;
var check = info.event === 'check';
if (!check && typeof info.selected === 'boolean' && !info.selected) {
this.onDeselect(info);
}
var item = info.node;
var value = this.state.value;
var label = this.state.label;
var props = this.props;
var selectedValue = (0, _util.getValuePropValue)(item);
var selectedLabel = this.getLabelFromOption(item);
props.onSelect(selectedValue, item);
var selectedNodes = info.checkedNodes || info.selectedNodes;
if ((0, _util.isMultipleOrTags)(props)) {
if (!check && value.indexOf(selectedValue) !== -1) {
return;
}
value = !check ? value.concat([selectedValue]) : [].concat(_toConsumableArray(selectedKeys));
label = !check ? label.concat([selectedLabel]) : selectedNodes.map(function (node) {
return _this.getLabelFromOption(node);
});
} else {
if (value[0] === selectedValue) {
this.setOpenState(false);
return;
}
value = !check ? [selectedValue] : [].concat(_toConsumableArray(selectedKeys));
label = !check ? [selectedLabel] : selectedNodes.map(function (node) {
return _this.getLabelFromOption(node);
});
this.setOpenState(false);
}
this.fireChange(value, label);
this.setState({
inputValue: ''
});
if ((0, _util.isCombobox)(props)) {
this.setState({
inputValue: (0, _util.getPropValue)(item, props.treeNodeLabelProp)
});
}
},
onDeselect: function onDeselect(info) {
this.removeSelected((0, _util.getValuePropValue)(info.node));
if (!(0, _util.isMultipleOrTags)(this.props)) {
this.setOpenState(false);
}
this.setState({
inputValue: ''
});
},
onPlaceholderClick: function onPlaceholderClick() {
this.getInputDOMNode().focus();
},
onClearSelection: function onClearSelection(event) {
var props = this.props;
var state = this.state;
if (props.disabled) {
return;
}
event.stopPropagation();
if (state.inputValue || state.value.length) {
this.fireChange([], []);
this.setOpenState(false);
this.setState({
inputValue: ''
});
}
},
getLabelBySingleValue: function getLabelBySingleValue(children, value) {
var _this2 = this;
if (value === undefined) {
return null;
}
var label = null;
// menu option 只有一层,treeNode 是多层嵌套
// React.Children.forEach(children, (child) => {
// if (getValuePropValue(child) === value) {
// label = this.getLabelFromOption(child);
// }
// });
var loop = function loop(childs) {
_react2['default'].Children.forEach(childs, function (item) {
if (item.props.children) {
loop(item.props.children);
}
if ((0, _util.getValuePropValue)(item) === value) {
label = _this2.getLabelFromOption(item);
}
});
};
loop(children, 0);
return label;
},
getLabelFromOption: function getLabelFromOption(child) {
return (0, _util.getPropValue)(child, this.props.treeNodeLabelProp);
},
getLabelFromProps: function getLabelFromProps(props, value, init) {
var label = [];
if ('label' in props) {
label = (0, _util.toArray)(props.label);
} else if (init && 'defaultLabel' in props) {
label = (0, _util.toArray)(props.defaultLabel);
} else {
label = this.getLabelByValue(props.children, value);
}
return label;
},
getVLForOnChange: function getVLForOnChange(vls) {
if (vls !== undefined) {
return (0, _util.isMultipleOrTags)(this.props) ? vls : vls[0];
}
return vls;
},
getLabelByValue: function getLabelByValue(children, values) {
var _this3 = this;
return values.map(function (value) {
var label = _this3.getLabelBySingleValue(children, value);
if (label === null) {
return value;
}
return label;
});
},
getDropdownContainer: function getDropdownContainer() {
if (!this.dropdownContainer) {
this.dropdownContainer = document.createElement('div');
document.body.appendChild(this.dropdownContainer);
}
return this.dropdownContainer;
},
getSearchPlaceholderElement: function getSearchPlaceholderElement(hidden) {
var props = this.props;
if (props.searchPlaceholder) {
return _react2['default'].createElement(
'span',
{
style: { display: hidden ? 'none' : 'block' },
onClick: this.onPlaceholderClick,
className: props.prefixCls + '-search__field__placeholder' },
props.searchPlaceholder
);
}
return null;
},
getInputElement: function getInputElement() {
var props = this.props;
return _react2['default'].createElement(
'span',
{ className: props.prefixCls + '-search__field__wrap' },
_react2['default'].createElement('input', { ref: this.saveInputRef,
onChange: this.onInputChange,
onKeyDown: this.onInputKeyDown,
value: this.state.inputValue,
disabled: props.disabled,
className: props.prefixCls + '-search__field',
role: 'textbox' }),
(0, _util.isMultipleOrTags)(props) ? null : this.getSearchPlaceholderElement(!!this.state.inputValue)
);
},
getInputDOMNode: function getInputDOMNode() {
return this.inputInstance;
},
getPopupDOMNode: function getPopupDOMNode() {
return this.refs.trigger.getPopupDOMNode();
},
getPopupMenuComponent: function getPopupMenuComponent() {
return this.refs.trigger.getInnerMenu();
},
setOpenState: function setOpenState(open) {
var _this4 = this;
var refs = this.refs;
this.setState({
open: open
}, function () {
if (open || (0, _util.isMultipleOrTagsOrCombobox)(_this4.props)) {
if (_this4.getInputDOMNode()) {
_this4.getInputDOMNode().focus();
}
} else if (refs.selection) {
refs.selection.focus();
}
});
},
removeSelected: function removeSelected(selectedValue) {
var props = this.props;
if (props.disabled) {
return;
}
var label = this.state.label.concat();
var index = this.state.value.indexOf(selectedValue);
var value = this.state.value.filter(function (singleValue) {
return singleValue !== selectedValue;
});
if (index !== -1) {
label.splice(index, 1);
}
this.fireChange(value, label);
},
openIfHasChildren: function openIfHasChildren() {
var props = this.props;
if (_react2['default'].Children.count(props.children) || (0, _util.isSingleMode)(props)) {
this.setOpenState(true);
}
},
isValueChange: function isValueChange(value) {
var sv = this.state.value;
if (typeof sv === 'string') {
sv = [sv];
}
if (value.length !== sv.length || !value.every(function (val, index) {
return sv[index] === val;
})) {
return true;
}
},
fireChange: function fireChange(value, label) {
var props = this.props;
if (!('value' in props)) {
this.setState({
value: value, label: label
});
}
if (this.isValueChange(value)) {
props.onChange(this.getVLForOnChange(value), this.getVLForOnChange(label), [].concat(_toConsumableArray(this.state.value)));
}
},
renderTopControlNode: function renderTopControlNode() {
var _this5 = this;
var value = this.state.value;
var label = this.state.label;
var props = this.props;
var choiceTransitionName = props.choiceTransitionName;
var prefixCls = props.prefixCls;
var maxTagTextLength = props.maxTagTextLength;
// single and not combobox, input is inside dropdown
if ((0, _util.isSingleMode)(props)) {
var placeholder = _react2['default'].createElement(
'span',
{ key: 'placeholder',
className: prefixCls + '-selection__placeholder' },
props.placeholder
);
var innerNode = placeholder;
if (this.state.label[0]) {
innerNode = _react2['default'].createElement(
'span',
{ key: 'value' },
this.state.label[0]
);
}
return _react2['default'].createElement(
'span',
{ className: prefixCls + '-selection__rendered' },
innerNode
);
}
var selectedValueNodes = [];
if ((0, _util.isMultipleOrTags)(props)) {
selectedValueNodes = value.map(function (singleValue, index) {
var content = label[index];
var title = content;
if (maxTagTextLength && typeof content === 'string' && content.length > maxTagTextLength) {
content = content.slice(0, maxTagTextLength) + '...';
}
return _react2['default'].createElement(
'li',
{ className: prefixCls + '-selection__choice',
key: singleValue,
title: title },
_react2['default'].createElement(
'span',
{ className: prefixCls + '-selection__choice__content' },
content
),
_react2['default'].createElement('span', { className: prefixCls + '-selection__choice__remove',
onClick: _this5.removeSelected.bind(_this5, singleValue) })
);
});
}
selectedValueNodes.push(_react2['default'].createElement(
'li',
{ className: prefixCls + '-search ' + prefixCls + '-search--inline', key: '__input' },
this.getInputElement()
));
var className = prefixCls + '-selection__rendered';
if ((0, _util.isMultipleOrTags)(props) && choiceTransitionName) {
return _react2['default'].createElement(
_rcAnimate2['default'],
{ className: className,
component: 'ul',
transitionName: choiceTransitionName },
selectedValueNodes
);
}
return _react2['default'].createElement(
'ul',
{ className: className },
selectedValueNodes
);
},
render: function render() {
var _rootCls;
var props = this.props;
var multiple = (0, _util.isMultipleOrTags)(props);
var state = this.state;
var className = props.className;
var disabled = props.disabled;
var allowClear = props.allowClear;
var prefixCls = props.prefixCls;
var ctrlNode = this.renderTopControlNode();
var extraSelectionProps = {};
if (!(0, _util.isCombobox)(props)) {
extraSelectionProps = {
onKeyDown: this.onKeyDown,
tabIndex: 0
};
}
var rootCls = (_rootCls = {}, _defineProperty(_rootCls, className, !!className), _defineProperty(_rootCls, prefixCls, 1), _defineProperty(_rootCls, prefixCls + '-open', state.open), _defineProperty(_rootCls, prefixCls + '-combobox', (0, _util.isCombobox)(props)), _defineProperty(_rootCls, prefixCls + '-disabled', disabled), _defineProperty(_rootCls, prefixCls + '-enabled', !disabled), _rootCls);
var clear = _react2['default'].createElement('span', { key: 'clear',
className: prefixCls + '-selection__clear',
onClick: this.onClearSelection });
return _react2['default'].createElement(
_SelectTrigger2['default'],
_extends({}, props, {
treeNodes: props.children,
multiple: multiple,
disabled: disabled,
visible: state.open,
inputValue: state.inputValue,
inputElement: this.getInputElement(),
value: state.value,
onDropdownVisibleChange: this.onDropdownVisibleChange,
onSelect: this.onSelect,
ref: 'trigger' }),
_react2['default'].createElement(
'span',
{
style: props.style,
onClick: props.onClick,
className: (0, _classnames2['default'])(rootCls) },
_react2['default'].createElement(
'span',
_extends({ ref: 'selection',
key: 'selection',
className: prefixCls + '-selection ' + prefixCls + '-selection--' + (multiple ? 'multiple' : 'single'),
role: 'combobox',
'aria-autocomplete': 'list',
'aria-haspopup': 'true',
'aria-expanded': state.open
}, extraSelectionProps),
ctrlNode,
allowClear && !(0, _util.isMultipleOrTags)(props) ? clear : null,
multiple || !props.showArrow ? null : _react2['default'].createElement(
'span',
{ key: 'arrow', className: prefixCls + '-arrow', tabIndex: '-1', style: { outline: 'none' } },
_react2['default'].createElement('b', null)
),
multiple ? this.getSearchPlaceholderElement(!!this.state.inputValue || this.state.value.length) : null
)
)
);
}
});
exports['default'] = Select;
module.exports = exports['default'];