From 08be52878eb95639baaf669491e06656e8b19b4b Mon Sep 17 00:00:00 2001 From: yuta4j1 Date: Sun, 13 Oct 2024 16:58:15 +0900 Subject: [PATCH 1/2] feat: topItemCount for TableVirtuoso --- src/TableVirtuoso.tsx | 47 +++++++++++++++++++++-- src/component-interfaces/TableVirtuoso.ts | 7 +++- 2 files changed, 50 insertions(+), 4 deletions(-) diff --git a/src/TableVirtuoso.tsx b/src/TableVirtuoso.tsx index 8043fec47..ba0061259 100644 --- a/src/TableVirtuoso.tsx +++ b/src/TableVirtuoso.tsx @@ -79,7 +79,7 @@ const DefaultFillerRow = ({ height }: { height: number }) => ( const ITEM_STYLE = { overflowAnchor: 'none' } as const -const Items = /*#__PURE__*/ React.memo(function VirtuosoItems() { +const Items = /*#__PURE__*/ React.memo(function VirtuosoItems({ showTopList = false }: { showTopList?: boolean }) { const listState = useEmitterValue('listState') const sizeRanges = usePublisher('sizeRanges') const useWindowScroll = useEmitterValue('useWindowScroll') @@ -123,12 +123,51 @@ const Items = /*#__PURE__*/ React.memo(function VirtuosoItems() { const firstItemIndex = useEmitterValue('firstItemIndex') const statefulTotalCount = useEmitterValue('statefulTotalCount') const context = useEmitterValue('context') + const fixedHeaderHeight = useEmitterValue('fixedHeaderHeight') if (statefulTotalCount === 0 && EmptyPlaceholder) { return } - const paddingTop = listState.offsetTop + paddingTopAddition + deviation + const topItems = (showTopList ? listState.topItems : []).map((item) => { + const index = item.originalIndex! + const key = computeItemKey(index + firstItemIndex, item.data, context) + const offsetTop = listState.topItems.reduce((acc, item, itemIndex) => { + if (itemIndex < index) { + return acc + item.size + } + return acc + }, 0) + + if (isSeeking) { + return ( + + ) + } + return ( + + {itemContent(item.index, item.data, context)} + + ) + }) + + const topItemsSize = (showTopList ? listState.topItems : []).reduce((acc, item) => acc + item.size, 0) + + const paddingTop = listState.offsetTop + paddingTopAddition + deviation - topItemsSize const paddingBottom = listState.offsetBottom const paddingTopEl = paddingTop > 0 ? : null @@ -168,6 +207,7 @@ const Items = /*#__PURE__*/ React.memo(function VirtuosoItems() { return ( {paddingTopEl} + {topItems} {items} {paddingBottomEl} @@ -225,6 +265,7 @@ const WindowViewport: React.FC> = ({ children } const TableRoot: React.FC = /*#__PURE__*/ React.memo(function TableVirtuosoRoot(props) { const useWindowScroll = useEmitterValue('useWindowScroll') + const showTopList = useEmitterValue('topItemsIndexes').length > 0 const customScrollParent = useEmitterValue('customScrollParent') const fixedHeaderHeight = usePublisher('fixedHeaderHeight') const fixedFooterHeight = usePublisher('fixedFooterHeight') @@ -276,7 +317,7 @@ const TableRoot: React.FC = /*#__PURE__*/ React.memo(function Ta {theHead} - + {theFoot} diff --git a/src/component-interfaces/TableVirtuoso.ts b/src/component-interfaces/TableVirtuoso.ts index 30e37d696..e740befa5 100644 --- a/src/component-interfaces/TableVirtuoso.ts +++ b/src/component-interfaces/TableVirtuoso.ts @@ -17,7 +17,7 @@ import type { } from '../interfaces' import type { VirtuosoProps } from './Virtuoso' -export interface TableVirtuosoProps extends Omit, 'components' | 'headerFooterTag' | 'topItemCount'> { +export interface TableVirtuosoProps extends Omit, 'components' | 'headerFooterTag'> { /** * Use the `components` property for advanced customization of the elements rendered by the table. */ @@ -33,6 +33,11 @@ export interface TableVirtuosoProps extends Omit, 'com */ fixedFooterContent?: FixedFooterContent + /** + * Set the amount of items to remain fixed at the top of the table. + */ + topItemCount?: number + /** * The total amount of items to be rendered. */ From 78bd0c6bba33d98b33a48325560b4de48883a436 Mon Sep 17 00:00:00 2001 From: yuta4j1 Date: Mon, 14 Oct 2024 22:35:18 +0900 Subject: [PATCH 2/2] fix: aggregate topItems and items into ItemsComponent --- src/TableVirtuoso.tsx | 135 ++++++++++++++++++------------------------ 1 file changed, 58 insertions(+), 77 deletions(-) diff --git a/src/TableVirtuoso.tsx b/src/TableVirtuoso.tsx index ba0061259..1bf741a4c 100644 --- a/src/TableVirtuoso.tsx +++ b/src/TableVirtuoso.tsx @@ -81,6 +81,61 @@ const ITEM_STYLE = { overflowAnchor: 'none' } as const const Items = /*#__PURE__*/ React.memo(function VirtuosoItems({ showTopList = false }: { showTopList?: boolean }) { const listState = useEmitterValue('listState') + const computeItemKey = useEmitterValue('computeItemKey') + const firstItemIndex = useEmitterValue('firstItemIndex') + const isSeeking = useEmitterValue('isSeeking') + const ScrollSeekPlaceholder = useEmitterValue('ScrollSeekPlaceholder') || DefaultScrollSeekPlaceholder + const context = useEmitterValue('context') + const TableRowComponent = useEmitterValue('TableRowComponent')! + const fixedHeaderHeight = useEmitterValue('fixedHeaderHeight') + const itemContent = useEmitterValue('itemContent') + + const topItemOffsets = (showTopList ? listState.topItems : []).reduce((acc, item, index) => { + if (index === 0) { + acc.push(item.size) + } else { + acc.push(acc[index - 1] + item.size) + } + return acc + }, []) + + const items = (showTopList ? listState.topItems : listState.items).map((item) => { + const index = item.originalIndex! + const key = computeItemKey(index + firstItemIndex, item.data, context) + const offsetTop = showTopList ? (index === 0 ? 0 : topItemOffsets[index - 1]) : 0 + + if (isSeeking) { + return ( + + ) + } + return ( + + {itemContent(item.index, item.data, context)} + + ) + }) + + return <>{items} +}) + +const TableBody = /*#__PURE__*/ React.memo(function TableVirtuosoBody() { + const listState = useEmitterValue('listState') + const showTopList = useEmitterValue('topItemsIndexes').length > 0 const sizeRanges = usePublisher('sizeRanges') const useWindowScroll = useEmitterValue('useWindowScroll') const customScrollParent = useEmitterValue('customScrollParent') @@ -88,7 +143,6 @@ const Items = /*#__PURE__*/ React.memo(function VirtuosoItems({ showTopList = fa const _scrollContainerStateCallback = usePublisher('scrollContainerState') const scrollContainerStateCallback = customScrollParent || useWindowScroll ? windowScrollContainerStateCallback : _scrollContainerStateCallback - const itemContent = useEmitterValue('itemContent') const trackItemSizes = useEmitterValue('trackItemSizes') const itemSize = useEmitterValue('itemSize') const log = useEmitterValue('log') @@ -113,58 +167,16 @@ const Items = /*#__PURE__*/ React.memo(function VirtuosoItems({ showTopList = fa } }) const EmptyPlaceholder = useEmitterValue('EmptyPlaceholder') - const ScrollSeekPlaceholder = useEmitterValue('ScrollSeekPlaceholder') || DefaultScrollSeekPlaceholder const FillerRow = useEmitterValue('FillerRow') || DefaultFillerRow const TableBodyComponent = useEmitterValue('TableBodyComponent')! - const TableRowComponent = useEmitterValue('TableRowComponent')! - const computeItemKey = useEmitterValue('computeItemKey') - const isSeeking = useEmitterValue('isSeeking') const paddingTopAddition = useEmitterValue('paddingTopAddition') - const firstItemIndex = useEmitterValue('firstItemIndex') const statefulTotalCount = useEmitterValue('statefulTotalCount') const context = useEmitterValue('context') - const fixedHeaderHeight = useEmitterValue('fixedHeaderHeight') if (statefulTotalCount === 0 && EmptyPlaceholder) { return } - const topItems = (showTopList ? listState.topItems : []).map((item) => { - const index = item.originalIndex! - const key = computeItemKey(index + firstItemIndex, item.data, context) - const offsetTop = listState.topItems.reduce((acc, item, itemIndex) => { - if (itemIndex < index) { - return acc + item.size - } - return acc - }, 0) - - if (isSeeking) { - return ( - - ) - } - return ( - - {itemContent(item.index, item.data, context)} - - ) - }) - const topItemsSize = (showTopList ? listState.topItems : []).reduce((acc, item) => acc + item.size, 0) const paddingTop = listState.offsetTop + paddingTopAddition + deviation - topItemsSize @@ -174,41 +186,11 @@ const Items = /*#__PURE__*/ React.memo(function VirtuosoItems({ showTopList = fa const paddingBottomEl = paddingBottom > 0 ? : null - const items = listState.items.map((item) => { - const index = item.originalIndex! - const key = computeItemKey(index + firstItemIndex, item.data, context) - - if (isSeeking) { - return ( - - ) - } - return ( - - {itemContent(item.index, item.data, context)} - - ) - }) - return ( {paddingTopEl} - {topItems} - {items} + {showTopList && } + {paddingBottomEl} ) @@ -265,7 +247,6 @@ const WindowViewport: React.FC> = ({ children } const TableRoot: React.FC = /*#__PURE__*/ React.memo(function TableVirtuosoRoot(props) { const useWindowScroll = useEmitterValue('useWindowScroll') - const showTopList = useEmitterValue('topItemsIndexes').length > 0 const customScrollParent = useEmitterValue('customScrollParent') const fixedHeaderHeight = usePublisher('fixedHeaderHeight') const fixedFooterHeight = usePublisher('fixedFooterHeight') @@ -317,7 +298,7 @@ const TableRoot: React.FC = /*#__PURE__*/ React.memo(function Ta {theHead} - + {theFoot}