Skip to content

Commit

Permalink
feat(Table): support keepForwardRenderRows, close #4395 (#4648)
Browse files Browse the repository at this point in the history
Co-authored-by: 珵之 <[email protected]>
  • Loading branch information
YunMeng99 and YSMJ1994 authored Dec 14, 2023
1 parent e86e1fd commit cc5ed00
Show file tree
Hide file tree
Showing 5 changed files with 230 additions and 4 deletions.
140 changes: 140 additions & 0 deletions docs/table/demo/virtual-rowspan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
# 虚拟滚动与行合并

- order: 28

虚拟滚动会卸载滚动到视区之外的行,行合并利用 td 元素的 rowSpan 属性,需要在合并的第一行设置 rowSpan 属性,若因滚动视区外卸载了该行,则会导致行合并失效,可以使用 `keepForwardRenderRows` 设置向前保留一定行数不被卸载,从而使行合并始终生效(设置 `keepForwardRenderRows` 大于 max rowSpan 即可)

:::lang=en-us
# Simple

- order: 28

Virtual scrolling will unload rows scrolled outside the viewport. Row merging uses the rowSpan attribute of the td element. The rowSpan attribute needs to be set on the first row of the merge. If the row is unloaded due to scrolling outside the viewport, the row merging will fail. , you can use `keepForwardRenderRows` to set a certain number of rows to be reserved forward without being unloaded, so that row merging will always take effect (just set `keepForwardRenderRows` to be greater than max rowSpan)
:::

---

````jsx
import { Table, Button } from '@alifd/next';

const noop = () => {};

const dataSource = (j) => {
const result = [];
for (let i = 0; i < j; i++) {
result.push({
title: { name: `Quotation for 1PCS Nano ${3 + i}.0 controller compatible` },
id: `100306660940${i}\nid\ntest`,
time: 2000 + i,
index: i
});
}
return result;
};

const render = (value, index, record) => {
return <a>remove({record.id})</a>;
};


class App extends React.Component {
state = {
scrollToRow: 0,
dataSource: [],
scrollToRow2: 0,
}

componentDidMount = () => {
console.log('componentDidMount');
setTimeout(() => {
this.setState({
dataSource: dataSource(200),
});
}, 1000);
}
onBodyScroll = (start) => {
this.setState({
scrollToRow: start
});
}

onBodyScroll2 = (start) => {
this.setState({
scrollToRow2: start
});
}


handleAdd = () => {
this.setState({
scrollToRow: 180,
});
}



renderCell = () => {
return <Button text onClick={this.handleAdd}>跳到底部</Button>;
}


render() {

return (
<div>
<div>{`keepForwardRenderRows < rowSpan的值时,滚动到第12行后行合并会失效`}</div>
<Table
className="inventory-table"
dataSource={this.state.dataSource}
maxBodyHeight={400}
useVirtual
keepForwardRenderRows={12}
scrollToRow={this.state.scrollToRow}
onBodyScroll={this.onBodyScroll}
cellProps={(rowIndex, colIndex) => {
if([0, 17, 34].includes(rowIndex) && colIndex === 0) {
return {
rowSpan: 17,
}
}
}}
>
<Table.Column title="Id1" dataIndex="id" width={120} />
<Table.Column title="Index" dataIndex="index" width={200} />
<Table.Column title="Time" dataIndex="time" width={200} />
<Table.Column title="remove" dataIndex="remove" cell={render} width={200} />
<Table.Column title="add" dataIndex="add" cell={this.renderCell} width={200} />
</Table>
<br/>
<div>{`keepForwardRenderRows >= rowSpan的值时,滚动到第12行后行合并不会失效`}</div>
<Table
className="inventory-table"
dataSource={this.state.dataSource}
maxBodyHeight={400}
useVirtual
keepForwardRenderRows={17}
scrollToRow={this.state.scrollToRow2}
onBodyScroll={this.onBodyScroll2}
cellProps={(rowIndex, colIndex) => {
if([0, 17, 34].includes(rowIndex) && colIndex === 0) {
return {
rowSpan: 17,
}
}
}}
>
<Table.Column title="Id1" dataIndex="id" width={120} />
<Table.Column title="Index" dataIndex="index" width={200} />
<Table.Column title="Time" dataIndex="time" width={200} />
<Table.Column title="remove" dataIndex="remove" cell={render} width={200} />
<Table.Column title="add" dataIndex="add" cell={this.renderCell} width={200} />
</Table>
</div>
);
}
}

ReactDOM.render(<App />, mountNode);

````

