import React, { Children } from 'react'; import PropTypes from 'prop-types'; import Checkbox from '../checkbox'; import Radio from '../radio'; import { func, log } from '../util'; import zhCN from '../locale/zh-cn'; import SelectionRow from './selection/row'; import Col from './column'; import { statics } from './util'; const { makeChain } = func; const unique = (arr, key = 'this') => { const temp = {}; const ret = []; arr.forEach(item => { let value; if (key === 'this') { value = item; } else { value = item[key]; } if (!temp[value]) { ret.push(item); temp[value] = true; } }); return ret; }; export default function selection(BaseComponent) { /** Table */ class SelectionTable extends React.Component { static SelectionRow = SelectionRow; static propTypes = { /** * 是否启用选择模式 * @property {Function} getProps `Function(record, index)=>Object` 获取selection的默认属性 * @property {Function} onChange `Function(selectedRowKeys:Array, records:Array)` 选择改变的时候触发的事件,**注意:** 其中records只会包含当前dataSource的数据,很可能会小于selectedRowKeys的长度。 * @property {Function} onSelect `Function(selected:Boolean, record:Object, records:Array)` 用户手动选择/取消选择某行的回调 * @property {Function} onSelectAll `Function(selected:Boolean, records:Array)` 用户手动选择/取消选择所有行的回调 * @property {Array} selectedRowKeys 设置了此属性,将rowSelection变为受控状态,接收值为该行数据的primaryKey的值 * @property {String} mode 选择selection的模式, 可选值为`single`, `multiple`,默认为`multiple` * @property {Function} columnProps `Function()=>Object` 选择列 的props,例如锁列、对齐等,可使用`Table.Column` 的所有参数 * @property {Function} titleProps `Function()=>Object` 选择列 表头的props,仅在 `multiple` 模式下生效 */ rowSelection: PropTypes.object, primaryKey: PropTypes.string, dataSource: PropTypes.array, entireDataSource: PropTypes.array, ...BaseComponent.propTypes, }; static defaultProps = { ...BaseComponent.defaultProps, locale: zhCN.Table, primaryKey: 'id', prefix: 'next-', }; static contextTypes = { listHeader: PropTypes.any, }; static childContextTypes = { rowSelection: PropTypes.object, selectedRowKeys: PropTypes.array, }; constructor(props, context) { super(props, context); this.state = { selectedRowKeys: props.rowSelection && 'selectedRowKeys' in props.rowSelection ? props.rowSelection.selectedRowKeys || [] : [], }; } getChildContext() { return { rowSelection: this.props.rowSelection, selectedRowKeys: this.state.selectedRowKeys, }; } componentWillReceiveProps(nextProps) { if ( nextProps.rowSelection && 'selectedRowKeys' in nextProps.rowSelection ) { const selectedRowKeys = nextProps.rowSelection.selectedRowKeys || []; this.setState({ selectedRowKeys, }); } } normalizeChildren(children) { const { prefix, rowSelection, size } = this.props; if (rowSelection) { children = Children.map(children, (child, index) => React.cloneElement(child, { key: index, })); const attrs = (rowSelection.columnProps && rowSelection.columnProps()) || {}; children.unshift( , ); return children; } return children; } renderSelectionHeader = () => { const onChange = this.selectAllRow; const attrs = {}; const { rowSelection, primaryKey, dataSource, entireDataSource, locale, } = this.props; const { selectedRowKeys } = this.state; const mode = rowSelection.mode ? rowSelection.mode : 'multiple'; let checked = !!selectedRowKeys.length; let indeterminate = false; const source = entireDataSource || dataSource; this.flatDataSource(source) .filter((record, index) => { if (!rowSelection.getProps) { return true; } else { return !(rowSelection.getProps(record, index) || {}) .disabled; } }) .map(record => record[primaryKey]) .forEach(id => { if (selectedRowKeys.indexOf(id) === -1) { checked = false; } else { indeterminate = true; } }); attrs.onClick = makeChain(e => { e.stopPropagation(); }, attrs.onClick); const userAttrs = (rowSelection.titleProps && rowSelection.titleProps()) || {}; if (checked) { indeterminate = false; } return [ mode === 'multiple' ? ( ) : null, rowSelection.titleAddons && rowSelection.titleAddons(), ]; }; renderSelectionBody = (value, index, record) => { const { rowSelection, primaryKey } = this.props; const { selectedRowKeys } = this.state; const mode = rowSelection.mode ? rowSelection.mode : 'multiple'; const checked = selectedRowKeys.indexOf(record[primaryKey]) > -1; const onChange = this.selectOneRow.bind(this, index, record); const attrs = rowSelection.getProps ? rowSelection.getProps(record, index) || {} : {}; attrs.onClick = makeChain(e => { e.stopPropagation(); }, attrs.onClick); return mode === 'multiple' ? ( ) : ( ); }; selectAllRow = (checked, e) => { const ret = [...this.state.selectedRowKeys]; const { rowSelection, primaryKey, dataSource, entireDataSource, } = this.props; const { selectedRowKeys } = this.state; const { getProps } = rowSelection; let attrs = {}; let records = []; const source = entireDataSource || dataSource; this.flatDataSource(source).forEach((record, index) => { const id = record[primaryKey]; if (getProps) { attrs = getProps(record, index) || {}; } // 反选和全选的时候不要丢弃禁用项的选中状态 if ( checked && (!attrs.disabled || selectedRowKeys.indexOf(id) > -1) ) { ret.push(id); records.push(record); } else if (attrs.disabled && selectedRowKeys.indexOf(id) > -1) { ret.push(id); records.push(record); } else { const i = ret.indexOf(id); i > -1 && ret.splice(i, 1); } }); records = unique(records, primaryKey); if (typeof rowSelection.onSelectAll === 'function') { rowSelection.onSelectAll(checked, records); } this.triggerSelection(rowSelection, unique(ret), records); e.stopPropagation(); }; selectOneRow(index, record, checked, e) { let selectedRowKeys = [...this.state.selectedRowKeys]; let i; const { primaryKey, rowSelection, dataSource } = this.props; const mode = rowSelection.mode ? rowSelection.mode : 'multiple'; const id = record[primaryKey]; if (!id) { log.warning( `Can't get value from record using given ${primaryKey} as primaryKey.`, ); } if (mode === 'multiple') { if (checked) { selectedRowKeys.push(id); } else { i = selectedRowKeys.indexOf(id); selectedRowKeys.splice(i, 1); } } else if (checked) { selectedRowKeys = [id]; } const records = unique( dataSource.filter( item => selectedRowKeys.indexOf(item[primaryKey]) > -1, ), primaryKey, ); if (typeof rowSelection.onSelect === 'function') { rowSelection.onSelect(checked, record, records); } this.triggerSelection(rowSelection, selectedRowKeys, records); e.stopPropagation(); } triggerSelection(rowSelection, selectedRowKeys, records) { if (!('selectedRowKeys' in rowSelection)) { this.setState({ selectedRowKeys, }); } if (typeof rowSelection.onChange === 'function') { rowSelection.onChange(selectedRowKeys, records); } } flatDataSource(dataSource) { let ret = dataSource; const { listHeader } = this.context; if (listHeader) { ret = []; const { hasChildrenSelection, hasSelection } = listHeader; dataSource.forEach(item => { const { children } = item; // 如果需要渲染selection才将这条记录插入到dataSource // 或者没有孩子节点 if (hasSelection) { ret.push(item); } if (children && hasChildrenSelection) { ret = ret.concat(children); } }); } return ret; } render() { /* eslint-disable prefer-const */ let { rowSelection, components, children, ...others } = this.props; if (rowSelection) { children = this.normalizeChildren(children); components = { ...components }; components.Row = components.Row || SelectionRow; } return ( {children} ); } } statics(SelectionTable, BaseComponent); return SelectionTable; }