Skip to content
Merged
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 79 additions & 1 deletion src/components/table/body/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { memo, useCallback, useRef, useEffect } from "react"
import React, { memo, useCallback, useRef, useEffect, useMemo } from "react"
import { useVirtualizer, defaultRangeExtractor } from "@tanstack/react-virtual"
import identity from "lodash/identity"
import Flex from "@/components/templates/flex"
Expand Down Expand Up @@ -35,6 +35,7 @@ const Body = memo(
initialOffset = 0,
onScroll,
enableColumnReordering,
renderPlaceholder,
...rest
}) => {
useTableState(rerenderSelector)
Expand Down Expand Up @@ -68,6 +69,45 @@ const Body = memo(

const virtualRows = rowVirtualizer.getVirtualItems()

const placeholders = useMemo(() => {
if (!renderPlaceholder) return { before: [], after: [] }

const range = rowVirtualizer.calculateRange()
if (!range) return { before: [], after: [] }

const { startIndex, endIndex } = range
if (startIndex === undefined || endIndex === undefined)
return { before: [], after: [] }

// Adjust for header: index 0 is the header row, data rows start at 1
const firstDataIndex = 1
const lastDataIndex = rows.length

// "before" = data rows with index < startIndex (excluding header at 0)
const beforeStart = firstDataIndex
const beforeEnd = Math.max(beforeStart, startIndex)

// "after" = data rows with index > endIndex
const afterStart = Math.min(lastDataIndex, endIndex + 1)
const afterEnd = lastDataIndex + 1

return {
before: Array.from({ length: beforeEnd - beforeStart }, (_, i) => beforeStart + i),
after: Array.from({ length: afterEnd - afterStart }, (_, i) => afterStart + i),
}
}, [renderPlaceholder, virtualRows, rows.length])

const getPlaceholderOffset = useCallback(
index => {
// measurementsCache is populated for all count items on every render
// (getTotalSize calls getMeasurements which fills the full cache).
// Entries use real sizes for measured rows and estimateSize for the rest.
const cached = rowVirtualizer.measurementsCache[index]
return cached ? cached.start : 0
},
[rowVirtualizer]
)

useEffect(() => {
if (!loadMore) return

Expand Down Expand Up @@ -109,6 +149,25 @@ const Body = memo(
flex: "1 0 auto",
}}
>
{renderPlaceholder &&
placeholders.before.map(index => (
<div
key={`placeholder-before-${index}`}
style={{
position: "absolute",
top: 0,
left: 0,
transform: `translateY(${getPlaceholderOffset(index)}px)`,
minWidth: "100%",
}}
>
{renderPlaceholder({
index: index - 1,
isBefore: true,
table,
})}
</div>
))}
{virtualRows.map(virtualRow => {
return (
<div
Expand Down Expand Up @@ -153,6 +212,25 @@ const Body = memo(
</div>
)
})}
{renderPlaceholder &&
placeholders.after.map(index => (
<div
key={`placeholder-after-${index}`}
style={{
position: "absolute",
top: 0,
left: 0,
transform: `translateY(${getPlaceholderOffset(index)}px)`,
minWidth: "100%",
}}
>
{renderPlaceholder({
index: index - 1,
isBefore: false,
table,
})}
</div>
))}
</div>
</Flex>
)
Expand Down
Loading