feat(core): add column pinning and grouping features to Griddy table
- Implement column pinning functionality allowing users to pin columns to the left or right. - Introduce data grouping capabilities for better data organization. - Enhance the theming guide with new styles for pinned columns and loading indicators. - Add infinite scroll support with loading indicators for improved user experience. - Update CSS styles to accommodate new features and improve visual feedback.
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { useEffect } from 'react'
|
||||
import { useEffect, useRef } from 'react'
|
||||
|
||||
import { CSS } from '../core/constants'
|
||||
import { useGriddyStore } from '../core/GriddyStore'
|
||||
@@ -9,11 +9,15 @@ export function VirtualBody() {
|
||||
const table = useGriddyStore((s) => s._table)
|
||||
const virtualizer = useGriddyStore((s) => s._virtualizer)
|
||||
const setTotalRows = useGriddyStore((s) => s.setTotalRows)
|
||||
const infiniteScroll = useGriddyStore((s) => s.infiniteScroll)
|
||||
|
||||
const rows = table?.getRowModel().rows
|
||||
const virtualRows = virtualizer?.getVirtualItems()
|
||||
const totalSize = virtualizer?.getTotalSize() ?? 0
|
||||
|
||||
// Track if we're currently loading to prevent multiple simultaneous calls
|
||||
const isLoadingRef = useRef(false)
|
||||
|
||||
// Sync row count to store for keyboard navigation bounds
|
||||
useEffect(() => {
|
||||
if (rows) {
|
||||
@@ -21,8 +25,45 @@ export function VirtualBody() {
|
||||
}
|
||||
}, [rows?.length, setTotalRows])
|
||||
|
||||
// Infinite scroll: detect when approaching the end
|
||||
useEffect(() => {
|
||||
if (!infiniteScroll?.enabled || !infiniteScroll.onLoadMore || !virtualRows || !rows) {
|
||||
return
|
||||
}
|
||||
|
||||
const { threshold = 10, hasMore = true, isLoading = false } = infiniteScroll
|
||||
|
||||
// Don't trigger if already loading or no more data
|
||||
if (isLoading || !hasMore || isLoadingRef.current) {
|
||||
return
|
||||
}
|
||||
|
||||
// Check if the last rendered virtual row is within threshold of the end
|
||||
const lastVirtualRow = virtualRows[virtualRows.length - 1]
|
||||
if (!lastVirtualRow) return
|
||||
|
||||
const lastVirtualIndex = lastVirtualRow.index
|
||||
const totalRows = rows.length
|
||||
const distanceFromEnd = totalRows - lastVirtualIndex - 1
|
||||
|
||||
if (distanceFromEnd <= threshold) {
|
||||
isLoadingRef.current = true
|
||||
const loadPromise = infiniteScroll.onLoadMore()
|
||||
|
||||
if (loadPromise instanceof Promise) {
|
||||
loadPromise.finally(() => {
|
||||
isLoadingRef.current = false
|
||||
})
|
||||
} else {
|
||||
isLoadingRef.current = false
|
||||
}
|
||||
}
|
||||
}, [virtualRows, rows, infiniteScroll])
|
||||
|
||||
if (!table || !virtualizer || !rows || !virtualRows) return null
|
||||
|
||||
const showLoadingIndicator = infiniteScroll?.enabled && infiniteScroll.isLoading
|
||||
|
||||
return (
|
||||
<div
|
||||
className={styles[CSS.tbody]}
|
||||
@@ -46,6 +87,19 @@ export function VirtualBody() {
|
||||
/>
|
||||
)
|
||||
})}
|
||||
{showLoadingIndicator && (
|
||||
<div
|
||||
className={styles['griddy-loading-indicator']}
|
||||
style={{
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
position: 'absolute',
|
||||
right: 0,
|
||||
}}
|
||||
>
|
||||
<div className={styles['griddy-loading-spinner']}>Loading more...</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user