diff --git a/components/table/Table.jsx b/components/table/Table.jsx new file mode 100755 index 000000000..0832a6245 --- /dev/null +++ b/components/table/Table.jsx @@ -0,0 +1,991 @@ + +import VcTable from '../vc-table' +import classNames from 'classnames' +import Pagination from '../pagination' +import Icon from '../icon' +import Spin from '../spin' +import LocaleReceiver from '../locale-provider/LocaleReceiver' +import defaultLocale from '../locale-provider/default' +import warning from '../_util/warning' +import FilterDropdown from './filterDropdown' +import createStore from './createStore' +import SelectionBox from './SelectionBox' +import SelectionCheckboxAll from './SelectionCheckboxAll' +import Column from './Column' +import ColumnGroup from './ColumnGroup' +import createBodyRow from './createBodyRow' +import { flatArray, treeMap, flatFilter } from './util' +import { initDefaultProps, mergeProps, getOptionProps } from '../_util/props-util' +import { + TableProps, +} from './interface' + +function noop () { +} + +function stopPropagation (e) { + e.stopPropagation() + if (e.nativeEvent.stopImmediatePropagation) { + e.nativeEvent.stopImmediatePropagation() + } +} + +const defaultPagination = { + on: { + change: noop, + showSizeChange: noop, + }, +} + +/** + * Avoid creating new object, so that parent component's shouldComponentUpdate + * can works appropriately。 + */ +const emptyObject = {} + +export default { + name: 'Table', + Column, + ColumnGroup, + + props: initDefaultProps(TableProps, { + dataSource: [], + prefixCls: 'ant-table', + useFixedHeader: false, + rowSelection: null, + size: 'large', + loading: false, + bordered: false, + indentSize: 20, + locale: {}, + rowKey: 'key', + showHeader: true, + }), + + // CheckboxPropsCache: { + // [key: string]: any; + // }; + // store: Store; + // columns: ColumnProps[]; + // components: TableComponents; + + data () { + // this.columns = props.columns || normalizeColumns(props.children) + + this.createComponents(this.components) + this.CheckboxPropsCache = {} + + this.store = createStore({ + selectedRowKeys: (this.rowSelection || {}).selectedRowKeys || [], + selectionDirty: false, + }) + return { + ...this.getDefaultSortOrder(this.columns), + // 减少状态 + sFilters: this.getFiltersFromColumns(), + sPagination: this.getDefaultPagination(this.$props), + } + }, + watch: { + pagination (val) { + this.setState(previousState => { + const newPagination = mergeProps( + defaultPagination, + previousState.sPagination, + val, + ) + newPagination.props = newPagination.props || {} + newPagination.props.current = newPagination.props.current || 1 + newPagination.props.pageSize = newPagination.props.pageSize || 10 + return { sPagination: val !== false ? newPagination : emptyObject } + }) + }, + rowSelection: { + handler: (val) => { + if (val && + 'selectedRowKeys' in val) { + this.store.setState({ + selectedRowKeys: val.selectedRowKeys || [], + }) + const { rowSelection } = this.props + if (rowSelection && ( + val.getCheckboxProps !== rowSelection.getCheckboxProps + )) { + this.CheckboxPropsCache = {} + } + } + }, + deep: true, + }, + dataSource (val) { + this.store.setState({ + selectionDirty: false, + }) + this.CheckboxPropsCache = {} + }, + columns (val) { + if (this.getSortOrderColumns(val).length > 0) { + const sortState = this.getSortStateFromColumns(val) + if (sortState.sSortColumn !== this.sSortColumn || + sortState.sSortOrder !== this.sSortOrder) { + this.setState(sortState) + } + } + + const filteredValueColumns = this.getFilteredValueColumns(val) + if (filteredValueColumns.length > 0) { + const filtersFromColumns = this.getFiltersFromColumns(val) + const newFilters = { ...this.state.filters } + Object.keys(filtersFromColumns).forEach(key => { + newFilters[key] = filtersFromColumns[key] + }) + if (this.isFiltersChanged(newFilters)) { + this.setState({ filters: newFilters }) + } + } + }, + components (val, preVal) { + this.createComponents(val, preVal) + }, + }, + + getCheckboxPropsByItem (item, index) { + const { rowSelection = {}} = this + if (!rowSelection.getCheckboxProps) { + return {} + } + const key = this.getRecordKey(item, index) + // Cache checkboxProps + if (!this.CheckboxPropsCache[key]) { + this.CheckboxPropsCache[key] = rowSelection.getCheckboxProps(item) + } + return this.CheckboxPropsCache[key] + }, + + getDefaultSelection () { + const { rowSelection = {}} = this + if (!rowSelection.getCheckboxProps) { + return [] + } + return this.getFlatData() + .filter((item, rowIndex) => this.getCheckboxPropsByItem(item, rowIndex).defaultChecked) + .map((record, rowIndex) => this.getRecordKey(record, rowIndex)) + }, + + getDefaultPagination (props) { + const pagination = props.pagination || {} + pagination.props = pagination.props || {} + return this.hasPagination(props) + ? mergeProps( + defaultPagination, + pagination, + { + props: { + current: pagination.props.defaultCurrent || pagination.props.current || 1, + pageSize: pagination.props.defaultPageSize || pagination.props.pageSize || 10, + }, + }) : {} + }, + + onRow (record, index) { + const { prefixCls, customRow } = this + const custom = customRow ? customRow(record, index) : {} + return mergeProps(custom, { + props: { + prefixCls, + store: this.store, + rowKey: this.getRecordKey(record, index), + }, + }) + }, + + setSelectedRowKeys (selectedRowKeys, { selectWay, record, checked, changeRowKeys }) { + const { rowSelection = {}, $listeners: { + rowSelectionChange, + rowSelectionSelect, + rowSelectionSelectAll, + rowSelectionSelectInvert, + }} = this + if (rowSelection && !('selectedRowKeys' in rowSelection)) { + this.store.setState({ selectedRowKeys }) + } + const data = this.getFlatData() + if (!rowSelectionChange && !rowSelection[selectWay]) { + return + } + const selectedRows = data.filter( + (row, i) => selectedRowKeys.indexOf(this.getRecordKey(row, i)) >= 0, + ) + this.$emit('rowSelectionChange', selectedRowKeys, selectedRows) + + if (selectWay === 'onSelect' && rowSelectionSelect) { + this.$emit('rowSelectionSelect', record, checked, selectedRows) + } else if (selectWay === 'onSelectAll' && rowSelectionSelectAll) { + const changeRows = data.filter( + (row, i) => changeRowKeys.indexOf(this.getRecordKey(row, i)) >= 0, + ) + this.$emit('rowSelectionSelectAll', checked, selectedRows, changeRows) + } else if (selectWay === 'onSelectInvert' && rowSelectionSelectInvert) { + this.$emit('rowSelectionSelectInvert', selectedRowKeys) + } + }, + + hasPagination () { + return this.pagination !== false + }, + + isFiltersChanged (filters) { + let filtersChanged = false + if (Object.keys(filters).length !== Object.keys(this.sFilters).length) { + filtersChanged = true + } else { + Object.keys(filters).forEach(columnKey => { + if (filters[columnKey] !== this.sFilters[columnKey]) { + filtersChanged = true + } + }) + } + return filtersChanged + }, + + getSortOrderColumns (columns) { + return flatFilter( + columns || this.columns || [], + (column) => 'sortOrder' in column, + ) + }, + + getFilteredValueColumns (columns) { + return flatFilter( + columns || this.columns || [], + (column) => typeof column.filteredValue !== 'undefined', + ) + }, + + getFiltersFromColumns (columns) { + const filters = {} + this.getFilteredValueColumns(columns).forEach((col) => { + const colKey = this.getColumnKey(col) + filters[colKey] = col.filteredValue + }) + return filters + }, + + getDefaultSortOrder (columns) { + const definedSortState = this.getSortStateFromColumns(columns) + + const defaultSortedColumn = flatFilter(columns || [], (column) => column.defaultSortOrder != null)[0] + + if (defaultSortedColumn && !definedSortState.sortColumn) { + return { + sSortColumn: defaultSortedColumn, + sSortOrder: defaultSortedColumn.defaultSortOrder, + } + } + + return definedSortState + }, + + getSortStateFromColumns (columns) { + // return first column which sortOrder is not falsy + const sortedColumn = this.getSortOrderColumns(columns).filter((col) => col.sortOrder)[0] + + if (sortedColumn) { + return { + sSortColumn: sortedColumn, + sSortOrder: sortedColumn.sortOrder, + } + } + + return { + sSortColumn: null, + sSortOrder: null, + } + }, + + getSorterFn () { + const { sSortOrder: sortOrder, sSortColumn: sortColumn } = this + if (!sortOrder || !sortColumn || + typeof sortColumn.sorter !== 'function') { + return + } + + return (a, b) => { + const result = sortColumn.sorter(a, b) + if (result !== 0) { + return (sortOrder === 'descend') ? -result : result + } + return 0 + } + }, + + toggleSortOrder (order, column) { + let { sSortOrder: sortOrder, sSortColumn: sortColumn } = this + // 只同时允许一列进行排序,否则会导致排序顺序的逻辑问题 + const isSortColumn = this.isSortColumn(column) + if (!isSortColumn) { // 当前列未排序 + sortOrder = order + sortColumn = column + } else { // 当前列已排序 + if (sortOrder === order) { // 切换为未排序状态 + sortOrder = '' + sortColumn = null + } else { // 切换为排序状态 + sortOrder = order + } + } + const newState = { + sSortOrder: sortOrder, + sSortColumn: sortColumn, + } + + // Controlled + if (this.getSortOrderColumns().length === 0) { + this.setState(newState) + } + this.$emit('change', this.prepareParamsArguments({ + ...this.$data, + ...newState, + })) + }, + + handleFilter (column, nextFilters) { + const props = this.$props + const pagination = { ...this.sPagination } + const filters = { + ...this.sFilters, + [this.getColumnKey(column)]: nextFilters, + } + // Remove filters not in current columns + const currentColumnKeys = [] + treeMap(this.columns, c => { + if (!c.children) { + currentColumnKeys.push(this.getColumnKey(c)) + } + }) + Object.keys(filters).forEach((columnKey) => { + if (currentColumnKeys.indexOf(columnKey) < 0) { + delete filters[columnKey] + } + }) + + if (props.pagination) { + // Reset current prop + pagination.props = pagination.props || {} + pagination.props.current = 1 + pagination.on.change(pagination.current) + } + + const newState = { + sPagination: pagination, + sFilters: {}, + } + const filtersToSetState = { ...filters } + // Remove filters which is controlled + this.getFilteredValueColumns().forEach((col) => { + const columnKey = this.getColumnKey(col) + if (columnKey) { + delete filtersToSetState[columnKey] + } + }) + if (Object.keys(filtersToSetState).length > 0) { + newState.sFilters = filtersToSetState + } + + // Controlled current prop will not respond user interaction + if (typeof props.pagination === 'object' && props.pagination.props && 'current' in (props.pagination.props)) { + newState.sPagination = mergeProps(pagination, { + props: { + current: this.sPagination.props.current, + }, + }) + } + + this.setState(newState, () => { + this.store.setState({ + selectionDirty: false, + }) + this.$emit('change', this.prepareParamsArguments({ + ...this.$data, + selectionDirty: false, + filters, + pagination, + })) + }) + }, + + handleSelect (record, rowIndex, e) { + const checked = e.target.checked + const defaultSelection = this.store.getState().selectionDirty ? [] : this.getDefaultSelection() + let selectedRowKeys = this.store.getState().selectedRowKeys.concat(defaultSelection) + const key = this.getRecordKey(record, rowIndex) + if (checked) { + selectedRowKeys.push(this.getRecordKey(record, rowIndex)) + } else { + selectedRowKeys = selectedRowKeys.filter((i) => key !== i) + } + this.store.setState({ + selectionDirty: true, + }) + this.setSelectedRowKeys(selectedRowKeys, { + selectWay: 'onSelect', + record, + checked, + }) + }, + + handleRadioSelect (record, rowIndex, e) { + const checked = e.target.checked + const defaultSelection = this.store.getState().selectionDirty ? [] : this.getDefaultSelection() + let selectedRowKeys = this.store.getState().selectedRowKeys.concat(defaultSelection) + const key = this.getRecordKey(record, rowIndex) + selectedRowKeys = [key] + this.store.setState({ + selectionDirty: true, + }) + this.setSelectedRowKeys(selectedRowKeys, { + selectWay: 'onSelect', + record, + checked, + }) + }, + + handleSelectRow (selectionKey, index, onSelectFunc) { + const data = this.getFlatCurrentPageData() + const defaultSelection = this.store.getState().selectionDirty ? [] : this.getDefaultSelection() + const selectedRowKeys = this.store.getState().selectedRowKeys.concat(defaultSelection) + const changeableRowKeys = data + .filter((item, i) => !this.getCheckboxPropsByItem(item, i).disabled) + .map((item, i) => this.getRecordKey(item, i)) + + const changeRowKeys = [] + let selectWay = '' + let checked + // handle default selection + switch (selectionKey) { + case 'all': + changeableRowKeys.forEach(key => { + if (selectedRowKeys.indexOf(key) < 0) { + selectedRowKeys.push(key) + changeRowKeys.push(key) + } + }) + selectWay = 'onSelectAll' + checked = true + break + case 'removeAll': + changeableRowKeys.forEach(key => { + if (selectedRowKeys.indexOf(key) >= 0) { + selectedRowKeys.splice(selectedRowKeys.indexOf(key), 1) + changeRowKeys.push(key) + } + }) + selectWay = 'onSelectAll' + checked = false + break + case 'invert': + changeableRowKeys.forEach(key => { + if (selectedRowKeys.indexOf(key) < 0) { + selectedRowKeys.push(key) + } else { + selectedRowKeys.splice(selectedRowKeys.indexOf(key), 1) + } + changeRowKeys.push(key) + selectWay = 'onSelectInvert' + }) + break + default: + break + } + + this.store.setState({ + selectionDirty: true, + }) + // when select custom selection, callback selections[n].onSelect + const { rowSelection } = this + let customSelectionStartIndex = 2 + if (rowSelection && rowSelection.hideDefaultSelections) { + customSelectionStartIndex = 0 + } + if (index >= customSelectionStartIndex && typeof onSelectFunc === 'function') { + return onSelectFunc(changeableRowKeys) + } + this.setSelectedRowKeys(selectedRowKeys, { + selectWay: selectWay, + checked, + changeRowKeys, + }) + }, + + handlePageChange (current, ...otherArguments) { + const props = this.$props + const pagination = { ...this.sPagination } + if (current) { + pagination.props.current = current + } else { + pagination.props.current = pagination.props.current || 1 + } + pagination.on.change(pagination.props.current, ...otherArguments) + + const newState = { + sPagination: pagination, + } + // Controlled current prop will not respond user interaction + if (props.pagination && + typeof props.pagination === 'object' && + props.pagination.props && + 'current' in (props.pagination.props)) { + newState.sPagination = mergeProps(pagination, { + props: { + current: this.sPagination.props.current, + }, + }) + } + this.setState(newState) + + this.store.setState({ + selectionDirty: false, + }) + this.$emit('change', this.prepareParamsArguments({ + ...this.$data, + selectionDirty: false, + pagination, + })) + }, + + renderSelectionBox (type) { + return (_, record, index) => { + const rowIndex = this.getRecordKey(record, index) // 从 1 开始 + const props = this.getCheckboxPropsByItem(record, index) + const handleChange = (e) => { + type === 'radio' ? this.handleRadioSelect(record, rowIndex, e) + : this.handleSelect(record, rowIndex, e) + } + const selectionBoxProps = mergeProps({ + props: { + type, + store: this.store, + rowIndex, + defaultSelection: this.getDefaultSelection(), + }, + on: { + change: handleChange, + }, + }, props) + + return ( + + + + ) + } + }, + + getRecordKey (record, index) { + const rowKey = this.rowKey + const recordKey = (typeof rowKey === 'function') + ? rowKey(record, index) : record[rowKey] + warning(recordKey !== undefined, + 'Each record in dataSource of table should have a unique `key` prop, or set `rowKey` to an unique primary key,', + ) + return recordKey === undefined ? index : recordKey + }, + + getPopupContainer () { + return this.$el + }, + + renderRowSelection (locale) { + const { prefixCls, rowSelection } = this + const columns = this.columns.concat() + if (rowSelection) { + const data = this.getFlatCurrentPageData().filter((item, index) => { + if (rowSelection.getCheckboxProps) { + return !this.getCheckboxPropsByItem(item, index).disabled + } + return true + }) + const selectionColumnClass = classNames(`${prefixCls}-selection-column`, { + [`${prefixCls}-selection-column-custom`]: rowSelection.selections, + }) + const selectionColumn = { + key: 'selection-column', + render: this.renderSelectionBox(rowSelection.type), + className: selectionColumnClass, + fixed: rowSelection.fixed, + } + if (rowSelection.type !== 'radio') { + const checkboxAllDisabled = data.every((item, index) => this.getCheckboxPropsByItem(item, index).disabled) + selectionColumn.title = ( + + ) + } + if ('fixed' in rowSelection) { + selectionColumn.fixed = rowSelection.fixed + } else if (columns.some(column => column.fixed === 'left' || column.fixed === true)) { + selectionColumn.fixed = 'left' + } + if (columns[0] && columns[0].key === 'selection-column') { + columns[0] = selectionColumn + } else { + columns.unshift(selectionColumn) + } + } + return columns + }, + + getColumnKey (column, index) { + return column.key || column.dataIndex || index + }, + + getMaxCurrent (total) { + const { current, pageSize } = this.sPagination + if ((current - 1) * pageSize >= total) { + return Math.floor((total - 1) / pageSize) + 1 + } + return current + }, + + isSortColumn (column) { + const { sSortColumn: sortColumn } = this + if (!column || !sortColumn) { + return false + } + return this.getColumnKey(sortColumn) === this.getColumnKey(column) + }, + + renderColumnsDropdown (columns, locale) { + const { prefixCls, dropdownPrefixCls } = this + const { sSortOrder: sortOrder } = this + return treeMap(columns, (originColumn, i) => { + const column = { ...originColumn } + const key = this.getColumnKey(column, i) + let filterDropdown + let sortButton + if ((column.filters && column.filters.length > 0) || column.filterDropdown) { + const colFilters = this.sFilters[key] || [] + filterDropdown = ( + + ) + } + if (column.sorter) { + const isSortColumn = this.isSortColumn(column) + if (isSortColumn) { + column.className = classNames(column.className, { + [`${prefixCls}-column-sort`]: sortOrder, + }) + } + const isAscend = isSortColumn && sortOrder === 'ascend' + const isDescend = isSortColumn && sortOrder === 'descend' + sortButton = ( +
+ this.toggleSortOrder('ascend', column)} + > + + + this.toggleSortOrder('descend', column)} + > + + +
+ ) + } + column.title = ( + + {column.title} + {sortButton} + {filterDropdown} + + ) + + if (sortButton || filterDropdown) { + column.className = classNames(`${prefixCls}-column-has-filters`, column.className) + } + + return column + }) + }, + + handleShowSizeChange (current, pageSize) { + const pagination = this.sPagination + pagination.on.showSizeChange(current, pageSize) + const nextPagination = mergeProps(pagination, { + props: { + pageSize, + current, + }, + }) + this.setState({ sPagination: nextPagination }) + this.$emit('change', this.prepareParamsArguments({ + ...this.$data, + pagination: nextPagination, + })) + }, + + renderPagination () { + // 强制不需要分页 + if (!this.hasPagination()) { + return null + } + let size = 'default' + const { sPagination: pagination } = this + if (pagination.props && pagination.props.size) { + size = pagination.props.size + } else if (this.size === 'middle' || this.size === 'small') { + size = 'small' + } + const total = (pagination.props && pagination.props.total) || this.getLocalData().length + const { class: cls, style, on, props } = pagination + const paginationProps = mergeProps({ + key: 'pagination', + class: classNames(cls, `${this.prefixCls}-pagination`), + props: { + ...props, + total, + size, + current: this.getMaxCurrent(total), + }, + style, + on: { + ...on, + change: this.handlePageChange, + showSizeChange: this.handleShowSizeChange, + }, + }) + return (total > 0) ? ( + + ) : null + }, + + // Get pagination, filters, sorter + prepareParamsArguments (state) { + const pagination = { ...state.sPagination } + // remove useless handle function in Table.onChange + if (pagination.on) { + delete pagination.on.change + delete pagination.on.showSizeChange + } + const filters = state.sFilters + const sorter = {} + if (state.sSortColumn && state.sSortOrder) { + sorter.column = state.sSortColumn + sorter.order = state.sSortOrder + sorter.field = state.sSortColumn.dataIndex + sorter.columnKey = this.getColumnKey(state.sSortColumn) + } + return [pagination, filters, sorter] + }, + + findColumn (myKey) { + let column + treeMap(this.columns, c => { + if (this.getColumnKey(c) === myKey) { + column = c + } + }) + return column + }, + + getCurrentPageData () { + let data = this.getLocalData() + let current + let pageSize + // 如果没有分页的话,默认全部展示 + if (!this.hasPagination()) { + pageSize = Number.MAX_VALUE + current = 1 + } else { + pageSize = this.sPagination.pageSize + current = this.getMaxCurrent(this.sPagination.total || data.length) + } + + // 分页 + // --- + // 当数据量少于等于每页数量时,直接设置数据 + // 否则进行读取分页数据 + if (data.length > pageSize || pageSize === Number.MAX_VALUE) { + data = data.filter((_, i) => { + return i >= (current - 1) * pageSize && i < current * pageSize + }) + } + return data + }, + + getFlatData () { + return flatArray(this.getLocalData()) + }, + + getFlatCurrentPageData () { + return flatArray(this.getCurrentPageData()) + }, + + recursiveSort (data, sorterFn) { + const { childrenColumnName = 'children' } = this + return data.sort(sorterFn).map((item) => (item[childrenColumnName] ? { + ...item, + [childrenColumnName]: this.recursiveSort(item[childrenColumnName], sorterFn), + } : item)) + }, + + getLocalData () { + const { dataSource, sFilters: filters } = this + let data = dataSource || [] + // 优化本地排序 + data = data.slice(0) + const sorterFn = this.getSorterFn() + if (sorterFn) { + data = this.recursiveSort(data, sorterFn) + } + // 筛选 + if (filters) { + Object.keys(filters).forEach((columnKey) => { + const col = this.findColumn(columnKey) + if (!col) { + return + } + const values = filters[columnKey] || [] + if (values.length === 0) { + return + } + const onFilter = col.onFilter + data = onFilter ? data.filter(record => { + return values.some(v => onFilter(v, record)) + }) : data + }) + } + return data + }, + + createComponents (components, prevComponents) { + const bodyRow = components && components.body && components.body.row + const preBodyRow = prevComponents && prevComponents.body && prevComponents.body.row + if (!this.components || bodyRow !== preBodyRow) { + this.components = { ...components } + this.components.body = { + ...components.body, + row: createBodyRow(bodyRow), + } + } + }, + + renderTable (contextLocale, loading) { + const locale = { ...contextLocale, ...this.locale } + const { prefixCls, showHeader, ...restProps } = getOptionProps(this) + const data = this.getCurrentPageData() + const expandIconAsCell = this.expandedRowRender && this.expandIconAsCell !== false + + const classString = classNames({ + [`${prefixCls}-${this.size}`]: true, + [`${prefixCls}-bordered`]: this.bordered, + [`${prefixCls}-empty`]: !data.length, + [`${prefixCls}-without-column-header`]: !showHeader, + }) + + let columns = this.renderRowSelection(locale) + columns = this.renderColumnsDropdown(columns, locale) + columns = columns.map((column, i) => { + const newColumn = { ...column } + newColumn.key = this.getColumnKey(newColumn, i) + return newColumn + }) + let expandIconColumnIndex = (columns[0] && columns[0].key === 'selection-column') ? 1 : 0 + if ('expandIconColumnIndex' in restProps) { + expandIconColumnIndex = restProps.expandIconColumnIndex + } + const vcTableProps = { + key: 'table', + props: { + ...restProps, + customRow: this.customRow, + components: this.components, + prefixCls, + data, + columns, + showHeader, + expandIconColumnIndex, + expandIconAsCell, + emptyText: !(loading.props && loading.spinning) && locale.emptyText, + }, + on: this.$listeners, + class: classString, + } + return ( + + ) + }, + + render () { + const { prefixCls } = this + const data = this.getCurrentPageData() + + let loading = this.loading + if (typeof loading === 'boolean') { + loading = { + props: { + spinning: loading, + }, + } + } + + const table = ( + this.renderTable(locale, loading)} + /> + ) + + // if there is no pagination or no data, + // the height of spin should decrease by half of pagination + const paginationPatchClass = (this.hasPagination() && data && data.length !== 0) + ? `${prefixCls}-with-pagination` : `${prefixCls}-without-pagination` + const spinProps = { + ...loading, + class: loading.props && loading.props.spinning ? `${paginationPatchClass} ${prefixCls}-spin-holder` : '', + } + return ( +
+ + {table} + {this.renderPagination()} + +
+ ) + }, +} diff --git a/components/table/Table.tsx b/components/table/Table.tsx deleted file mode 100755 index c01b116e3..000000000 --- a/components/table/Table.tsx +++ /dev/null @@ -1,1002 +0,0 @@ -import * as React from 'react'; -import * as ReactDOM from 'react-dom'; -import RcTable from 'rc-table'; -import PropTypes from 'prop-types'; -import classNames from 'classnames'; -import Pagination, { PaginationProps } from '../pagination'; -import Icon from '../icon'; -import Spin from '../spin'; -import LocaleReceiver from '../locale-provider/LocaleReceiver'; -import defaultLocale from '../locale-provider/default'; -import warning from '../_util/warning'; -import FilterDropdown from './filterDropdown'; -import createStore, { Store } from './createStore'; -import SelectionBox from './SelectionBox'; -import SelectionCheckboxAll from './SelectionCheckboxAll'; -import Column from './Column'; -import ColumnGroup from './ColumnGroup'; -import createBodyRow from './createBodyRow'; -import { flatArray, treeMap, flatFilter, normalizeColumns } from './util'; -import { SpinProps } from '../spin'; -import { - TableProps, - TableState, - TableComponents, - RowSelectionType, - TableLocale, - ColumnProps, - CompareFn, - TableStateFilters, - SelectionItemSelectFn, -} from './interface'; - -function noop() { -} - -function stopPropagation(e: React.SyntheticEvent) { - e.stopPropagation(); - if (e.nativeEvent.stopImmediatePropagation) { - e.nativeEvent.stopImmediatePropagation(); - } -} - -const defaultPagination = { - onChange: noop, - onShowSizeChange: noop, -}; - -/** - * Avoid creating new object, so that parent component's shouldComponentUpdate - * can works appropriately。 - */ -const emptyObject = {}; - -export default class Table extends React.Component, TableState> { - static Column = Column; - static ColumnGroup = ColumnGroup; - - static propTypes = { - dataSource: PropTypes.array, - columns: PropTypes.array, - prefixCls: PropTypes.string, - useFixedHeader: PropTypes.bool, - rowSelection: PropTypes.object, - className: PropTypes.string, - size: PropTypes.string, - loading: PropTypes.oneOfType([ - PropTypes.bool, - PropTypes.object, - ]), - bordered: PropTypes.bool, - onChange: PropTypes.func, - locale: PropTypes.object, - dropdownPrefixCls: PropTypes.string, - }; - - static defaultProps = { - dataSource: [], - prefixCls: 'ant-table', - useFixedHeader: false, - rowSelection: null, - className: '', - size: 'large', - loading: false, - bordered: false, - indentSize: 20, - locale: {}, - rowKey: 'key', - showHeader: true, - }; - - CheckboxPropsCache: { - [key: string]: any; - }; - store: Store; - columns: ColumnProps[]; - components: TableComponents; - - constructor(props: TableProps) { - super(props); - - warning( - !('columnsPageRange' in props || 'columnsPageSize' in props), - '`columnsPageRange` and `columnsPageSize` are removed, please use ' + - 'fixed columns instead, see: https://u.ant.design/fixed-columns.', - ); - - this.columns = props.columns || normalizeColumns(props.children as React.ReactChildren); - - this.createComponents(props.components); - - this.state = { - ...this.getDefaultSortOrder(this.columns), - // 减少状态 - filters: this.getFiltersFromColumns(), - pagination: this.getDefaultPagination(props), - }; - - this.CheckboxPropsCache = {}; - - this.store = createStore({ - selectedRowKeys: (props.rowSelection || {}).selectedRowKeys || [], - selectionDirty: false, - }); - } - - getCheckboxPropsByItem = (item: T, index: number) => { - const { rowSelection = {} } = this.props; - if (!rowSelection.getCheckboxProps) { - return {}; - } - const key = this.getRecordKey(item, index); - // Cache checkboxProps - if (!this.CheckboxPropsCache[key]) { - this.CheckboxPropsCache[key] = rowSelection.getCheckboxProps(item); - } - return this.CheckboxPropsCache[key]; - } - - getDefaultSelection() { - const { rowSelection = {} } = this.props; - if (!rowSelection.getCheckboxProps) { - return []; - } - return this.getFlatData() - .filter((item: T, rowIndex) => this.getCheckboxPropsByItem(item, rowIndex).defaultChecked) - .map((record, rowIndex) => this.getRecordKey(record, rowIndex)); - } - - getDefaultPagination(props: TableProps) { - const pagination: PaginationProps = props.pagination || {}; - return this.hasPagination(props) ? - { - ...defaultPagination, - ...pagination, - current: pagination.defaultCurrent || pagination.current || 1, - pageSize: pagination.defaultPageSize || pagination.pageSize || 10, - } : {}; - } - - componentWillReceiveProps(nextProps: TableProps) { - this.columns = nextProps.columns || normalizeColumns(nextProps.children as React.ReactChildren); - if ('pagination' in nextProps || 'pagination' in this.props) { - this.setState(previousState => { - const newPagination = { - ...defaultPagination, - ...previousState.pagination, - ...nextProps.pagination, - }; - newPagination.current = newPagination.current || 1; - newPagination.pageSize = newPagination.pageSize || 10; - return { pagination: nextProps.pagination !== false ? newPagination : emptyObject }; - }); - } - if (nextProps.rowSelection && - 'selectedRowKeys' in nextProps.rowSelection) { - this.store.setState({ - selectedRowKeys: nextProps.rowSelection.selectedRowKeys || [], - }); - const { rowSelection } = this.props; - if (rowSelection && ( - nextProps.rowSelection.getCheckboxProps !== rowSelection.getCheckboxProps - )) { - this.CheckboxPropsCache = {}; - } - } - if ('dataSource' in nextProps && - nextProps.dataSource !== this.props.dataSource) { - this.store.setState({ - selectionDirty: false, - }); - this.CheckboxPropsCache = {}; - } - - if (this.getSortOrderColumns(this.columns).length > 0) { - const sortState = this.getSortStateFromColumns(this.columns); - if (sortState.sortColumn !== this.state.sortColumn || - sortState.sortOrder !== this.state.sortOrder) { - this.setState(sortState); - } - } - - const filteredValueColumns = this.getFilteredValueColumns(this.columns); - if (filteredValueColumns.length > 0) { - const filtersFromColumns = this.getFiltersFromColumns(this.columns); - const newFilters = { ...this.state.filters }; - Object.keys(filtersFromColumns).forEach(key => { - newFilters[key] = filtersFromColumns[key]; - }); - if (this.isFiltersChanged(newFilters)) { - this.setState({ filters: newFilters }); - } - } - - this.createComponents(nextProps.components, this.props.components); - } - - onRow = (record: T, index: number) => { - const { onRow, prefixCls } = this.props; - const custom = onRow ? onRow(record, index) : {}; - return { - ...custom, - prefixCls, - store: this.store, - rowKey: this.getRecordKey(record, index), - }; - } - - setSelectedRowKeys(selectedRowKeys: string[], { selectWay, record, checked, changeRowKeys }: any) { - const { rowSelection = {} as any } = this.props; - if (rowSelection && !('selectedRowKeys' in rowSelection)) { - this.store.setState({ selectedRowKeys }); - } - const data = this.getFlatData(); - if (!rowSelection.onChange && !rowSelection[selectWay]) { - return; - } - const selectedRows = data.filter( - (row, i) => selectedRowKeys.indexOf(this.getRecordKey(row, i)) >= 0, - ); - if (rowSelection.onChange) { - rowSelection.onChange(selectedRowKeys, selectedRows); - } - if (selectWay === 'onSelect' && rowSelection.onSelect) { - rowSelection.onSelect(record, checked, selectedRows); - } else if (selectWay === 'onSelectAll' && rowSelection.onSelectAll) { - const changeRows = data.filter( - (row, i) => changeRowKeys.indexOf(this.getRecordKey(row, i)) >= 0, - ); - rowSelection.onSelectAll(checked, selectedRows, changeRows); - } else if (selectWay === 'onSelectInvert' && rowSelection.onSelectInvert) { - rowSelection.onSelectInvert(selectedRowKeys); - } - } - - hasPagination(props?: any) { - return (props || this.props).pagination !== false; - } - - isFiltersChanged(filters: TableStateFilters) { - let filtersChanged = false; - if (Object.keys(filters).length !== Object.keys(this.state.filters).length) { - filtersChanged = true; - } else { - Object.keys(filters).forEach(columnKey => { - if (filters[columnKey] !== this.state.filters[columnKey]) { - filtersChanged = true; - } - }); - } - return filtersChanged; - } - - getSortOrderColumns(columns?: ColumnProps[]) { - return flatFilter( - columns || this.columns || [], - (column: ColumnProps) => 'sortOrder' in column, - ); - } - - getFilteredValueColumns(columns?: ColumnProps[]) { - return flatFilter( - columns || this.columns || [], - (column: ColumnProps) => typeof column.filteredValue !== 'undefined', - ); - } - - getFiltersFromColumns(columns?: ColumnProps[]) { - let filters: any = {}; - this.getFilteredValueColumns(columns).forEach((col: ColumnProps) => { - const colKey = this.getColumnKey(col) as string; - filters[colKey] = col.filteredValue; - }); - return filters; - } - - getDefaultSortOrder(columns?: ColumnProps[]) { - const definedSortState = this.getSortStateFromColumns(columns); - - let defaultSortedColumn = flatFilter(columns || [], (column: ColumnProps) => column.defaultSortOrder != null)[0]; - - if (defaultSortedColumn && !definedSortState.sortColumn) { - return { - sortColumn: defaultSortedColumn, - sortOrder: defaultSortedColumn.defaultSortOrder, - }; - } - - return definedSortState; - } - - getSortStateFromColumns(columns?: ColumnProps[]) { - // return first column which sortOrder is not falsy - const sortedColumn = - this.getSortOrderColumns(columns).filter((col: ColumnProps) => col.sortOrder)[0]; - - if (sortedColumn) { - return { - sortColumn: sortedColumn, - sortOrder: sortedColumn.sortOrder, - }; - } - - return { - sortColumn: null, - sortOrder: null, - }; - } - - getSorterFn() { - const { sortOrder, sortColumn } = this.state; - if (!sortOrder || !sortColumn || - typeof sortColumn.sorter !== 'function') { - return; - } - - return (a: T, b: T) => { - const result = (sortColumn!.sorter as CompareFn)(a, b); - if (result !== 0) { - return (sortOrder === 'descend') ? -result : result; - } - return 0; - }; - } - - toggleSortOrder(order: string, column: ColumnProps) { - let { sortColumn, sortOrder } = this.state; - // 只同时允许一列进行排序,否则会导致排序顺序的逻辑问题 - let isSortColumn = this.isSortColumn(column); - if (!isSortColumn) { // 当前列未排序 - sortOrder = order; - sortColumn = column; - } else { // 当前列已排序 - if (sortOrder === order) { // 切换为未排序状态 - sortOrder = ''; - sortColumn = null; - } else { // 切换为排序状态 - sortOrder = order; - } - } - const newState = { - sortOrder, - sortColumn, - }; - - // Controlled - if (this.getSortOrderColumns().length === 0) { - this.setState(newState); - } - - const onChange = this.props.onChange; - if (onChange) { - onChange.apply(null, this.prepareParamsArguments({ - ...this.state, - ...newState, - })); - } - } - - handleFilter = (column: ColumnProps, nextFilters: string[]) => { - const props = this.props; - let pagination = { ...this.state.pagination }; - const filters = { - ...this.state.filters, - [this.getColumnKey(column) as string]: nextFilters, - }; - // Remove filters not in current columns - const currentColumnKeys: string[] = []; - treeMap(this.columns, c => { - if (!c.children) { - currentColumnKeys.push(this.getColumnKey(c) as string); - } - }); - Object.keys(filters).forEach((columnKey) => { - if (currentColumnKeys.indexOf(columnKey) < 0) { - delete filters[columnKey]; - } - }); - - if (props.pagination) { - // Reset current prop - pagination.current = 1; - pagination.onChange!(pagination.current); - } - - const newState = { - pagination, - filters: {}, - }; - const filtersToSetState = { ...filters }; - // Remove filters which is controlled - this.getFilteredValueColumns().forEach((col: ColumnProps) => { - const columnKey = this.getColumnKey(col); - if (columnKey) { - delete filtersToSetState[columnKey]; - } - }); - if (Object.keys(filtersToSetState).length > 0) { - newState.filters = filtersToSetState; - } - - // Controlled current prop will not respond user interaction - if (typeof props.pagination === 'object' && 'current' in (props.pagination as Object)) { - newState.pagination = { - ...pagination, - current: this.state.pagination.current, - }; - } - - this.setState(newState, () => { - this.store.setState({ - selectionDirty: false, - }); - const onChange = this.props.onChange; - if (onChange) { - onChange.apply(null, this.prepareParamsArguments({ - ...this.state, - selectionDirty: false, - filters, - pagination, - })); - } - }); - } - - handleSelect = (record: T, rowIndex: number, e: React.ChangeEvent) => { - const checked = e.target.checked; - const defaultSelection = this.store.getState().selectionDirty ? [] : this.getDefaultSelection(); - let selectedRowKeys = this.store.getState().selectedRowKeys.concat(defaultSelection); - let key = this.getRecordKey(record, rowIndex); - if (checked) { - selectedRowKeys.push(this.getRecordKey(record, rowIndex)); - } else { - selectedRowKeys = selectedRowKeys.filter((i: string) => key !== i); - } - this.store.setState({ - selectionDirty: true, - }); - this.setSelectedRowKeys(selectedRowKeys, { - selectWay: 'onSelect', - record, - checked, - }); - } - - handleRadioSelect = (record: T, rowIndex: number, e: React.ChangeEvent) => { - const checked = e.target.checked; - const defaultSelection = this.store.getState().selectionDirty ? [] : this.getDefaultSelection(); - let selectedRowKeys = this.store.getState().selectedRowKeys.concat(defaultSelection); - let key = this.getRecordKey(record, rowIndex); - selectedRowKeys = [key]; - this.store.setState({ - selectionDirty: true, - }); - this.setSelectedRowKeys(selectedRowKeys, { - selectWay: 'onSelect', - record, - checked, - }); - } - - handleSelectRow = (selectionKey: string, index: number, onSelectFunc: SelectionItemSelectFn) => { - const data = this.getFlatCurrentPageData(); - const defaultSelection = this.store.getState().selectionDirty ? [] : this.getDefaultSelection(); - const selectedRowKeys = this.store.getState().selectedRowKeys.concat(defaultSelection); - const changeableRowKeys = data - .filter((item, i) => !this.getCheckboxPropsByItem(item, i).disabled) - .map((item, i) => this.getRecordKey(item, i)); - - let changeRowKeys: string[] = []; - let selectWay = ''; - let checked; - // handle default selection - switch (selectionKey) { - case 'all': - changeableRowKeys.forEach(key => { - if (selectedRowKeys.indexOf(key) < 0) { - selectedRowKeys.push(key); - changeRowKeys.push(key); - } - }); - selectWay = 'onSelectAll'; - checked = true; - break; - case 'removeAll': - changeableRowKeys.forEach(key => { - if (selectedRowKeys.indexOf(key) >= 0) { - selectedRowKeys.splice(selectedRowKeys.indexOf(key), 1); - changeRowKeys.push(key); - } - }); - selectWay = 'onSelectAll'; - checked = false; - break; - case 'invert': - changeableRowKeys.forEach(key => { - if (selectedRowKeys.indexOf(key) < 0) { - selectedRowKeys.push(key); - } else { - selectedRowKeys.splice(selectedRowKeys.indexOf(key), 1); - } - changeRowKeys.push(key); - selectWay = 'onSelectInvert'; - }); - break; - default: - break; - } - - this.store.setState({ - selectionDirty: true, - }); - // when select custom selection, callback selections[n].onSelect - const { rowSelection } = this.props; - let customSelectionStartIndex = 2; - if (rowSelection && rowSelection.hideDefaultSelections) { - customSelectionStartIndex = 0; - } - if (index >= customSelectionStartIndex && typeof onSelectFunc === 'function') { - return onSelectFunc(changeableRowKeys); - } - this.setSelectedRowKeys(selectedRowKeys, { - selectWay: selectWay, - checked, - changeRowKeys, - }); - } - - handlePageChange = (current: number, ...otherArguments: any[]) => { - const props = this.props; - let pagination = { ...this.state.pagination }; - if (current) { - pagination.current = current; - } else { - pagination.current = pagination.current || 1; - } - pagination.onChange!(pagination.current, ...otherArguments); - - const newState = { - pagination, - }; - // Controlled current prop will not respond user interaction - if (props.pagination && - typeof props.pagination === 'object' && - 'current' in (props.pagination as Object)) { - newState.pagination = { - ...pagination, - current: this.state.pagination.current, - }; - } - this.setState(newState); - - this.store.setState({ - selectionDirty: false, - }); - - const onChange = this.props.onChange; - if (onChange) { - onChange.apply(null, this.prepareParamsArguments({ - ...this.state, - selectionDirty: false, - pagination, - })); - } - } - - renderSelectionBox = (type: RowSelectionType | undefined) => { - return (_: any, record: T, index: number) => { - let rowIndex = this.getRecordKey(record, index); // 从 1 开始 - const props = this.getCheckboxPropsByItem(record, index); - const handleChange = (e: React.ChangeEvent) => { - type === 'radio' ? this.handleRadioSelect(record, rowIndex, e) : - this.handleSelect(record, rowIndex, e); - }; - - return ( - - - - ); - }; - } - - getRecordKey = (record: T, index: number) => { - const rowKey = this.props.rowKey; - const recordKey = (typeof rowKey === 'function') ? - rowKey(record, index) : (record as any)[rowKey as string]; - warning(recordKey !== undefined, - 'Each record in dataSource of table should have a unique `key` prop, or set `rowKey` to an unique primary key,' + - 'see https://u.ant.design/table-row-key', - ); - return recordKey === undefined ? index : recordKey; - } - - getPopupContainer = () => { - return ReactDOM.findDOMNode(this) as HTMLElement; - } - - renderRowSelection(locale: TableLocale) { - const { prefixCls, rowSelection } = this.props; - const columns = this.columns.concat(); - if (rowSelection) { - const data = this.getFlatCurrentPageData().filter((item, index) => { - if (rowSelection.getCheckboxProps) { - return !this.getCheckboxPropsByItem(item, index).disabled; - } - return true; - }); - let selectionColumnClass = classNames(`${prefixCls}-selection-column`, { - [`${prefixCls}-selection-column-custom`]: rowSelection.selections, - }); - const selectionColumn: ColumnProps = { - key: 'selection-column', - render: this.renderSelectionBox(rowSelection.type), - className: selectionColumnClass, - fixed: rowSelection.fixed, - }; - if (rowSelection.type !== 'radio') { - const checkboxAllDisabled = data.every((item, index) => this.getCheckboxPropsByItem(item, index).disabled); - selectionColumn.title = ( - - ); - } - if ('fixed' in rowSelection) { - selectionColumn.fixed = rowSelection.fixed; - } else if (columns.some(column => column.fixed === 'left' || column.fixed === true)) { - selectionColumn.fixed = 'left'; - } - if (columns[0] && columns[0].key === 'selection-column') { - columns[0] = selectionColumn; - } else { - columns.unshift(selectionColumn); - } - } - return columns; - } - - getColumnKey(column: ColumnProps, index?: number) { - return column.key || column.dataIndex || index; - } - - getMaxCurrent(total: number) { - const { current, pageSize } = this.state.pagination; - if ((current! - 1) * pageSize! >= total) { - return Math.floor((total - 1) / pageSize!) + 1; - } - return current; - } - - isSortColumn(column: ColumnProps) { - const { sortColumn } = this.state; - if (!column || !sortColumn) { - return false; - } - return this.getColumnKey(sortColumn) === this.getColumnKey(column); - } - - renderColumnsDropdown(columns: ColumnProps[], locale: TableLocale) { - const { prefixCls, dropdownPrefixCls } = this.props; - const { sortOrder } = this.state; - return treeMap(columns, (originColumn, i) => { - let column = { ...originColumn }; - let key = this.getColumnKey(column, i) as string; - let filterDropdown; - let sortButton; - if ((column.filters && column.filters.length > 0) || column.filterDropdown) { - let colFilters = this.state.filters[key] || []; - filterDropdown = ( - - ); - } - if (column.sorter) { - let isSortColumn = this.isSortColumn(column); - if (isSortColumn) { - column.className = classNames(column.className, { - [`${prefixCls}-column-sort`]: sortOrder, - }); - } - const isAscend = isSortColumn && sortOrder === 'ascend'; - const isDescend = isSortColumn && sortOrder === 'descend'; - sortButton = ( -
- this.toggleSortOrder('ascend', column)} - > - - - this.toggleSortOrder('descend', column)} - > - - -
- ); - } - column.title = ( - - {column.title} - {sortButton} - {filterDropdown} - - ); - - if (sortButton || filterDropdown) { - column.className = classNames(`${prefixCls}-column-has-filters`, column.className); - } - - return column; - }); - } - - handleShowSizeChange = (current: number, pageSize: number) => { - const pagination = this.state.pagination; - pagination.onShowSizeChange!(current, pageSize); - const nextPagination = { - ...pagination, - pageSize, - current, - }; - this.setState({ pagination: nextPagination }); - - const onChange = this.props.onChange; - if (onChange) { - onChange.apply(null, this.prepareParamsArguments({ - ...this.state, - pagination: nextPagination, - })); - } - } - - renderPagination() { - // 强制不需要分页 - if (!this.hasPagination()) { - return null; - } - let size = 'default'; - const { pagination } = this.state; - if (pagination.size) { - size = pagination.size; - } else if (this.props.size as string === 'middle' || this.props.size === 'small') { - size = 'small'; - } - let total = pagination.total || this.getLocalData().length; - return (total > 0) ? ( - - ) : null; - } - - // Get pagination, filters, sorter - prepareParamsArguments(state: any): [any, string[], Object] { - const pagination = { ...state.pagination }; - // remove useless handle function in Table.onChange - delete pagination.onChange; - delete pagination.onShowSizeChange; - const filters = state.filters; - const sorter: any = {}; - if (state.sortColumn && state.sortOrder) { - sorter.column = state.sortColumn; - sorter.order = state.sortOrder; - sorter.field = state.sortColumn.dataIndex; - sorter.columnKey = this.getColumnKey(state.sortColumn); - } - return [pagination, filters, sorter]; - } - - findColumn(myKey: string | number) { - let column; - treeMap(this.columns, c => { - if (this.getColumnKey(c) === myKey) { - column = c; - } - }); - return column; - } - - getCurrentPageData() { - let data = this.getLocalData(); - let current: number; - let pageSize: number; - let state = this.state; - // 如果没有分页的话,默认全部展示 - if (!this.hasPagination()) { - pageSize = Number.MAX_VALUE; - current = 1; - } else { - pageSize = state.pagination.pageSize as number; - current = this.getMaxCurrent(state.pagination.total || data.length) as number; - } - - // 分页 - // --- - // 当数据量少于等于每页数量时,直接设置数据 - // 否则进行读取分页数据 - if (data.length > pageSize || pageSize === Number.MAX_VALUE) { - data = data.filter((_, i) => { - return i >= (current - 1) * pageSize && i < current * pageSize; - }); - } - return data; - } - - getFlatData() { - return flatArray(this.getLocalData()); - } - - getFlatCurrentPageData() { - return flatArray(this.getCurrentPageData()); - } - - recursiveSort(data: T[], sorterFn: (a: any, b: any) => number): T[] { - const { childrenColumnName = 'children' } = this.props; - return data.sort(sorterFn).map((item: any) => (item[childrenColumnName] ? { - ...item, - [childrenColumnName]: this.recursiveSort(item[childrenColumnName], sorterFn), - } : item)); - } - - getLocalData() { - const state = this.state; - const { dataSource } = this.props; - let data = dataSource || []; - // 优化本地排序 - data = data.slice(0); - const sorterFn = this.getSorterFn(); - if (sorterFn) { - data = this.recursiveSort(data, sorterFn); - } - // 筛选 - if (state.filters) { - Object.keys(state.filters).forEach((columnKey) => { - let col = this.findColumn(columnKey) as any; - if (!col) { - return; - } - let values = state.filters[columnKey] || []; - if (values.length === 0) { - return; - } - const onFilter = col.onFilter; - data = onFilter ? data.filter(record => { - return values.some(v => onFilter(v, record)); - }) : data; - }); - } - return data; - } - - createComponents(components: TableComponents = {}, prevComponents?: TableComponents) { - const bodyRow = components && components.body && components.body.row; - const preBodyRow = prevComponents && prevComponents.body && prevComponents.body.row; - if (!this.components || bodyRow !== preBodyRow) { - this.components = { ...components }; - this.components.body = { - ...components.body, - row: createBodyRow(bodyRow), - }; - } - } - - renderTable = (contextLocale: TableLocale, loading: SpinProps) => { - const locale = { ...contextLocale, ...this.props.locale }; - const { style, className, prefixCls, showHeader, ...restProps } = this.props; - const data = this.getCurrentPageData(); - const expandIconAsCell = this.props.expandedRowRender && this.props.expandIconAsCell !== false; - - const classString = classNames({ - [`${prefixCls}-${this.props.size}`]: true, - [`${prefixCls}-bordered`]: this.props.bordered, - [`${prefixCls}-empty`]: !data.length, - [`${prefixCls}-without-column-header`]: !showHeader, - }); - - let columns = this.renderRowSelection(locale); - columns = this.renderColumnsDropdown(columns, locale); - columns = columns.map((column, i) => { - const newColumn = { ...column }; - newColumn.key = this.getColumnKey(newColumn, i); - return newColumn; - }); - let expandIconColumnIndex = (columns[0] && columns[0].key === 'selection-column') ? 1 : 0; - if ('expandIconColumnIndex' in restProps) { - expandIconColumnIndex = restProps.expandIconColumnIndex as number; - } - - return ( - - ); - } - - render() { - const { style, className, prefixCls } = this.props; - const data = this.getCurrentPageData(); - - let loading = this.props.loading as SpinProps; - if (typeof loading === 'boolean') { - loading = { - spinning: loading, - }; - } - - const table = ( - - {(locale) => this.renderTable(locale, loading)} - - ); - - // if there is no pagination or no data, - // the height of spin should decrease by half of pagination - const paginationPatchClass = (this.hasPagination() && data && data.length !== 0) - ? `${prefixCls}-with-pagination` : `${prefixCls}-without-pagination`; - - return ( -
- - {table} - {this.renderPagination()} - -
- ); - } -} diff --git a/components/table/interface.js b/components/table/interface.js index 707897ad0..bea5f786f 100644 --- a/components/table/interface.js +++ b/components/table/interface.js @@ -28,7 +28,7 @@ export const ColumnProps = { defaultSortOrder: PropTypes.oneOf(['ascend', 'descend']), colSpan: PropTypes.number, width: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), - // className: string, + className: PropTypes.string, fixed: PropTypes.oneOfType([PropTypes.bool, PropTypes.oneOf(['left', 'right'])]), filterIcon: PropTypes.any, filteredValue: PropTypes.array, @@ -103,7 +103,8 @@ export const TableProps = { locale: PropTypes.object, indentSize: PropTypes.number, // onRowClick?: (record: T, index: number, event: Event) => any; - // onRow?: (record: T, index: number) => any; + customRow: PropTypes.func, + customHeaderRow: PropTypes.func, useFixedHeader: PropTypes.bool, bordered: PropTypes.bool, showHeader: PropTypes.bool, diff --git a/components/vc-table/demo/column-resize.js b/components/vc-table/demo/column-resize.js index b5a95d37a..04fbf6026 100644 --- a/components/vc-table/demo/column-resize.js +++ b/components/vc-table/demo/column-resize.js @@ -59,7 +59,7 @@ export default { render () { const columns = this.columns.map((col, index) => ({ ...col, - onHeaderCell: (column) => ({ + customHeaderCell: (column) => ({ width: column.width, onResize: this.handleResize(index), }), diff --git a/components/vc-table/demo/rowAndCellClick.js b/components/vc-table/demo/rowAndCellClick.js index fd8878b88..dfe244527 100644 --- a/components/vc-table/demo/rowAndCellClick.js +++ b/components/vc-table/demo/rowAndCellClick.js @@ -10,8 +10,8 @@ const onRowClick = (record, index, event) => { } } -const onRowDoubleClick = (record, index) => { - console.log(`Double click nth(${index}) row of parent, record.name: ${record.name}`) +const onRowDoubleClick = (record, index, e) => { + console.log(`Double click nth(${index}) row of parent, record.name: ${record.name}`, e) } const columns = [{ @@ -98,7 +98,7 @@ export default { ({ + customRow={(record, index) => ({ on: { click: onRowClick.bind(null, record, index), dblclick: onRowDoubleClick.bind(null, record, index), diff --git a/components/vc-table/src/BaseTable.jsx b/components/vc-table/src/BaseTable.jsx index 5095223e4..a5b18002a 100644 --- a/components/vc-table/src/BaseTable.jsx +++ b/components/vc-table/src/BaseTable.jsx @@ -46,8 +46,8 @@ const BaseTable = { rowContextmenu: onRowContextMenu = noop, rowMouseenter: onRowMouseEnter = noop, rowMouseleave: onRowMouseLeave = noop, - row: onRow = noop, }, + customRow = noop, } = this.table const { getRowKey, fixed, expander, isAnyColumnsFixed } = this @@ -106,9 +106,9 @@ const BaseTable = { ancestorKeys, components, isAnyColumnsFixed, + customRow, }, on: { - row: onRow, rowDoubleclick: onRowDoubleClick, rowContextmenu: onRowContextMenu, rowMouseenter: onRowMouseEnter, diff --git a/components/vc-table/src/Column.jsx b/components/vc-table/src/Column.jsx index 8ad12648c..1b7022130 100644 --- a/components/vc-table/src/Column.jsx +++ b/components/vc-table/src/Column.jsx @@ -16,8 +16,9 @@ export default { 'right', ]), render: PropTypes.func, + className: PropTypes.string, // onCellClick: PropTypes.func, // onCell: PropTypes.func, - // onHeaderCell: PropTypes.func, + customHeaderCell: PropTypes.func, }, } diff --git a/components/vc-table/src/Table.jsx b/components/vc-table/src/Table.jsx index b20b7f453..0f7651d9e 100644 --- a/components/vc-table/src/Table.jsx +++ b/components/vc-table/src/Table.jsx @@ -25,8 +25,8 @@ export default { bodyStyle: PropTypes.object, rowKey: PropTypes.oneOfType([PropTypes.string, PropTypes.func]), rowClassName: PropTypes.oneOfType([PropTypes.string, PropTypes.func]), - // onRow: PropTypes.func, - // onHeaderRow: PropTypes.func, + customRow: PropTypes.func, + customHeaderRow: PropTypes.func, // onRowClick: PropTypes.func, // onRowDoubleClick: PropTypes.func, // onRowContextMenu: PropTypes.func, @@ -74,6 +74,7 @@ export default { scroll: {}, rowRef: () => null, emptyText: () => 'No Data', + customHeaderRow: () => {}, }), // static childContextTypes = { @@ -91,7 +92,7 @@ export default { ].forEach(name => { warningOnce( this.$listeners[name] === undefined, - `${name} is deprecated, please use onRow instead.`, + `${name} is deprecated, please use customRow instead.`, ) }) diff --git a/components/vc-table/src/TableCell.jsx b/components/vc-table/src/TableCell.jsx index 73662cf18..831a8dac2 100644 --- a/components/vc-table/src/TableCell.jsx +++ b/components/vc-table/src/TableCell.jsx @@ -41,7 +41,7 @@ export default { component: BodyCell, } = this const { dataIndex, render, className = '' } = column - const cls = column.class || className + const cls = className || column.class // We should return undefined if no dataIndex is specified, but in order to // be compatible with object-path's behavior, we return the record object instead. let text diff --git a/components/vc-table/src/TableHeader.jsx b/components/vc-table/src/TableHeader.jsx index 9e6271b8e..2a1471158 100644 --- a/components/vc-table/src/TableHeader.jsx +++ b/components/vc-table/src/TableHeader.jsx @@ -13,7 +13,7 @@ function getHeaderRows (columns, currentRow = 0, rows) { } const cell = { key: column.key, - className: column.className || '', + className: column.className || column.class || '', children: column.title, column, } @@ -44,15 +44,10 @@ export default { inject: { table: { default: {}}, }, - methods: { - onHeaderRow () { - this.table.__emit('headerRow', ...arguments) - }, - }, render () { - const { sComponents: components, prefixCls, showHeader } = this.table - const { expander, columns, fixed, onHeaderRow } = this + const { sComponents: components, prefixCls, showHeader, customHeaderRow } = this.table + const { expander, columns, fixed } = this if (!showHeader) { return null @@ -76,7 +71,7 @@ export default { rows={rows} row={row} components={components} - onHeaderRow={onHeaderRow} + customHeaderRow={customHeaderRow} /> )) } diff --git a/components/vc-table/src/TableHeaderRow.jsx b/components/vc-table/src/TableHeaderRow.jsx index bfc0f538f..618bae948 100644 --- a/components/vc-table/src/TableHeaderRow.jsx +++ b/components/vc-table/src/TableHeaderRow.jsx @@ -11,14 +11,14 @@ const TableHeaderRow = { row: PropTypes.array, components: PropTypes.object, height: PropTypes.any, + customHeaderRow: PropTypes.func, }, name: 'TableHeaderRow', render (h) { - const { row, index, height, components, $listeners = {}} = this - const onHeaderRow = $listeners.headerRow + const { row, index, height, components, customHeaderRow } = this const HeaderRow = components.header.row const HeaderCell = components.header.cell - const rowProps = onHeaderRow(row.map(cell => cell.column), index) + const rowProps = customHeaderRow(row.map(cell => cell.column), index) const customStyle = rowProps ? rowProps.style : {} const style = { height, ...customStyle } @@ -27,7 +27,7 @@ const TableHeaderRow = { {row.map((cell, i) => { const { column, children, className, ...cellProps } = cell const cls = cell.class || className - const customProps = column.onHeaderCell ? column.onHeaderCell(column) : {} + const customProps = column.customHeaderCell ? column.customHeaderCell(column) : {} if (column.align) { cellProps.style = { textAlign: column.align } } diff --git a/components/vc-table/src/TableRow.jsx b/components/vc-table/src/TableRow.jsx index 3ccf1c67e..5ee9e8265 100644 --- a/components/vc-table/src/TableRow.jsx +++ b/components/vc-table/src/TableRow.jsx @@ -9,7 +9,7 @@ const TableRow = { name: 'TableRow', mixins: [BaseMixin], props: initDefaultProps({ - // onRow: PropTypes.func, + customRow: PropTypes.func, // onRowClick: PropTypes.func, // onRowDoubleClick: PropTypes.func, // onRowContextMenu: PropTypes.func, @@ -175,7 +175,7 @@ const TableRow = { columns, record, index, - // onRow, + customRow = noop, indent, indentSize, hovered, @@ -185,9 +185,7 @@ const TableRow = { hasExpandIcon, renderExpandIcon, renderExpandIconCell, - $listeners, } = this - const { row: onRow = noop } = $listeners const BodyRow = components.body.row const BodyCell = components.body.cell @@ -227,7 +225,7 @@ const TableRow = { const rowClassName = `${prefixCls} ${className} ${prefixCls}-level-${indent}`.trim() - const rowProps = onRow(record, index) + const rowProps = customRow(record, index) const customStyle = rowProps ? rowProps.style : {} let style = { height: typeof height === 'number' ? `${height}px` : height }