diff --git a/components/style/themes/default.less b/components/style/themes/default.less index b33fc28389..561c83e0c6 100644 --- a/components/style/themes/default.less +++ b/components/style/themes/default.less @@ -319,6 +319,7 @@ @table-header-bg: @background-color-light; @table-header-sort-bg: @background-color-base; @table-row-hover-bg: @primary-1; +@table-selected-row-bg: #fafafa; @table-padding-vertical: 16px; @table-padding-horizontal: 16px; diff --git a/components/table/Table.tsx b/components/table/Table.tsx index 354b403565..dd02d2c070 100755 --- a/components/table/Table.tsx +++ b/components/table/Table.tsx @@ -15,6 +15,7 @@ import SelectionBox from './SelectionBox'; import SelectionCheckboxAll, { SelectionDecorator } from './SelectionCheckboxAll'; import Column, { ColumnProps } from './Column'; import ColumnGroup from './ColumnGroup'; +import createTableRow from './createTableRow'; import { flatArray, treeMap, flatFilter, normalizeColumns } from './util'; function noop() { @@ -41,16 +42,16 @@ const emptyObject = {}; export type TableColumnConfig = ColumnProps; export interface TableComponents { - table?: React.ComponentType; + table?: any; header?: { - wrapper?: React.ComponentType; - row?: React.ComponentType; - cell?: React.ComponentType; + wrapper?: any; + row?: any; + cell?: any; }; body?: { - wrapper?: React.ComponentType; - row?: React.ComponentType; - cell?: React.ComponentType; + wrapper?: any; + row?: any; + cell?: any; }; } @@ -142,6 +143,7 @@ export default class Table extends React.Component, any> { CheckboxPropsCache: Object; store: Store; columns: ColumnProps[]; + components: TableComponents; constructor(props) { super(props); @@ -154,6 +156,8 @@ export default class Table extends React.Component, any> { this.columns = props.columns || normalizeColumns(props.children); + this.createComponents(props.components); + this.state = { ...this.getDefaultSortOrder(this.columns), // ε‡ε°‘ηŠΆζ€ @@ -256,6 +260,19 @@ export default class Table extends React.Component, any> { this.setState({ filters: newFilters }); } } + + this.createComponents(nextProps.components, this.props.components); + } + + onRow = (record, index) => { + const { onRow, prefixCls } = this.props; + const custom = onRow ? onRow(record, index) : {}; + return { + ...custom, + prefixCls, + store: this.store, + rowKey: this.getRecordKey(record, index), + }; } setSelectedRowKeys(selectedRowKeys, { selectWay, record, checked, changeRowKeys }: any) { @@ -921,6 +938,18 @@ export default class Table extends React.Component, any> { return data; } + createComponents(components: TableComponents = {}, prevComponents?: TableComponents) { + const bodyRow = components && components.body && components.body.row; + const preBodyRow = prevComponents && prevComponents.body && prevComponents.body.row; + this.components = { ...components }; + if (!prevComponents || bodyRow !== preBodyRow) { + this.components.body = { + ...components.body, + row: createTableRow(bodyRow), + }; + } + } + renderTable = (contextLocale) => { const locale = { ...contextLocale, ...this.props.locale }; const { style, className, prefixCls, showHeader, ...restProps } = this.props; @@ -945,10 +974,13 @@ export default class Table extends React.Component, any> { if ('expandIconColumnIndex' in restProps) { expandIconColumnIndex = restProps.expandIconColumnIndex as number; } + return ( { wrapper.update(); expect(renderedNames(wrapper)).toEqual(['10', '11', '12', '13', '14', '15', '16', '17', '18', '19']); }); + + it('highlight selected row', () => { + const wrapper = mount(createTable()); + wrapper.find('input').at(1).simulate('change', { target: { checked: true } }); + expect(wrapper.find('tbody tr').at(0).hasClass('ant-table-row-selected')).toBe(true); + }); }); diff --git a/components/table/createTableRow.tsx b/components/table/createTableRow.tsx new file mode 100644 index 0000000000..f171014c5c --- /dev/null +++ b/components/table/createTableRow.tsx @@ -0,0 +1,72 @@ +import * as React from 'react'; +import classnames from 'classnames'; +import omit from 'omit.js'; +import { Store } from './createStore'; + +interface TableRowProps { + store: Store; + className?: string; + rowKey: string; + prefixCls: string; +} + +interface TableRowState { + selected: boolean; +} + +export default function createTableRow(Component = 'tr') { + class TableRow extends React.Component { + private store: Store; + private unsubscribe: () => void; + + constructor(props) { + super(props); + + this.store = props.store; + const { selectedRowKeys } = this.store.getState(); + + this.state = { + selected: selectedRowKeys.indexOf(props.rowKey) >= 0, + }; + } + + componentDidMount() { + this.subscribe(); + } + + componentWillUnmount() { + if (this.unsubscribe) { + this.unsubscribe(); + } + } + + subscribe() { + const { store, rowKey } = this.props; + this.unsubscribe = store.subscribe(() => { + const { selectedRowKeys } = this.store.getState(); + const selected = selectedRowKeys.indexOf(rowKey) >= 0; + if (selected !== this.state.selected) { + this.setState({ selected }); + } + }); + } + + render() { + const rowProps = omit(this.props, ['prefixCls', 'rowKey']); + const className = classnames( + this.props.className, + { + [`${this.props.prefixCls}-row-selected`]: this.state.selected, + }, + ); + + return ( + + {this.props.children} + + ); + } + } + + return TableRow; +} diff --git a/components/table/style/index.less b/components/table/style/index.less index 022b0afa52..e3db5f6bc1 100644 --- a/components/table/style/index.less +++ b/components/table/style/index.less @@ -146,6 +146,10 @@ border-radius: 0; } + &-tbody > tr.@{table-prefix-cls}-row-selected td { + background: @table-selected-row-bg; + } + &-thead > tr > th.@{table-prefix-cls}-column-sort { background: @table-header-sort-bg; }