Refactor ObjectTable component to improve data handling and loading logic. Introduce skeleton page creation for better user experience during data fetching. Optimize page management and scrolling behavior, enhancing performance and maintainability.
All checks were successful
farmcontrol/farmcontrol-ui/pipeline/head This commit looks good
All checks were successful
farmcontrol/farmcontrol-ui/pipeline/head This commit looks good
This commit is contained in:
parent
cd30be7677
commit
edce328880
@ -3,6 +3,7 @@ import {
|
|||||||
useImperativeHandle,
|
useImperativeHandle,
|
||||||
useRef,
|
useRef,
|
||||||
useEffect,
|
useEffect,
|
||||||
|
useLayoutEffect,
|
||||||
useState,
|
useState,
|
||||||
useCallback,
|
useCallback,
|
||||||
useMemo,
|
useMemo,
|
||||||
@ -49,6 +50,7 @@ const logger = loglevel.getLogger('DasboardTable')
|
|||||||
logger.setLevel(config.logLevel)
|
logger.setLevel(config.logLevel)
|
||||||
|
|
||||||
const SCROLL_THRESHOLD = 50
|
const SCROLL_THRESHOLD = 50
|
||||||
|
const SKELETON_HEIGHT = 49
|
||||||
|
|
||||||
const RowForm = ({ record, isEditing, onRegister, children }) => {
|
const RowForm = ({ record, isEditing, onRegister, children }) => {
|
||||||
const [form] = Form.useForm()
|
const [form] = Form.useForm()
|
||||||
@ -152,10 +154,12 @@ const ObjectTable = forwardRef(
|
|||||||
// Table state
|
// Table state
|
||||||
const [pages, setPages] = useState([])
|
const [pages, setPages] = useState([])
|
||||||
const pagesRef = useRef(pages)
|
const pagesRef = useRef(pages)
|
||||||
const [hasMore, setHasMore] = useState(true)
|
const tableData = useMemo(
|
||||||
|
() => pages.flatMap((page) => page.items),
|
||||||
|
[pages]
|
||||||
|
)
|
||||||
const [loading, setLoading] = useState(true)
|
const [loading, setLoading] = useState(true)
|
||||||
const [lazyLoading, setLazyLoading] = useState(false)
|
const [lazyLoading, setLazyLoading] = useState(false)
|
||||||
const [tableData, setTableData] = useState([])
|
|
||||||
|
|
||||||
const [isEditing, setIsEditing] = useState(false)
|
const [isEditing, setIsEditing] = useState(false)
|
||||||
const [editLoading, setEditLoading] = useState(false)
|
const [editLoading, setEditLoading] = useState(false)
|
||||||
@ -182,37 +186,26 @@ const ObjectTable = forwardRef(
|
|||||||
const rowActions =
|
const rowActions =
|
||||||
model.actions?.filter((action) => action.row == true) || []
|
model.actions?.filter((action) => action.row == true) || []
|
||||||
|
|
||||||
const createSkeletonData = useCallback(() => {
|
const createSkeletonData = useCallback((pageNum) => {
|
||||||
return Array(pageSize)
|
return Array(pageSize)
|
||||||
.fill(null)
|
.fill(null)
|
||||||
.map(() => ({
|
.map((_, index) => ({
|
||||||
_id: `skeleton-${Math.random().toString(36).substring(2, 15)}`,
|
_id: `skeleton-${pageNum}-${index}`,
|
||||||
isSkeleton: true
|
isSkeleton: true
|
||||||
}))
|
}))
|
||||||
}, [pageSize])
|
}, [pageSize])
|
||||||
|
|
||||||
const scrollOperationTimingOut = useRef(false)
|
const createSkeletonPage = useCallback(
|
||||||
const scrollOperationTimeout = useRef(null)
|
(pageNum) => ({
|
||||||
|
pageNum,
|
||||||
|
items: createSkeletonData(pageNum),
|
||||||
|
isSkeletonPage: true
|
||||||
|
}),
|
||||||
|
[createSkeletonData]
|
||||||
|
)
|
||||||
|
|
||||||
const runScrollOperation = useCallback((operation) => {
|
const loadingPagesRef = useRef(new Set())
|
||||||
if (scrollOperationTimingOut.current) return
|
const pendingScrollAnchorRef = useRef(null)
|
||||||
|
|
||||||
scrollOperationTimingOut.current = true
|
|
||||||
operation()
|
|
||||||
|
|
||||||
scrollOperationTimeout.current = setTimeout(() => {
|
|
||||||
scrollOperationTimingOut.current = false
|
|
||||||
scrollOperationTimeout.current = null
|
|
||||||
}, 150)
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
return () => {
|
|
||||||
if (scrollOperationTimeout.current) {
|
|
||||||
clearTimeout(scrollOperationTimeout.current)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
const renderActions = (objectData) => {
|
const renderActions = (objectData) => {
|
||||||
return (
|
return (
|
||||||
@ -255,7 +248,7 @@ const ObjectTable = forwardRef(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const fetchData = useCallback(
|
const fetchPage = useCallback(
|
||||||
async (pageNum = 1, filter = null, sorter = null) => {
|
async (pageNum = 1, filter = null, sorter = null) => {
|
||||||
if (filter == null) {
|
if (filter == null) {
|
||||||
filter = activeFilterRef.current
|
filter = activeFilterRef.current
|
||||||
@ -270,148 +263,266 @@ const ObjectTable = forwardRef(
|
|||||||
order: sorter.order
|
order: sorter.order
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
try {
|
return fetchObjects(type, {
|
||||||
const result = await fetchObjects(type, {
|
page: pageNum,
|
||||||
page: pageNum,
|
limit: pageSize,
|
||||||
limit: pageSize,
|
filter: { ...filter, ...masterFilter },
|
||||||
filter: { ...filter, ...masterFilter },
|
sorter,
|
||||||
sorter,
|
onDataChange
|
||||||
onDataChange
|
})
|
||||||
})
|
|
||||||
|
|
||||||
setHasMore(result.hasMore)
|
|
||||||
|
|
||||||
setPages((prev) => {
|
|
||||||
const existingPageIndex = prev.findIndex(
|
|
||||||
(p) => p.pageNum === pageNum
|
|
||||||
)
|
|
||||||
if (existingPageIndex !== -1) {
|
|
||||||
// Update existing page
|
|
||||||
const newPages = [...prev]
|
|
||||||
newPages[existingPageIndex] = { pageNum, items: result.data }
|
|
||||||
return newPages
|
|
||||||
}
|
|
||||||
// If page doesn't exist, return unchanged
|
|
||||||
return prev
|
|
||||||
})
|
|
||||||
|
|
||||||
setLoading(false)
|
|
||||||
setLazyLoading(false)
|
|
||||||
return result.data || []
|
|
||||||
} catch (error) {
|
|
||||||
setPages((prev) =>
|
|
||||||
prev.map((page) => ({
|
|
||||||
...page,
|
|
||||||
items: page.items.filter((item) => !item.isSkeleton)
|
|
||||||
}))
|
|
||||||
)
|
|
||||||
setLoading(false)
|
|
||||||
setLazyLoading(false)
|
|
||||||
throw error
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
[type, masterFilter, pageSize, onDataChange, fetchObjects]
|
[type, masterFilter, pageSize, onDataChange, fetchObjects]
|
||||||
)
|
)
|
||||||
|
|
||||||
const loadNextPage = useCallback(() => {
|
const skeletonsHeight = useMemo(() => {
|
||||||
const highestPage = Math.max(...pages.map((p) => p.pageNum))
|
return SKELETON_HEIGHT * pageSize
|
||||||
const nextPage = highestPage + 1
|
}, [pageSize])
|
||||||
if (hasMore && lazyLoading == false) {
|
|
||||||
setPages((prev) => {
|
|
||||||
const filteredPages = prev.map((page) => ({
|
|
||||||
...page,
|
|
||||||
items: page.items.filter((item) => !item.isSkeleton)
|
|
||||||
}))
|
|
||||||
const minPage = Math.min(...filteredPages.map((p) => p.pageNum))
|
|
||||||
const relevantPages = filteredPages.filter(
|
|
||||||
(p) => p.pageNum !== minPage
|
|
||||||
)
|
|
||||||
return [
|
|
||||||
...relevantPages,
|
|
||||||
{ pageNum: nextPage, items: createSkeletonData() }
|
|
||||||
]
|
|
||||||
})
|
|
||||||
fetchData(nextPage)
|
|
||||||
}
|
|
||||||
}, [pages, createSkeletonData, fetchData, hasMore, lazyLoading])
|
|
||||||
|
|
||||||
const loadPreviousPage = useCallback(() => {
|
const createPageWindow = useCallback(
|
||||||
const lowestPage = Math.min(...pages.map((p) => p.pageNum))
|
(loadedPages, direction) => {
|
||||||
const prevPage = lowestPage - 1
|
const sortedPages = [...loadedPages].sort(
|
||||||
|
(a, b) => a.pageNum - b.pageNum
|
||||||
|
)
|
||||||
|
const visiblePages =
|
||||||
|
direction === 'previous'
|
||||||
|
? sortedPages.slice(0, 2)
|
||||||
|
: sortedPages.slice(-2)
|
||||||
|
const firstPage = visiblePages[0]
|
||||||
|
const lastPage = visiblePages[visiblePages.length - 1]
|
||||||
|
const nextPages = []
|
||||||
|
|
||||||
if (prevPage > 0 && lazyLoading == false) {
|
if (firstPage?.pageNum > 1) {
|
||||||
setPages((prev) => {
|
nextPages.push(createSkeletonPage(firstPage.pageNum - 1))
|
||||||
const filteredPages = prev.map((page) => ({
|
}
|
||||||
...page,
|
nextPages.push(...visiblePages)
|
||||||
items: page.items.filter((item) => !item.isSkeleton)
|
if (lastPage?.hasMore) {
|
||||||
}))
|
nextPages.push(createSkeletonPage(lastPage.pageNum + 1))
|
||||||
const maxPage = Math.max(...filteredPages.map((p) => p.pageNum))
|
}
|
||||||
const relevantPages = filteredPages.filter(
|
|
||||||
(p) => p.pageNum !== maxPage
|
|
||||||
)
|
|
||||||
return [
|
|
||||||
{ pageNum: prevPage, items: createSkeletonData() },
|
|
||||||
...relevantPages
|
|
||||||
]
|
|
||||||
})
|
|
||||||
fetchData(prevPage)
|
|
||||||
}
|
|
||||||
}, [pages, createSkeletonData, fetchData, lazyLoading])
|
|
||||||
|
|
||||||
const scrollDown = useCallback(
|
return nextPages
|
||||||
(target, scrollHeight) => {
|
|
||||||
setTimeout(() => {
|
|
||||||
target.scrollTop = scrollHeight / 2 - target.clientHeight
|
|
||||||
}, 0)
|
|
||||||
setLazyLoading(true)
|
|
||||||
logger.debug('Loading next page...')
|
|
||||||
loadNextPage()
|
|
||||||
},
|
},
|
||||||
[loadNextPage]
|
[createSkeletonPage]
|
||||||
)
|
)
|
||||||
|
|
||||||
const scrollUp = useCallback(
|
const fetchData = useCallback(
|
||||||
(target, scrollHeight) => {
|
async (pageNum = 1, filter = null, sorter = null) => {
|
||||||
setTimeout(() => {
|
try {
|
||||||
target.scrollTop = scrollHeight / 2
|
const result = await fetchPage(pageNum, filter, sorter)
|
||||||
}, 0)
|
const loadedPage = {
|
||||||
setLazyLoading(true)
|
pageNum,
|
||||||
logger.debug('Loading previous page...')
|
items: result.data || [],
|
||||||
loadPreviousPage()
|
hasMore: result.hasMore
|
||||||
|
}
|
||||||
|
setPages((prev) =>
|
||||||
|
prev.map((page) =>
|
||||||
|
page.pageNum === pageNum ? loadedPage : page
|
||||||
|
)
|
||||||
|
)
|
||||||
|
setLoading(false)
|
||||||
|
return result.data || []
|
||||||
|
} catch (error) {
|
||||||
|
setLoading(false)
|
||||||
|
throw error
|
||||||
|
}
|
||||||
},
|
},
|
||||||
[loadPreviousPage]
|
[fetchPage]
|
||||||
|
)
|
||||||
|
|
||||||
|
const findRenderedRow = useCallback((scrollTarget, id) => {
|
||||||
|
if (!scrollTarget || id == null) return null
|
||||||
|
return Array.from(
|
||||||
|
scrollTarget.querySelectorAll('tr[data-row-key]')
|
||||||
|
).find((row) => row.getAttribute('data-row-key') === String(id))
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const captureScrollAnchor = useCallback(
|
||||||
|
(scrollTarget, placeholderPage, direction, currentPages) => {
|
||||||
|
if (!scrollTarget) return null
|
||||||
|
|
||||||
|
const targetTop = scrollTarget.getBoundingClientRect().top
|
||||||
|
const closestRow = (items) => {
|
||||||
|
return items
|
||||||
|
.map((item, index) => {
|
||||||
|
const element = findRenderedRow(scrollTarget, item._id)
|
||||||
|
return element
|
||||||
|
? {
|
||||||
|
index,
|
||||||
|
id: item._id,
|
||||||
|
top: element.getBoundingClientRect().top
|
||||||
|
}
|
||||||
|
: null
|
||||||
|
})
|
||||||
|
.filter(Boolean)
|
||||||
|
.sort(
|
||||||
|
(a, b) =>
|
||||||
|
Math.abs(a.top - targetTop) - Math.abs(b.top - targetTop)
|
||||||
|
)[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
const placeholderAnchor = closestRow(placeholderPage.items)
|
||||||
|
const loadedPages = currentPages.filter(
|
||||||
|
(page) => !page.isSkeletonPage
|
||||||
|
)
|
||||||
|
const retainedPage =
|
||||||
|
direction === 'previous'
|
||||||
|
? loadedPages[0]
|
||||||
|
: loadedPages[loadedPages.length - 1]
|
||||||
|
const fallbackAnchor = retainedPage
|
||||||
|
? closestRow(retainedPage.items)
|
||||||
|
: null
|
||||||
|
|
||||||
|
return { placeholderAnchor, fallbackAnchor }
|
||||||
|
},
|
||||||
|
[findRenderedRow]
|
||||||
|
)
|
||||||
|
|
||||||
|
const restoreScrollAnchor = useCallback(
|
||||||
|
(scrollTarget, anchor, loadedPage) => {
|
||||||
|
if (!scrollTarget || !anchor) return
|
||||||
|
|
||||||
|
const loadedItem =
|
||||||
|
loadedPage.items[anchor.placeholderAnchor?.index]
|
||||||
|
const anchorId = loadedItem?._id ?? anchor.fallbackAnchor?.id
|
||||||
|
const previousTop = loadedItem
|
||||||
|
? anchor.placeholderAnchor?.top
|
||||||
|
: anchor.fallbackAnchor?.top
|
||||||
|
if (anchorId == null || previousTop == null) return
|
||||||
|
|
||||||
|
const element = findRenderedRow(scrollTarget, anchorId)
|
||||||
|
if (!element) return
|
||||||
|
const renderedTop = element.getBoundingClientRect().top
|
||||||
|
scrollTarget.scrollTop += renderedTop - previousTop
|
||||||
|
},
|
||||||
|
[findRenderedRow]
|
||||||
|
)
|
||||||
|
|
||||||
|
useLayoutEffect(() => {
|
||||||
|
const pendingAnchor = pendingScrollAnchorRef.current
|
||||||
|
if (!pendingAnchor) return
|
||||||
|
pendingScrollAnchorRef.current = null
|
||||||
|
restoreScrollAnchor(
|
||||||
|
pendingAnchor.scrollTarget,
|
||||||
|
pendingAnchor.anchor,
|
||||||
|
pendingAnchor.loadedPage
|
||||||
|
)
|
||||||
|
}, [pages, restoreScrollAnchor])
|
||||||
|
|
||||||
|
const loadBoundaryPage = useCallback(
|
||||||
|
async (pageNum, direction, scrollTarget) => {
|
||||||
|
if (loadingPagesRef.current.size > 0) return
|
||||||
|
|
||||||
|
const currentPages = pagesRef.current
|
||||||
|
const placeholderPage = currentPages.find(
|
||||||
|
(page) => page.pageNum === pageNum && page.isSkeletonPage
|
||||||
|
)
|
||||||
|
if (!placeholderPage) return
|
||||||
|
const scrollAnchor = captureScrollAnchor(
|
||||||
|
scrollTarget,
|
||||||
|
placeholderPage,
|
||||||
|
direction,
|
||||||
|
currentPages
|
||||||
|
)
|
||||||
|
|
||||||
|
loadingPagesRef.current.add(pageNum)
|
||||||
|
setLazyLoading(true)
|
||||||
|
logger.debug(`Loading ${direction} page...`)
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = await fetchPage(pageNum)
|
||||||
|
const loadedPage = {
|
||||||
|
pageNum,
|
||||||
|
items: result.data || [],
|
||||||
|
hasMore: result.hasMore
|
||||||
|
}
|
||||||
|
|
||||||
|
pendingScrollAnchorRef.current = {
|
||||||
|
scrollTarget,
|
||||||
|
anchor: scrollAnchor,
|
||||||
|
loadedPage
|
||||||
|
}
|
||||||
|
setPages((prev) => {
|
||||||
|
if (
|
||||||
|
!prev.some(
|
||||||
|
(page) => page.pageNum === pageNum && page.isSkeletonPage
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return prev
|
||||||
|
}
|
||||||
|
|
||||||
|
const loadedPages = prev
|
||||||
|
.filter((page) => !page.isSkeletonPage)
|
||||||
|
.concat(loadedPage)
|
||||||
|
return createPageWindow(loadedPages, direction)
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
logger.error(`Error loading page ${pageNum}:`, error)
|
||||||
|
} finally {
|
||||||
|
loadingPagesRef.current.delete(pageNum)
|
||||||
|
if (loadingPagesRef.current.size === 0) {
|
||||||
|
setLazyLoading(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[
|
||||||
|
captureScrollAnchor,
|
||||||
|
createPageWindow,
|
||||||
|
fetchPage
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
const loadNextPage = useCallback(
|
||||||
|
(scrollTarget) => {
|
||||||
|
const nextPage = pagesRef.current[pagesRef.current.length - 1]
|
||||||
|
if (nextPage?.isSkeletonPage) {
|
||||||
|
loadBoundaryPage(nextPage.pageNum, 'next', scrollTarget)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[loadBoundaryPage]
|
||||||
|
)
|
||||||
|
|
||||||
|
const loadPreviousPage = useCallback(
|
||||||
|
(scrollTarget) => {
|
||||||
|
const previousPage = pagesRef.current[0]
|
||||||
|
if (previousPage?.isSkeletonPage) {
|
||||||
|
loadBoundaryPage(previousPage.pageNum, 'previous', scrollTarget)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[loadBoundaryPage]
|
||||||
)
|
)
|
||||||
|
|
||||||
const handleScroll = useCallback(
|
const handleScroll = useCallback(
|
||||||
(e) => {
|
(e) => {
|
||||||
const { target } = e
|
const { target } = e
|
||||||
const scrollHeight = target.scrollHeight
|
const firstPage = pagesRef.current[0]
|
||||||
const scrollTop = target.scrollTop
|
const lastPage = pagesRef.current[pagesRef.current.length - 1]
|
||||||
const clientHeight = target.clientHeight
|
|
||||||
const lowestPage = Math.min(...pages.map((p) => p.pageNum))
|
|
||||||
const prevPage = lowestPage - 1
|
|
||||||
|
|
||||||
// Load more data when scrolling down
|
|
||||||
if (
|
if (
|
||||||
scrollHeight - scrollTop - clientHeight < SCROLL_THRESHOLD &&
|
lastPage?.isSkeletonPage &&
|
||||||
hasMore
|
target.scrollHeight - target.scrollTop - target.clientHeight <=
|
||||||
|
skeletonsHeight + SCROLL_THRESHOLD
|
||||||
) {
|
) {
|
||||||
runScrollOperation(() => scrollDown(target, scrollHeight))
|
loadNextPage(target)
|
||||||
}
|
} else if (
|
||||||
|
firstPage?.isSkeletonPage &&
|
||||||
// Load previous data when scrolling up
|
target.scrollTop <= skeletonsHeight + SCROLL_THRESHOLD
|
||||||
if (scrollTop < SCROLL_THRESHOLD && prevPage > 0) {
|
) {
|
||||||
runScrollOperation(() => scrollUp(target, scrollHeight))
|
loadPreviousPage(target)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[hasMore, pages, runScrollOperation, scrollDown, scrollUp]
|
[loadNextPage, loadPreviousPage, skeletonsHeight]
|
||||||
)
|
)
|
||||||
|
|
||||||
const reload = useCallback(async () => {
|
const reload = useCallback(async () => {
|
||||||
setLazyLoading(true)
|
setLazyLoading(true)
|
||||||
for (let i = 0; i < pagesRef.current.length; i++) {
|
try {
|
||||||
const page = pagesRef.current[i]
|
const loadedPages = pagesRef.current.filter(
|
||||||
await fetchData(page.pageNum)
|
(page) => !page.isSkeletonPage
|
||||||
|
)
|
||||||
|
for (let i = 0; i < loadedPages.length; i++) {
|
||||||
|
await fetchData(loadedPages[i].pageNum)
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
setLazyLoading(false)
|
||||||
}
|
}
|
||||||
}, [fetchData])
|
}, [fetchData])
|
||||||
|
|
||||||
@ -601,24 +712,41 @@ const ObjectTable = forwardRef(
|
|||||||
|
|
||||||
const loadPage = useCallback(
|
const loadPage = useCallback(
|
||||||
async (pageNum, filter = null, sorter = null) => {
|
async (pageNum, filter = null, sorter = null) => {
|
||||||
// Create initial page with skeletons
|
setPages([createSkeletonPage(pageNum)])
|
||||||
setPages([{ pageNum: pageNum, items: createSkeletonData() }])
|
setLoading(true)
|
||||||
|
|
||||||
const items = await fetchData(pageNum, filter, sorter)
|
try {
|
||||||
|
const firstResult = await fetchPage(pageNum, filter, sorter)
|
||||||
|
const loadedPages = [
|
||||||
|
{
|
||||||
|
pageNum,
|
||||||
|
items: firstResult.data || [],
|
||||||
|
hasMore: firstResult.hasMore
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
if (items.length >= 25) {
|
if (firstResult.hasMore) {
|
||||||
setPages((prev) => {
|
setPages([
|
||||||
// Remove any existing page with the same pageNum
|
...loadedPages,
|
||||||
const filtered = prev.filter((p) => p.pageNum !== pageNum + 1)
|
createSkeletonPage(pageNum + 1)
|
||||||
return [
|
])
|
||||||
...filtered,
|
const secondResult = await fetchPage(pageNum + 1)
|
||||||
{ pageNum: pageNum + 1, items: createSkeletonData() }
|
loadedPages.push({
|
||||||
]
|
pageNum: pageNum + 1,
|
||||||
})
|
items: secondResult.data || [],
|
||||||
await fetchData(pageNum + 1, filter, sorter)
|
hasMore: secondResult.hasMore
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
setPages(createPageWindow(loadedPages, 'next'))
|
||||||
|
} catch (error) {
|
||||||
|
logger.error(`Error loading page ${pageNum}:`, error)
|
||||||
|
setPages([])
|
||||||
|
} finally {
|
||||||
|
setLoading(false)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[createSkeletonData, fetchData]
|
[createPageWindow, createSkeletonPage, fetchPage]
|
||||||
)
|
)
|
||||||
|
|
||||||
const loadInitialPage = useCallback(async () => {
|
const loadInitialPage = useCallback(async () => {
|
||||||
@ -667,7 +795,6 @@ const ObjectTable = forwardRef(
|
|||||||
setInitialized(false)
|
setInitialized(false)
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
setLazyLoading(false)
|
setLazyLoading(false)
|
||||||
setHasMore(true)
|
|
||||||
prevValuesRef.current = { type, masterFilter }
|
prevValuesRef.current = { type, masterFilter }
|
||||||
}
|
}
|
||||||
}, [type, masterFilter])
|
}, [type, masterFilter])
|
||||||
@ -727,8 +854,6 @@ const ObjectTable = forwardRef(
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
console.log('filters--', filters)
|
|
||||||
|
|
||||||
setSidebarFilter(next)
|
setSidebarFilter(next)
|
||||||
setPages([])
|
setPages([])
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
@ -766,11 +891,6 @@ const ObjectTable = forwardRef(
|
|||||||
pagesRef.current = pages
|
pagesRef.current = pages
|
||||||
}, [pages])
|
}, [pages])
|
||||||
|
|
||||||
// Flatten pages array for table display
|
|
||||||
useEffect(() => {
|
|
||||||
setTableData(pages.flatMap((page) => page.items))
|
|
||||||
}, [pages])
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!expandHeight || cards) return
|
if (!expandHeight || cards) return
|
||||||
|
|
||||||
@ -911,35 +1031,27 @@ const ObjectTable = forwardRef(
|
|||||||
|
|
||||||
const handleCardsScroll = (e) => {
|
const handleCardsScroll = (e) => {
|
||||||
const { scrollTop, scrollHeight, clientHeight } = e.target
|
const { scrollTop, scrollHeight, clientHeight } = e.target
|
||||||
const lowestPage = Math.min(...pages.map((p) => p.pageNum))
|
const firstPage = pagesRef.current[0]
|
||||||
const prevPage = lowestPage - 1
|
const lastPage = pagesRef.current[pagesRef.current.length - 1]
|
||||||
|
|
||||||
// Load more data when scrolling down
|
|
||||||
if (
|
if (
|
||||||
|
lastPage?.isSkeletonPage &&
|
||||||
scrollHeight - scrollTop - clientHeight < 100 &&
|
scrollHeight - scrollTop - clientHeight < 100 &&
|
||||||
hasMore &&
|
|
||||||
!lazyLoading
|
!lazyLoading
|
||||||
) {
|
) {
|
||||||
setTimeout(() => {
|
|
||||||
e.target.scrollTop = scrollHeight / 2
|
|
||||||
}, 0)
|
|
||||||
setLazyLoading(true)
|
|
||||||
loadNextPage()
|
loadNextPage()
|
||||||
}
|
} else if (
|
||||||
|
firstPage?.isSkeletonPage &&
|
||||||
// Load previous data when scrolling up
|
scrollTop < 100 &&
|
||||||
if (scrollTop < 100 && prevPage > 0 && !lazyLoading) {
|
!lazyLoading
|
||||||
setTimeout(() => {
|
) {
|
||||||
e.target.scrollTop = scrollHeight / 2
|
|
||||||
}, 0)
|
|
||||||
setLazyLoading(true)
|
|
||||||
loadPreviousPage()
|
loadPreviousPage()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
container.addEventListener('scroll', handleCardsScroll)
|
container.addEventListener('scroll', handleCardsScroll)
|
||||||
return () => container.removeEventListener('scroll', handleCardsScroll)
|
return () => container.removeEventListener('scroll', handleCardsScroll)
|
||||||
}, [cards, pages, hasMore, lazyLoading, loadNextPage, loadPreviousPage])
|
}, [cards, pages, lazyLoading, loadNextPage, loadPreviousPage])
|
||||||
|
|
||||||
const renderCards = () => {
|
const renderCards = () => {
|
||||||
return (
|
return (
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user