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;
}