feat(web): upgrade lists to ReUI Table v9 and EventCalendar
quality / commitlint (push) Skipped
quality / changes (push) Successful in 9s
quality / docker-check (push) Skipped
quality / openapi (push) Successful in 39s
quality / web (push) Successful in 1m29s
quality / go (push) Successful in 1m3s
quality / bird2 (push) Successful in 15s
CD / quality (push) Successful in 3m43s
CD / publish (push) Failing after 8m29s
quality / commitlint (push) Skipped
quality / changes (push) Successful in 9s
quality / docker-check (push) Skipped
quality / openapi (push) Successful in 39s
quality / web (push) Successful in 1m29s
quality / go (push) Successful in 1m3s
quality / bird2 (push) Successful in 15s
CD / quality (push) Successful in 3m43s
CD / publish (push) Failing after 8m29s
Единый kitDataGridTableLayout и ResourcePage/FrameDataGrid на всех списках. Календарь задач переведён на EventCalendar, lookup — на cascader, у операций появился вид доски. Удалены settings-7 и самописные DataGridSection/toolbar. Co-authored-by: Cursor <[email protected]>
This commit is contained in:
@@ -1,14 +1,10 @@
|
||||
"use client"
|
||||
|
||||
import {
|
||||
CSSProperties,
|
||||
memo,
|
||||
ReactNode,
|
||||
useCallback,
|
||||
useEffect,
|
||||
useState,
|
||||
} from "react"
|
||||
import { memo, useCallback, useEffect, useRef, useState } from "react"
|
||||
import type { CSSProperties, ReactNode } from "react"
|
||||
import { useDataGrid } from "@/components/reui/data-grid/data-grid"
|
||||
import type {
|
||||
DataGridFeatures,
|
||||
DataGridTableInstance,
|
||||
} from "@/components/reui/data-grid/data-grid"
|
||||
import {
|
||||
DataGridTableBase,
|
||||
DataGridTableBody,
|
||||
@@ -23,14 +19,16 @@ import {
|
||||
DataGridTableRenderedRow,
|
||||
DataGridTableRowSpacer,
|
||||
DataGridTableViewport,
|
||||
getDataGridScrollAreaViewport,
|
||||
getDataGridTableMergedHeaderGroups,
|
||||
getDataGridTableRowSections,
|
||||
getPinningStyles,
|
||||
hasDataGridTableRightPinnedColumns,
|
||||
} from "@/components/reui/data-grid/data-grid-table"
|
||||
import { Column, flexRender, Row, Table } from "@tanstack/react-table"
|
||||
import {
|
||||
useVirtualizer,
|
||||
import { flexRender } from "@tanstack/react-table"
|
||||
import type { Column, Row, Table } from "@tanstack/react-table"
|
||||
import { useVirtualizer } from "@tanstack/react-virtual"
|
||||
import type {
|
||||
VirtualItem,
|
||||
Virtualizer,
|
||||
VirtualizerOptions,
|
||||
@@ -49,21 +47,226 @@ type DataGridTableVirtualizerInstance = Virtualizer<
|
||||
HTMLTableRowElement
|
||||
>
|
||||
|
||||
type DataGridTableVirtualizerOptions<TData> = Omit<
|
||||
type DataGridTableVirtualScrollAlignment = "auto" | "center" | "start" | "end"
|
||||
|
||||
type DataGridTableVirtualScrollRequest = {
|
||||
align: DataGridTableVirtualScrollAlignment
|
||||
behavior: ScrollBehavior
|
||||
containerElement: HTMLDivElement
|
||||
headerSticky: boolean
|
||||
isVirtualizationEnabled: boolean
|
||||
rowId: string | undefined
|
||||
rowIndex: number
|
||||
scrollElement: HTMLElement
|
||||
}
|
||||
|
||||
function isSameDataGridTableScrollRequest(
|
||||
previous: DataGridTableVirtualScrollRequest | null,
|
||||
next: DataGridTableVirtualScrollRequest
|
||||
) {
|
||||
return (
|
||||
previous?.align === next.align &&
|
||||
previous.behavior === next.behavior &&
|
||||
previous.containerElement === next.containerElement &&
|
||||
previous.headerSticky === next.headerSticky &&
|
||||
previous.isVirtualizationEnabled === next.isVirtualizationEnabled &&
|
||||
previous.rowId === next.rowId &&
|
||||
previous.rowIndex === next.rowIndex &&
|
||||
previous.scrollElement === next.scrollElement
|
||||
)
|
||||
}
|
||||
|
||||
function getDataGridTableScrollTarget({
|
||||
align,
|
||||
clientHeight,
|
||||
rowBottom,
|
||||
rowHeight,
|
||||
rowTop,
|
||||
scrollHeight,
|
||||
scrollTop,
|
||||
viewportTopOffset = 0,
|
||||
}: {
|
||||
align: DataGridTableVirtualScrollAlignment
|
||||
clientHeight: number
|
||||
rowBottom: number
|
||||
rowHeight: number
|
||||
rowTop: number
|
||||
scrollHeight: number
|
||||
scrollTop: number
|
||||
viewportTopOffset?: number
|
||||
}) {
|
||||
const visibleHeight = Math.max(0, clientHeight - viewportTopOffset)
|
||||
const viewportTop = scrollTop + viewportTopOffset
|
||||
const viewportBottom = scrollTop + clientHeight
|
||||
|
||||
const targetTop =
|
||||
align === "auto"
|
||||
? rowTop < viewportTop
|
||||
? rowTop - viewportTopOffset
|
||||
: rowBottom > viewportBottom
|
||||
? rowBottom - clientHeight
|
||||
: null
|
||||
: align === "start"
|
||||
? rowTop - viewportTopOffset
|
||||
: align === "end"
|
||||
? rowBottom - clientHeight
|
||||
: rowTop -
|
||||
viewportTopOffset -
|
||||
Math.max(0, (visibleHeight - rowHeight) / 2)
|
||||
|
||||
if (targetTop === null) return null
|
||||
|
||||
return Math.min(
|
||||
Math.max(0, targetTop),
|
||||
Math.max(0, scrollHeight - clientHeight)
|
||||
)
|
||||
}
|
||||
|
||||
function getDataGridTableHeaderOffset({
|
||||
containerElement,
|
||||
headerSticky,
|
||||
scrollElement,
|
||||
}: {
|
||||
containerElement: HTMLDivElement
|
||||
headerSticky: boolean
|
||||
scrollElement: HTMLElement
|
||||
}) {
|
||||
if (!headerSticky) return 0
|
||||
|
||||
const headerElement = containerElement.querySelector<HTMLElement>(
|
||||
':scope > [data-slot="data-grid-table"] > thead'
|
||||
)
|
||||
|
||||
if (!headerElement) return 0
|
||||
|
||||
const scrollRect = scrollElement.getBoundingClientRect()
|
||||
const headerRect = headerElement.getBoundingClientRect()
|
||||
const headerBottomOffset = headerRect.bottom - scrollRect.top
|
||||
const overlapsViewportTop =
|
||||
headerRect.top <= scrollRect.top + 0.5 && headerBottomOffset > 0
|
||||
|
||||
if (!overlapsViewportTop) return 0
|
||||
|
||||
return Math.min(scrollElement.clientHeight, Math.max(0, headerBottomOffset))
|
||||
}
|
||||
|
||||
function scrollDataGridTableToOffset({
|
||||
behavior,
|
||||
scrollElement,
|
||||
targetTop,
|
||||
virtualizer,
|
||||
}: {
|
||||
behavior: ScrollBehavior
|
||||
scrollElement: HTMLElement
|
||||
targetTop: number
|
||||
virtualizer?: DataGridTableVirtualizerInstance
|
||||
}) {
|
||||
if (virtualizer) {
|
||||
virtualizer.scrollToOffset(targetTop, { align: "start", behavior })
|
||||
} else if (typeof scrollElement.scrollTo === "function") {
|
||||
scrollElement.scrollTo({ behavior, top: targetTop })
|
||||
} else {
|
||||
scrollElement.scrollTop = targetTop
|
||||
}
|
||||
}
|
||||
|
||||
function scrollDataGridTableRowIntoView({
|
||||
align,
|
||||
behavior,
|
||||
cancelPendingScroll = false,
|
||||
containerElement,
|
||||
headerSticky,
|
||||
rowIndex,
|
||||
scrollElement,
|
||||
virtualizer,
|
||||
}: {
|
||||
align: DataGridTableVirtualScrollAlignment
|
||||
behavior: ScrollBehavior
|
||||
cancelPendingScroll?: boolean
|
||||
containerElement: HTMLDivElement | null
|
||||
headerSticky: boolean
|
||||
rowIndex: number
|
||||
scrollElement: HTMLElement | null
|
||||
virtualizer?: DataGridTableVirtualizerInstance
|
||||
}) {
|
||||
if (!containerElement || !scrollElement) return false
|
||||
|
||||
const rowElement = containerElement.querySelector<HTMLTableRowElement>(
|
||||
`:scope > [data-slot="data-grid-table"] > tbody > tr[data-index="${rowIndex}"]`
|
||||
)
|
||||
|
||||
if (!rowElement) return false
|
||||
|
||||
const scrollRect = scrollElement.getBoundingClientRect()
|
||||
const rowRect = rowElement.getBoundingClientRect()
|
||||
const viewportTopOffset = getDataGridTableHeaderOffset({
|
||||
containerElement,
|
||||
headerSticky,
|
||||
scrollElement,
|
||||
})
|
||||
const rowTop = scrollElement.scrollTop + rowRect.top - scrollRect.top
|
||||
const rowBottom = scrollElement.scrollTop + rowRect.bottom - scrollRect.top
|
||||
const targetTop = getDataGridTableScrollTarget({
|
||||
align,
|
||||
clientHeight: scrollElement.clientHeight,
|
||||
rowBottom,
|
||||
rowHeight: rowRect.height || rowElement.offsetHeight,
|
||||
rowTop,
|
||||
scrollHeight: scrollElement.scrollHeight,
|
||||
scrollTop: scrollElement.scrollTop,
|
||||
viewportTopOffset,
|
||||
})
|
||||
|
||||
if (
|
||||
targetTop === null ||
|
||||
Math.abs(targetTop - scrollElement.scrollTop) < 0.5
|
||||
) {
|
||||
if (cancelPendingScroll) {
|
||||
scrollDataGridTableToOffset({
|
||||
behavior: "auto",
|
||||
scrollElement,
|
||||
targetTop: scrollElement.scrollTop,
|
||||
virtualizer,
|
||||
})
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
scrollDataGridTableToOffset({
|
||||
behavior,
|
||||
scrollElement,
|
||||
targetTop,
|
||||
virtualizer,
|
||||
})
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
type DataGridTableVirtualizerOptions<TData extends object> = Omit<
|
||||
VirtualizerOptions<HTMLElement, HTMLTableRowElement>,
|
||||
"count" | "estimateSize" | "getItemKey" | "getScrollElement"
|
||||
> & {
|
||||
estimateSize?: (index: number, row: Row<TData>) => number
|
||||
getItemKey?: (index: number, row: Row<TData>) => string | number
|
||||
estimateSize?: (index: number, row: Row<DataGridFeatures, TData>) => number
|
||||
getItemKey?: (
|
||||
index: number,
|
||||
row: Row<DataGridFeatures, TData>
|
||||
) => string | number
|
||||
getScrollElement?: (
|
||||
elements: DataGridTableVirtualScrollElements
|
||||
) => HTMLElement | null
|
||||
}
|
||||
|
||||
interface DataGridTableVirtualProps<TData> {
|
||||
interface DataGridTableVirtualProps<TData extends object> {
|
||||
height?: number | string
|
||||
estimateSize?: number
|
||||
overscan?: number
|
||||
/** Scroll animation used when revealing a controlled target row. */
|
||||
scrollBehavior?: ScrollBehavior
|
||||
/** Alignment used when revealing a controlled target row. Defaults to auto. */
|
||||
scrollToRowAlign?: DataGridTableVirtualScrollAlignment
|
||||
/** Index within the center (non-pinned) row section to reveal. */
|
||||
scrollToRowIndex?: number
|
||||
footerContent?: ReactNode
|
||||
renderHeader?: boolean
|
||||
onFetchMore?: () => void
|
||||
@@ -73,11 +276,11 @@ interface DataGridTableVirtualProps<TData> {
|
||||
virtualizerOptions?: DataGridTableVirtualizerOptions<TData>
|
||||
}
|
||||
|
||||
interface VirtualBodyProps<TData> {
|
||||
table: Table<TData>
|
||||
topRows: Row<TData>[]
|
||||
centerRows: Row<TData>[]
|
||||
bottomRows: Row<TData>[]
|
||||
interface VirtualBodyProps<TData extends object> {
|
||||
table: DataGridTableInstance<TData>
|
||||
topRows: Row<DataGridFeatures, TData>[]
|
||||
centerRows: Row<DataGridFeatures, TData>[]
|
||||
bottomRows: Row<DataGridFeatures, TData>[]
|
||||
virtualItems: VirtualItem[]
|
||||
totalSize: number
|
||||
isVirtualizationEnabled: boolean
|
||||
@@ -89,16 +292,16 @@ interface VirtualBodyProps<TData> {
|
||||
measureRowRef?: (element: HTMLTableRowElement | null) => void
|
||||
}
|
||||
|
||||
function DataGridTableVirtualPinnedPlaceholderCell<TData>({
|
||||
function DataGridTableVirtualPinnedPlaceholderCell<TData extends object>({
|
||||
column,
|
||||
}: {
|
||||
column: Column<TData>
|
||||
column: Column<DataGridFeatures, TData, unknown>
|
||||
}) {
|
||||
const { props } = useDataGrid()
|
||||
const isPinned = column.getIsPinned()
|
||||
const isLastLeftPinned = isPinned === "left" && column.getIsLastColumn("left")
|
||||
const isFirstRightPinned =
|
||||
isPinned === "right" && column.getIsFirstColumn("right")
|
||||
const isLastStartPinned =
|
||||
isPinned === "start" && column.getIsLastColumn("start")
|
||||
const isFirstEndPinned = isPinned === "end" && column.getIsFirstColumn("end")
|
||||
|
||||
return (
|
||||
<td
|
||||
@@ -113,20 +316,20 @@ function DataGridTableVirtualPinnedPlaceholderCell<TData>({
|
||||
}}
|
||||
data-pinned={isPinned || undefined}
|
||||
data-last-col={
|
||||
isLastLeftPinned ? "left" : isFirstRightPinned ? "right" : undefined
|
||||
isLastStartPinned ? "start" : isFirstEndPinned ? "end" : undefined
|
||||
}
|
||||
className={cn(
|
||||
"p-0",
|
||||
props.tableLayout?.cellBorder && "border-e",
|
||||
props.tableLayout?.columnsPinnable &&
|
||||
column.getCanPin() &&
|
||||
"data-pinned:bg-background data-pinned:isolate [&[data-pinned=left][data-last-col=left]]:shadow-[inset_-1px_0_0_0_var(--border)] [&[data-pinned=right][data-last-col=right]]:shadow-[inset_1px_0_0_0_var(--border)]"
|
||||
"data-pinned:bg-background data-pinned:isolate [&[data-pinned=end][data-last-col=end]]:shadow-[inset_1px_0_0_0_var(--border)] [&[data-pinned=start][data-last-col=start]]:shadow-[inset_-1px_0_0_0_var(--border)]"
|
||||
)}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function DataGridTableVirtualUtilityRow<TData>({
|
||||
function DataGridTableVirtualUtilityRow<TData extends object>({
|
||||
table,
|
||||
children,
|
||||
centerCellClassName,
|
||||
@@ -134,7 +337,7 @@ function DataGridTableVirtualUtilityRow<TData>({
|
||||
rowClassName,
|
||||
ariaHidden,
|
||||
}: {
|
||||
table: Table<TData>
|
||||
table: DataGridTableInstance<TData>
|
||||
children: ReactNode
|
||||
centerCellClassName?: string
|
||||
centerCellStyle?: CSSProperties
|
||||
@@ -142,9 +345,9 @@ function DataGridTableVirtualUtilityRow<TData>({
|
||||
ariaHidden?: boolean
|
||||
}) {
|
||||
const { props } = useDataGrid()
|
||||
const leftVisibleColumns = table.getLeftVisibleLeafColumns()
|
||||
const leftVisibleColumns = table.getStartVisibleLeafColumns()
|
||||
const centerVisibleColumns = table.getCenterVisibleLeafColumns()
|
||||
const rightVisibleColumns = table.getRightVisibleLeafColumns()
|
||||
const rightVisibleColumns = table.getEndVisibleLeafColumns()
|
||||
const hasRightPinnedColumns = hasDataGridTableRightPinnedColumns(table)
|
||||
|
||||
return (
|
||||
@@ -178,11 +381,11 @@ function DataGridTableVirtualUtilityRow<TData>({
|
||||
)
|
||||
}
|
||||
|
||||
function DataGridTableVirtualSpacer<TData>({
|
||||
function DataGridTableVirtualSpacer<TData extends object>({
|
||||
table,
|
||||
height,
|
||||
}: {
|
||||
table: Table<TData>
|
||||
table: DataGridTableInstance<TData>
|
||||
height: number
|
||||
}) {
|
||||
if (height <= 0) return null
|
||||
@@ -199,12 +402,12 @@ function DataGridTableVirtualSpacer<TData>({
|
||||
)
|
||||
}
|
||||
|
||||
function DataGridTableVirtualStatusRow<TData>({
|
||||
function DataGridTableVirtualStatusRow<TData extends object>({
|
||||
table,
|
||||
children,
|
||||
className,
|
||||
}: {
|
||||
table: Table<TData>
|
||||
table: DataGridTableInstance<TData>
|
||||
children: ReactNode
|
||||
className?: string
|
||||
}) {
|
||||
@@ -221,7 +424,7 @@ function DataGridTableVirtualStatusRow<TData>({
|
||||
)
|
||||
}
|
||||
|
||||
function DataGridTableVirtualBody<TData>({
|
||||
function DataGridTableVirtualBody<TData extends object>({
|
||||
table,
|
||||
topRows,
|
||||
centerRows,
|
||||
@@ -236,9 +439,25 @@ function DataGridTableVirtualBody<TData>({
|
||||
allRowsLoadedMessage,
|
||||
measureRowRef,
|
||||
}: VirtualBodyProps<TData>) {
|
||||
const { isLoading } = useDataGrid()
|
||||
const totalRows = topRows.length + centerRows.length + bottomRows.length
|
||||
|
||||
if (!totalRows) return <DataGridTableEmpty />
|
||||
if (!totalRows) {
|
||||
// Initial load must not flash the empty state as if the query returned
|
||||
// nothing.
|
||||
if (isLoading) {
|
||||
return (
|
||||
<DataGridTableVirtualStatusRow table={table}>
|
||||
<div className="flex items-center justify-center gap-2">
|
||||
<Spinner className="size-4 opacity-60" />
|
||||
{loadingMoreMessage}
|
||||
</div>
|
||||
</DataGridTableVirtualStatusRow>
|
||||
)
|
||||
}
|
||||
|
||||
return <DataGridTableEmpty />
|
||||
}
|
||||
|
||||
const hasCenterRows = centerRows.length > 0
|
||||
const showFetchingRow = isInfiniteMode && isFetchingMore
|
||||
@@ -291,6 +510,7 @@ function DataGridTableVirtualBody<TData>({
|
||||
key={row.id}
|
||||
row={row}
|
||||
rowRef={measureRowRef}
|
||||
rowIndex={virtualRow.index}
|
||||
/>
|
||||
)
|
||||
})
|
||||
@@ -305,8 +525,10 @@ function DataGridTableVirtualBody<TData>({
|
||||
)
|
||||
}
|
||||
} else {
|
||||
centerRows.forEach((row) => {
|
||||
renderedRows.push(<DataGridTableRenderedRow key={row.id} row={row} />)
|
||||
centerRows.forEach((row, rowIndex) => {
|
||||
renderedRows.push(
|
||||
<DataGridTableRenderedRow key={row.id} row={row} rowIndex={rowIndex} />
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -357,13 +579,16 @@ function DataGridTableVirtualBody<TData>({
|
||||
*/
|
||||
const MemoizedVirtualBody = memo(
|
||||
DataGridTableVirtualBody,
|
||||
(_prev, next) => !!next.table.getState().columnSizingInfo.isResizingColumn
|
||||
(_prev, next) => !!next.table.state.columnResizing.isResizingColumn
|
||||
) as typeof DataGridTableVirtualBody
|
||||
|
||||
function DataGridTableVirtual<TData>({
|
||||
function DataGridTableVirtual<TData extends object>({
|
||||
height,
|
||||
estimateSize = 48,
|
||||
overscan = 10,
|
||||
scrollBehavior = "auto",
|
||||
scrollToRowAlign = "auto",
|
||||
scrollToRowIndex,
|
||||
footerContent,
|
||||
renderHeader = true,
|
||||
onFetchMore,
|
||||
@@ -372,7 +597,7 @@ function DataGridTableVirtual<TData>({
|
||||
fetchMoreOffset = 0,
|
||||
virtualizerOptions,
|
||||
}: DataGridTableVirtualProps<TData>) {
|
||||
const { table, props } = useDataGrid()
|
||||
const { table, props } = useDataGrid<TData>()
|
||||
const mergedHeaderGroups = getDataGridTableMergedHeaderGroups(table)
|
||||
const hasRightPinnedColumns = hasDataGridTableRightPinnedColumns(table)
|
||||
const { topRows, centerRows, bottomRows } = getDataGridTableRowSections(
|
||||
@@ -397,17 +622,16 @@ function DataGridTableVirtual<TData>({
|
||||
|
||||
const isVirtualizationEnabled = virtualizerOptions?.enabled !== false
|
||||
const loadingMoreMessage =
|
||||
props.fetchingMoreMessage || props.loadingMessage || "Загрузка…"
|
||||
props.fetchingMoreMessage || props.loadingMessage || "Loading..."
|
||||
const allRowsLoadedMessage =
|
||||
props.allRowsLoadedMessage || "Все записи загружены"
|
||||
props.allRowsLoadedMessage || "All records loaded"
|
||||
|
||||
const handleViewportRef = useCallback((node: HTMLDivElement | null) => {
|
||||
setViewportElements({
|
||||
containerElement: node,
|
||||
scrollElement:
|
||||
(node?.closest(
|
||||
'[data-slot="scroll-area-viewport"]'
|
||||
) as HTMLElement | null) ?? node,
|
||||
scrollElement: node
|
||||
? (getDataGridScrollAreaViewport(node) ?? node)
|
||||
: null,
|
||||
})
|
||||
}, [])
|
||||
|
||||
@@ -464,6 +688,134 @@ function DataGridTableVirtual<TData>({
|
||||
? virtualizer.measureElement
|
||||
: undefined
|
||||
const resolvedFetchMoreOffset = Math.max(0, fetchMoreOffset)
|
||||
const scrollToRowId =
|
||||
scrollToRowIndex !== undefined
|
||||
? centerRows[scrollToRowIndex]?.id
|
||||
: undefined
|
||||
const scrollToRowVirtualItem =
|
||||
isVirtualizationEnabled && scrollToRowIndex !== undefined
|
||||
? virtualItems.find((item) => item.index === scrollToRowIndex)
|
||||
: undefined
|
||||
const pendingScrollToRowIndexRef = useRef<number | null>(null)
|
||||
const lastScrollRequestRef = useRef<DataGridTableVirtualScrollRequest | null>(
|
||||
null
|
||||
)
|
||||
// Latch onFetchMore per row count: virtualItems gets a new identity every
|
||||
// scroll frame, so without it the effect fires duplicate page requests
|
||||
// before the consumer flips isFetchingMore, and loops at end-of-data when
|
||||
// hasMore is never set.
|
||||
const fetchMoreFiredAtCountRef = useRef<number | null>(null)
|
||||
|
||||
// Resolve after every commit so a stable getter can expose a replaced ref;
|
||||
// the request signature prevents duplicate scrolling on ordinary renders.
|
||||
useEffect(() => {
|
||||
const previousRequest = lastScrollRequestRef.current
|
||||
|
||||
if (
|
||||
scrollToRowIndex === undefined ||
|
||||
scrollToRowIndex < 0 ||
|
||||
scrollToRowIndex >= centerRows.length
|
||||
) {
|
||||
pendingScrollToRowIndexRef.current = null
|
||||
lastScrollRequestRef.current = null
|
||||
|
||||
if (previousRequest) {
|
||||
const scrollElement = resolveScrollElement()
|
||||
|
||||
if (scrollElement) {
|
||||
scrollDataGridTableToOffset({
|
||||
behavior: "auto",
|
||||
scrollElement,
|
||||
targetTop: scrollElement.scrollTop,
|
||||
virtualizer: isVirtualizationEnabled ? virtualizer : undefined,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
const scrollElement = resolveScrollElement()
|
||||
const containerElement = viewportElements.containerElement
|
||||
if (!containerElement || !scrollElement) return
|
||||
|
||||
const headerSticky = renderHeader && !!props.tableLayout?.headerSticky
|
||||
const nextRequest: DataGridTableVirtualScrollRequest = {
|
||||
align: scrollToRowAlign,
|
||||
behavior: scrollBehavior,
|
||||
containerElement,
|
||||
headerSticky,
|
||||
isVirtualizationEnabled,
|
||||
rowId: scrollToRowId,
|
||||
rowIndex: scrollToRowIndex,
|
||||
scrollElement,
|
||||
}
|
||||
|
||||
if (isSameDataGridTableScrollRequest(previousRequest, nextRequest)) return
|
||||
|
||||
pendingScrollToRowIndexRef.current = null
|
||||
|
||||
const rowWasHandled = scrollDataGridTableRowIntoView({
|
||||
align: scrollToRowAlign,
|
||||
behavior: scrollBehavior,
|
||||
cancelPendingScroll: previousRequest !== null,
|
||||
containerElement,
|
||||
headerSticky,
|
||||
rowIndex: scrollToRowIndex,
|
||||
scrollElement,
|
||||
virtualizer: isVirtualizationEnabled ? virtualizer : undefined,
|
||||
})
|
||||
|
||||
if (rowWasHandled) {
|
||||
lastScrollRequestRef.current = nextRequest
|
||||
return
|
||||
}
|
||||
|
||||
if (!isVirtualizationEnabled) return
|
||||
|
||||
pendingScrollToRowIndexRef.current = scrollToRowIndex
|
||||
lastScrollRequestRef.current = nextRequest
|
||||
virtualizer.scrollToIndex(scrollToRowIndex, {
|
||||
align: scrollToRowAlign,
|
||||
behavior: scrollBehavior,
|
||||
})
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
!isVirtualizationEnabled ||
|
||||
scrollToRowIndex === undefined ||
|
||||
pendingScrollToRowIndexRef.current !== scrollToRowIndex ||
|
||||
!scrollToRowVirtualItem
|
||||
) {
|
||||
return
|
||||
}
|
||||
|
||||
const rowWasHandled = scrollDataGridTableRowIntoView({
|
||||
align: scrollToRowAlign,
|
||||
behavior: "auto",
|
||||
cancelPendingScroll: true,
|
||||
containerElement: viewportElements.containerElement,
|
||||
headerSticky: renderHeader && !!props.tableLayout?.headerSticky,
|
||||
rowIndex: scrollToRowIndex,
|
||||
scrollElement: resolveScrollElement(),
|
||||
virtualizer,
|
||||
})
|
||||
|
||||
if (rowWasHandled) {
|
||||
pendingScrollToRowIndexRef.current = null
|
||||
}
|
||||
}, [
|
||||
isVirtualizationEnabled,
|
||||
props.tableLayout?.headerSticky,
|
||||
renderHeader,
|
||||
resolveScrollElement,
|
||||
scrollToRowAlign,
|
||||
scrollToRowIndex,
|
||||
scrollToRowVirtualItem,
|
||||
virtualizer,
|
||||
viewportElements.containerElement,
|
||||
])
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
@@ -478,7 +830,10 @@ function DataGridTableVirtual<TData>({
|
||||
const lastItem = virtualItems[virtualItems.length - 1]
|
||||
if (!lastItem) return
|
||||
|
||||
if (fetchMoreFiredAtCountRef.current === centerRows.length) return
|
||||
|
||||
if (lastItem.index >= centerRows.length - 1 - resolvedFetchMoreOffset) {
|
||||
fetchMoreFiredAtCountRef.current = centerRows.length
|
||||
onFetchMore?.()
|
||||
}
|
||||
}, [
|
||||
@@ -499,7 +854,15 @@ function DataGridTableVirtual<TData>({
|
||||
style={
|
||||
usesExternalScrollArea
|
||||
? undefined
|
||||
: { height, overflow: "auto", position: "relative" }
|
||||
: {
|
||||
height,
|
||||
overflow: "auto",
|
||||
position: "relative",
|
||||
// Standalone mode: this node IS the scroll container, so it
|
||||
// must stay at its parent's width (not the resizable table
|
||||
// width) or horizontal scrolling becomes impossible.
|
||||
width: "auto",
|
||||
}
|
||||
}
|
||||
>
|
||||
<DataGridTableBase>
|
||||
@@ -508,7 +871,7 @@ function DataGridTableVirtual<TData>({
|
||||
{mergedHeaderGroups.map((headerGroup) => (
|
||||
<DataGridTableHeadRow key={headerGroup.id} rowId={headerGroup.id}>
|
||||
{headerGroup.headers
|
||||
.filter((header) => header.column.getIsPinned() !== "right")
|
||||
.filter((header) => header.column.getIsPinned() !== "end")
|
||||
.map((header) => {
|
||||
const { column } = header
|
||||
|
||||
@@ -532,7 +895,7 @@ function DataGridTableVirtual<TData>({
|
||||
<DataGridTableFillHeadCell />
|
||||
) : null}
|
||||
{headerGroup.headers
|
||||
.filter((header) => header.column.getIsPinned() === "right")
|
||||
.filter((header) => header.column.getIsPinned() === "end")
|
||||
.map((header) => {
|
||||
const { column } = header
|
||||
|
||||
@@ -593,6 +956,7 @@ function DataGridTableVirtual<TData>({
|
||||
|
||||
export { DataGridTableVirtual }
|
||||
export type {
|
||||
DataGridTableVirtualScrollAlignment,
|
||||
DataGridTableVirtualProps,
|
||||
DataGridTableVirtualScrollElements,
|
||||
DataGridTableVirtualizerOptions,
|
||||
|
||||
Reference in New Issue
Block a user