5 changes: 5 additions & 0 deletions src/table/base.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,10 @@ class Table extends React.Component {
* @returns {Object} 返回td元素的所支持的属性对象
*/
cellProps: PropTypes.func,
/**
* 虚拟滚动时向前保留渲染的行数
*/
keepForwardRenderRows: PropTypes.number,
/**
* 表格是否具有边框
*/
Expand Down Expand Up @@ -321,6 +325,7 @@ class Table extends React.Component {
cellProps: noop,
prefix: 'next-',
hasBorder: true,
keepForwardRenderRows: 10,
hasHeader: true,
isZebra: false,
loading: false,
Expand Down
9 changes: 6 additions & 3 deletions src/table/virtual.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import VirtualBody from './virtual/body';
import { statics } from './util';

const noop = () => {};
const THRESHOLD = 10;
export default function virtual(BaseComponent) {
class VirtualTable extends React.Component {
static VirtualBody = VirtualBody;
Expand All @@ -27,6 +26,7 @@ export default function virtual(BaseComponent) {
* 在内容区域滚动的时候触发的函数
*/
onBodyScroll: PropTypes.func,
keepForwardRenderRows: PropTypes.number,
...BaseComponent.propTypes,
};

Expand All @@ -38,6 +38,7 @@ export default function virtual(BaseComponent) {
components: {},
prefix: 'next-',
onBodyScroll: noop,
keepForwardRenderRows: 10,
};

static childContextTypes = {
Expand Down Expand Up @@ -141,12 +142,13 @@ export default function virtual(BaseComponent) {
}

computeInnerTop() {
const { keepForwardRenderRows } = this.props;
const { rowHeight } = this.state;
if (typeof rowHeight === 'function') {
return 0;
}

const start = Math.max(this.start - THRESHOLD, 0);
const start = Math.max(this.start - keepForwardRenderRows, 0);

return start * rowHeight;
}
Expand Down Expand Up @@ -269,6 +271,7 @@ export default function virtual(BaseComponent) {
rowHeight,
scrollToRow,
onBodyScroll,
keepForwardRenderRows,
...others
} = this.props;

Expand All @@ -284,7 +287,7 @@ export default function virtual(BaseComponent) {
dataSource.forEach((current, index, record) => {
if (!current.__hidden) {
count += 1;
if (count >= Math.max(start - THRESHOLD, 0) && count < end) {
if (count >= Math.max(start - keepForwardRenderRows, 0) && count < end) {
newDataSource.push(current);
}
}
Expand Down
74 changes: 73 additions & 1 deletion test/table/issue-spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ import Adapter from 'enzyme-adapter-react-16';
import assert from 'power-assert';
import Promise from 'promise-polyfill';
import Table from '../../src/table/index';
import Button from '../../src/button/index';
import ConfigProvider from '../../src/config-provider';
import Input from '../../src/input';
import '../../src/table/style.js';
import Button from '../../src/button/index';
/* eslint-disable */
Enzyme.configure({ adapter: new Adapter() });
const delay = duration => new Promise(resolve => setTimeout(resolve, duration));
Expand Down Expand Up @@ -1426,4 +1426,76 @@ describe('TableScroll', () => {
const scrollRowTop = scrollRow.getBoundingClientRect().top;
assert(scrollRowTop >= getBodyTop() - 10);
});
it('set keepForwardRenderRows to support large rowSpan when useVirtual, close #4395', async () => {
const datas = j => {
const result = [];
for (let i = 0; i < j; i++) {
result.push({
title: { name: `Quotation for 1PCS Nano ${3 + i}.0 controller compatible` },
id: `100306660940${i}\nid\ntest`,
time: 2000 + i,
index: i,
});
}
return result;
};
class App extends React.Component {
state = {
scrollToRow: 0,
dataSource: datas(200),
};

componentDidUpdate(prevProps) {
if ('scrollToRow' in this.props && this.props.scrollToRow !== prevProps.scrollToRow) {
this.setState({
scrollToRow: this.props.scrollToRow,
});
}
}

onBodyScroll = start => {
this.setState({
scrollToRow: start,
});
};

render() {
return (
<Table
className="inventory-table"
dataSource={this.state.dataSource}
maxBodyHeight={314}
useVirtual
keepForwardRenderRows={this.props.keepForwardRenderRows}
scrollToRow={this.state.scrollToRow}
onBodyScroll={this.onBodyScroll}
cellProps={(rowIndex, colIndex) => {
if ([0, 17, 34].includes(rowIndex) && colIndex === 0) {
return {
rowSpan: 17,
};
}
}}
>
<Table.Column title="Id1" dataIndex="id" width={120} />
<Table.Column title="Index" dataIndex="index" width={200} />
<Table.Column title="Time" dataIndex="time" width={200} />
</Table>
);
}
}

const container = document.createElement('div');
document.body.appendChild(container);
const wrapper = mount(<App keepForwardRenderRows={10} />, { attachTo: container });
await delay(100);

wrapper.setProps({ scrollToRow: 15 });
assert(!container.querySelector('[data-next-table-row="0"]'));

wrapper.setProps({ keepForwardRenderRows: 17 });
assert(container.querySelector('[data-next-table-row="0"]'));

wrapper.unmount();
});
});
6 changes: 6 additions & 0 deletions types/table/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,12 @@ export interface TableProps extends React.HTMLAttributes<HTMLElement>, BaseTable
* 在 hover 时出现十字参考轴,适用于表头比较复杂,需要做表头分类的场景。
*/
crossline?: boolean;

/**
* 虚拟滚动时向前保留渲染的行数
* @defaultValue 10
*/
keepForwardRenderRows?: number;
}

export default class Table extends React.Component<TableProps, any> {
Expand Down

0 comments on commit cc5ed00

Please sign in to comment.