fit-select
Version:
选择框
295 lines (293 loc) • 11 kB
JSX
"use strict";
const React = require('react');
const ReactDOM = require('react-dom');
const classNames = require('classnames');
const $ = require('jquery');
const _ = require('lodash');
const module = require('./module');
const src_1 = require('../../../input/src');
const src_2 = require('../../../../common/transmit-transparently/src');
const option_1 = require('../option');
const opt_group_1 = require('../opt-group');
require('./index.scss');
class Select extends React.Component {
constructor(props) {
super(props);
this.state = new module.State();
}
componentWillMount() {
this.setState({
value: this.props.value !== '' ? this.props.value : this.props.defaultValue
});
// 点击document
this.handleDocumentClick = (event) => {
if (!this._isMounted)
return;
if (!$.contains(this.dom, event.target)) {
this.setState({
open: false
});
}
};
}
componentWillReceiveProps(nextProps) {
if ('value' in nextProps && nextProps.value !== null) {
this.setState({
value: nextProps.value
});
}
}
componentDidMount() {
this._isMounted = true;
this.dom = ReactDOM.findDOMNode(this);
$(document).on('click', this.handleDocumentClick);
}
componentWillUnmount() {
this._isMounted = false;
$(document).off('click', this.handleDocumentClick);
}
// 选择框点击
handleSelectClick() {
this.setState({
open: !this.state.open
}, () => {
if (this.state.open) {
$(this.dom).find('#j-search').focus();
}
});
}
// 选择栏目点击
handleClick(value, label, children, zIndex = 1) {
// 如果没有 children,说明是最后一级了
if (!children) {
let newValue = this.state.value;
let newCascader = this.state.cascader;
// 将此级后的所有级联元素删除
let deleteEndNumber = newCascader.length - zIndex + 1;
while (deleteEndNumber > 0) {
newCascader.pop();
deleteEndNumber = deleteEndNumber - 1;
}
if (zIndex === 1) {
// 如果是第一级级联,才设置 state.value
newValue = value;
this.firstLabelValue = label;
}
else {
// 否则设置级联元素对应项的value
newCascader[zIndex - 2].value = value;
newCascader[zIndex - 2].labelValue = label;
}
this.setState({
cascader: newCascader
});
// 如果级联显示完整路径,修改 labelValue 的值
let labelValue = label;
if (this.props.cascaderFull) {
const labelValueArray = [this.firstLabelValue];
this.state.cascader.forEach(item => {
labelValueArray.push(item.labelValue);
});
labelValue = labelValueArray.join(' / ');
}
this.setState({
open: false,
value: newValue,
labelValue: labelValue
}, () => {
if (this.props.cascaderFull) {
// 级联显示完整路径
const pathArray = [this.state.value];
this.state.cascader.forEach(item => {
pathArray.push(item.value);
});
this.props.onChange(pathArray);
}
else {
this.props.onChange(value);
}
});
}
else {
// 有 children,说明还有级联, zIndex 表示级联层级,最外层是 1,那么第一层级联就是 2
let newCascader = this.state.cascader;
if (zIndex === 1) {
// 如果是第一级级联,设置 state.value
this.firstLabelValue = label;
this.setState({
value: value
});
}
else {
// 否则设置级联元素对应项的value
let newCascader = this.state.cascader;
newCascader[zIndex - 2].value = value;
newCascader[zIndex - 2].labelValue = label;
this.setState({
cascader: newCascader
});
}
// 因为点击选项,但后面还有,因此没点完,将labelValue设置为空
this.setState({
labelValue: ''
});
// 在级联后追加
if (newCascader.length = zIndex - 1) {
newCascader.push({
value: '',
options: children
});
}
else {
// 改写已有级联,删除后面的数组
newCascader[zIndex - 1] = {
value: value,
options: children
};
// 先有级联层级比当前 zIndex 大多少,全都 pop 掉
let deleteEndNumber = newCascader.length - zIndex;
while (deleteEndNumber > 0) {
newCascader.pop();
deleteEndNumber = deleteEndNumber - 1;
}
}
this.setState({
cascader: newCascader
});
}
}
// 搜索框改变
handleSearchChange(event) {
this.setState({
searchValue: event.target.value
});
}
/**
* 设置初始化labelValue
*/
handleSetLabelValue(labelValue) {
this.setState({
labelValue: labelValue
});
}
getOptionChildren() {
let chosenDropStyle = {
display: this.state.open ? null : 'none',
left: 0
};
// 循环子元素,同时获取value,同时判断search
let Children = React.Children.map(this.props['children'], (item, index) => {
let active = false;
if (item.props.value === this.state.value) {
active = true;
}
if (_.isArray(item.props.children)) {
item.props.children.map((childItem) => {
if (childItem.props.value === this.state.value) {
active = true;
}
});
}
return React.cloneElement(item, {
onClick: this.handleClick.bind(this),
key: index,
active: active,
setLabelValue: this.handleSetLabelValue.bind(this),
activeValue: this.state.value,
searchValue: this.state.searchValue
});
});
// 搜索框
let Search = null;
if (this.props.search) {
Search = (<div className="chosen-search">
<src_1.default id="j-search" className="search-input" label="" placeholder="搜索.." autoComplete="off" onChange={this.handleSearchChange.bind(this)}/>
</div>);
}
return (<div id="j-chosen" className="chosen-drop" style={chosenDropStyle}>
{Search}
<ul className="chosen-results">
{Children}
</ul>
</div>);
}
getOptionChildrenByOptions() {
let chosenDropStyle = {
display: this.state.open ? null : 'none',
left: 0
};
let OptionChildren = this.props.options.map((item, index) => {
return this.getOptionItemByType(item, index, this.state.value, 1);
});
// 追加渲染级联元素
const CascaderChildrens = this.state.cascader.map((item, index) => {
const options = item.options.map((childrenItem, childrenItemIndex) => {
return this.getOptionItemByType(childrenItem, childrenItemIndex, item.value, index + 2);
});
return (<ul key={index} className="chosen-results">
{options}
</ul>);
});
return (<div id="j-chosen" className="chosen-drop" style={chosenDropStyle}>
<div className="flex-option-container">
<ul className="chosen-results">
{OptionChildren}
</ul>
{CascaderChildrens}
</div>
</div>);
}
/**
* 根据一个 Option 元素类型返回对应ReactElement
*/
getOptionItemByType(item, key, activeValue, zIndex = 1) {
if (item.groupValue) {
// 是一个分组
const GroupChildren = item.children.map((item, index) => {
return this.getOptionItemByType(item, index, activeValue, zIndex);
});
return (<opt_group_1.default key={key} ignoreChildren={true} label={item.groupValue}>{GroupChildren}</opt_group_1.default>);
}
// option 元素
let active = false;
if (item.key === activeValue) {
active = true;
}
return (<option_1.default key={key} value={item.key} onClick={this.handleClick.bind(this)} active={active} zIndex={zIndex} optChildren={item.children} setLabelValue={this.handleSetLabelValue.bind(this)} activeValue={this.state.value} searchValue={this.state.searchValue}>{item.value}</option_1.default>);
}
dropIconRender() {
const classes = classNames({
'open': this.state.open,
'fit-select-drop': true
});
return <i className={classes}/>;
}
render() {
const classes = classNames({
'_namespace': true,
[this.props['className']]: !!this.props['className'],
'simple': this.props.simple
});
let renderChosen;
if (this.props.options.length === 0) {
renderChosen = this.getOptionChildren.call(this);
}
else {
renderChosen = this.getOptionChildrenByOptions.call(this);
}
let extProps = {};
// 给精简模式用的额外字段
if (this.props.simple) {
extProps.label = '';
extProps.placeholder = '';
}
// 给搜索模式用的额外字段
if (this.props.search) {
extProps.highlightLine = false;
}
return (<src_1.default {...src_2.others(new module.Props(), this.props, ['children'])} {...extProps} onClick={this.handleSelectClick.bind(this)} className={classes} value={this.state.labelValue} rightRender={this.dropIconRender.bind(this)} innerRender={renderChosen}/>);
}
}
Select.defaultProps = new module.Props();
Object.defineProperty(exports, "__esModule", { value: true });
exports.default = Select;