From cc3d61017f37db303064a46b5f47078eb660eccc Mon Sep 17 00:00:00 2001 From: Shi Jia Date: Fri, 12 Jul 2024 15:22:01 +0800 Subject: [PATCH] fix: fixed virtualized Table List ref is null bug #2305 (#2319) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: fixed virtualized Table List ref is null bug #2305 * fix: table story import error --------- Co-authored-by: shijia.me Co-authored-by: pointhalo <88709023+pointhalo@users.noreply.github.com> Co-authored-by: 代强 Co-authored-by: pointhalo --- cypress/e2e/table.spec.js | 8 ++ packages/semi-foundation/slider/foundation.ts | 2 +- packages/semi-ui/slider/index.tsx | 22 ++-- packages/semi-ui/table/Body/index.tsx | 25 ++-- .../semi-ui/table/_story/table.stories.jsx | 1 + .../_story/v2/FixedVirtualizedRef/index.tsx | 118 ++++++++++++++++++ packages/semi-ui/table/_story/v2/index.js | 1 + packages/semi-ui/table/interface.ts | 4 +- 8 files changed, 160 insertions(+), 21 deletions(-) create mode 100644 packages/semi-ui/table/_story/v2/FixedVirtualizedRef/index.tsx diff --git a/cypress/e2e/table.spec.js b/cypress/e2e/table.spec.js index 3edcaff1c7..1b90f37320 100644 --- a/cypress/e2e/table.spec.js +++ b/cypress/e2e/table.spec.js @@ -303,6 +303,14 @@ describe('table', () => { cy.get('tbody .semi-table-row-section').eq(0).should('have.class', 'test-group'); }); + + it('test virtualized table ref', () => { + cy.visit('http://localhost:6006/iframe.html?id=table--fixed-virtualized-ref&viewMode=story'); + cy.get('.semi-button').eq(0).click(); + cy.wait(300); + cy.get('.semi-table-row-cell').should('contain.text', 'Semi Design 设计稿20.fig'); + }); + it('test rowSelection onCell and onHeaderCell', () => { cy.visit('http://localhost:6006/iframe.html?id=table--row-selection-on-cell&viewMode=story'); cy.get('.test-th').should('have.attr', 'style').should('contain', 'background: blue'); diff --git a/packages/semi-foundation/slider/foundation.ts b/packages/semi-foundation/slider/foundation.ts index d1f4d56a4c..dafb950adb 100644 --- a/packages/semi-foundation/slider/foundation.ts +++ b/packages/semi-foundation/slider/foundation.ts @@ -40,7 +40,7 @@ export interface SliderProps{ handleDot?: { size?: string; color?: string - } & ({ + } | ({ size?: string; color?: string }[]) diff --git a/packages/semi-ui/slider/index.tsx b/packages/semi-ui/slider/index.tsx index 5352cc0424..569ceec582 100644 --- a/packages/semi-ui/slider/index.tsx +++ b/packages/semi-ui/slider/index.tsx @@ -137,7 +137,7 @@ export default class Slider extends BaseComponent { const offsetParentRect = this.sliderEl.current.offsetParent?.getBoundingClientRect(); const offset = { - x: offsetParentRect ? (rect.left - offsetParentRect.left): this.sliderEl.current.offsetLeft, + x: offsetParentRect ? (rect.left - offsetParentRect.left) : this.sliderEl.current.offsetLeft, y: offsetParentRect ? (rect.top - offsetParentRect.top) : this.sliderEl.current.offsetTop, }; return { @@ -333,7 +333,13 @@ export default class Slider extends BaseComponent { 'aria-disabled': disabled }; vertical && Object.assign(commonAria, { 'aria-orientation': 'vertical' }); - + const handleDot = this.props.handleDot as { + size?: string; + color?: string + } & ({ + size?: string; + color?: string + }[]); const handleContents = !range ? ( { aria-valuemax={max} aria-valuemin={min} > - {this.props.handleDot &&
} @@ -445,9 +451,9 @@ export default class Slider extends BaseComponent { aria-valuemax={currentValue[1]} aria-valuemin={min} > - {this.props.handleDot?.[0] &&
} diff --git a/packages/semi-ui/table/Body/index.tsx b/packages/semi-ui/table/Body/index.tsx index 4fa9bae48e..52c288eef4 100644 --- a/packages/semi-ui/table/Body/index.tsx +++ b/packages/semi-ui/table/Body/index.tsx @@ -147,15 +147,7 @@ class Body extends BaseComponent { }; this.listRef = React.createRef(); - const { getVirtualizedListRef, flattenedColumns, getCellWidths } = context; - if (getVirtualizedListRef) { - if (props.virtualized) { - getVirtualizedListRef(this.listRef); - } else { - console.warn('getVirtualizedListRef only works with virtualized. ' + - 'See https://semi.design/en-US/show/table for more information.'); - } - } + const { flattenedColumns, getCellWidths } = context; this.foundation = new BodyFoundation(this.adapter); this.flattenedColumns = flattenedColumns; this.cellWidths = getCellWidths(flattenedColumns); @@ -246,6 +238,19 @@ class Body extends BaseComponent { } }; + setListRef = (listInstance: List) => { + this.listRef.current = listInstance; + const { getVirtualizedListRef } = this.context; + if (getVirtualizedListRef) { + if (this.props.virtualized) { + getVirtualizedListRef(this.listRef); + } else { + console.warn('getVirtualizedListRef only works with virtualized. ' + + 'See https://semi.design/en-US/show/table for more information.'); + } + } + }; + itemSize = (index: number) => { const { virtualized, size: tableSize } = this.props; const { virtualizedData } = this.state; @@ -432,7 +437,7 @@ class Body extends BaseComponent { initialScrollOffset={this.state.cache.virtualizedScrollTop} onScroll={this.handleVirtualizedScroll} onItemsRendered={this.onItemsRendered} - ref={this.listRef} + ref={this.setListRef} className={wrapCls} outerRef={this.forwardRef} height={virtualizedData?.length ? y : 0} diff --git a/packages/semi-ui/table/_story/table.stories.jsx b/packages/semi-ui/table/_story/table.stories.jsx index aed9a7e30f..750d1eba64 100644 --- a/packages/semi-ui/table/_story/table.stories.jsx +++ b/packages/semi-ui/table/_story/table.stories.jsx @@ -117,6 +117,7 @@ export { FixedRowSelectionEmpty, DndKitDrag, FixedOnGroupedRowClassName, + FixedVirtualizedRef, RowSelectionOnCell } from './v2'; export { default as FixSelectAll325 } from './Demos/rowSelection'; diff --git a/packages/semi-ui/table/_story/v2/FixedVirtualizedRef/index.tsx b/packages/semi-ui/table/_story/v2/FixedVirtualizedRef/index.tsx new file mode 100644 index 0000000000..6d82a43e17 --- /dev/null +++ b/packages/semi-ui/table/_story/v2/FixedVirtualizedRef/index.tsx @@ -0,0 +1,118 @@ +import React, { useRef, useState, useEffect } from "react"; +import * as dateFns from 'date-fns'; +import { VariableSizeList } from 'react-window'; + +import { Avatar, Button, Table } from "../../../../index"; +import { ColumnProps } from "../../../interface"; + +export default function VirtualizedFixedDemo() { + const DAY = 24 * 60 * 60 * 1000; + let virtualizedListRef = useRef(); + const [scroll, setScroll] = useState({}); + const style = { width: 750, margin: '0 auto' }; + const getData = () => { + const data = []; + for (let i = 0; i < 1000; i++) { + const isSemiDesign = i % 2 === 0; + const randomNumber = (i * 1000) % 199; + data.push({ + key: '' + i, + name: isSemiDesign ? `Semi Design 设计稿${i}.fig` : `Semi D2C 设计稿${i}.fig`, + owner: isSemiDesign ? '姜鹏志' : '郝宣', + size: randomNumber, + updateTime: new Date('2024-06-24').valueOf() + randomNumber * DAY, + avatarBg: isSemiDesign ? 'grey' : 'red', + }); + } + return data; + }; + + const data = getData(); + const columns: ColumnProps[] = [ + { + title: '标题', + dataIndex: 'name', + width: 200, + fixed: true, + render: (text, record, index) => { + return
{text}
; + }, + filters: [ + { + text: 'Semi Design 设计稿', + value: 'Semi Design 设计稿', + }, + { + text: 'Semi D2C 设计稿', + value: 'Semi D2C 设计稿', + }, + ], + onCell: (_, index) => { + return ({ + className: `row-${index}` + }); + }, + onFilter: (value, record) => record.name.includes(value), + }, + { + title: '大小', + dataIndex: 'size', + width: 150, + sorter: (a, b) => (a.size - b.size > 0 ? 1 : -1), + render: text => `${text} KB`, + }, + { + title: '所有者', + dataIndex: 'owner', + render: (text, record, index) => { + return ( +
+ + {typeof text === 'string' && text.slice(0, 1)} + + {text} +
+ ); + }, + }, + { + title: '更新日期', + dataIndex: 'updateTime', + fixed: 'right', + width: 150, + sorter: (a, b) => (a.updateTime - b.updateTime > 0 ? 1 : -1), + render: value => { + return dateFns.format(new Date(value), 'yyyy-MM-dd'); + }, + }, + ]; + + useEffect(() => { + setScroll({ + y: 400, x: 900 + }); + }, []); + + const handleClick = () => { + console.log('ref', virtualizedListRef); + virtualizedListRef.current && virtualizedListRef.current.scrollToItem(20); + }; + + return ( + <> + + { + console.log('ref', ref); + virtualizedListRef = ref; + }} + /> + + ); +} \ No newline at end of file diff --git a/packages/semi-ui/table/_story/v2/index.js b/packages/semi-ui/table/_story/v2/index.js index 007ad3158b..2430489eb8 100644 --- a/packages/semi-ui/table/_story/v2/index.js +++ b/packages/semi-ui/table/_story/v2/index.js @@ -36,4 +36,5 @@ export { default as FixedDefaultExpandedGroupedRows } from './FixedExpandGroupRo export { default as FixedRowSelectionEmpty } from './FixedRowSelectionEmpty'; export { default as DndKitDrag } from './DndKitDrag'; export { default as FixedOnGroupedRowClassName } from './FixedOnGroupedRowClassName'; +export { default as FixedVirtualizedRef } from './FixedVirtualizedRef'; export { default as RowSelectionOnCell } from './RowSelectionOnCell'; diff --git a/packages/semi-ui/table/interface.ts b/packages/semi-ui/table/interface.ts index 69e323013e..43ba7b68ad 100644 --- a/packages/semi-ui/table/interface.ts +++ b/packages/semi-ui/table/interface.ts @@ -20,7 +20,7 @@ import type { BaseIncludeGroupRecord, BaseEllipsis } from '@douyinfe/semi-foundation/table/foundation'; -import type { ScrollDirection, CSSDirection } from 'react-window'; +import type { ScrollDirection, CSSDirection, VariableSizeList } from 'react-window'; import type { ColumnFilterProps } from './ColumnFilter'; export interface TableProps = any> extends BaseProps { @@ -277,7 +277,7 @@ export type ExpandIcon = ((expanded?: boolean) => React.ReactNode) | React.React export type ExpandedRowRender = (record?: RecordType, index?: number, expanded?: boolean) => React.ReactNode; export type Footer = ReactNode | ((pageData?: RecordType[]) => React.ReactNode); export type FormatPageText = ((pageInfo?: { currentStart?: number; currentEnd?: number; total?: number }) => React.ReactNode) | boolean; -export type GetVirtualizedListRef = (ref: MutableRefObject) => void; +export type GetVirtualizedListRef = (ref: MutableRefObject) => void; export type GroupByFunction = BaseGroupByFn; export type GroupBy = BaseGroupBy; export type Size = ArrayElement;