diff --git a/src/components/Dashboard/common/ObjectCard.jsx b/src/components/Dashboard/common/ObjectCard.jsx index 8ebdb47..1959aab 100644 --- a/src/components/Dashboard/common/ObjectCard.jsx +++ b/src/components/Dashboard/common/ObjectCard.jsx @@ -1,10 +1,11 @@ -import { Descriptions, Card, Flex, Divider } from 'antd' +import { Descriptions, Card, Flex, Divider, Skeleton } from 'antd' import PropTypes from 'prop-types' import ObjectProperty from './ObjectProperty' import { createElement } from 'react' import Thumbnail from './Thumbnail' const ObjectCard = ({ + isSkeleton = false, model, modelProperties, visibleColumns = {}, @@ -40,7 +41,9 @@ const ObjectCard = ({ key: prop.name, label: prop.label, span: 2, - children: ( + children: isSkeleton ? ( + + ) : ( ) } - disabled={disabled} + disabled={disabled || objectData?.isSkeleton} type={'text'} size={'small'} onClick={() => { @@ -304,6 +304,21 @@ const ObjectTable = forwardRef( return SKELETON_HEIGHT * pageSize }, [pageSize]) + const skeletonBoundaryIds = useMemo(() => { + const firstPage = pages[0] + const lastPage = pages[pages.length - 1] + return { + previous: + firstPage?.isSkeletonPage && firstPage.items.length > 0 + ? firstPage.items[firstPage.items.length - 1]._id + : null, + next: + lastPage?.isSkeletonPage && lastPage.items[0] + ? lastPage.items[0]._id + : null + } + }, [pages]) + const createPageWindow = useCallback( (loadedPages, direction) => { const sortedPages = [...loadedPages].sort( @@ -1101,37 +1116,39 @@ const ObjectTable = forwardRef( return () => cardsResizeObserverRef.current?.disconnect() }, []) - // Card view scroll handler - useEffect(() => { + // Card view: load when the leading skeleton card in a boundary page enters view + useLayoutEffect(() => { if (!cards) return const container = cardsContainerNodeRef.current if (!container) return const scrollEl = container.querySelector('.simplebar-content-wrapper') if (!scrollEl) return - const handleCardsScroll = (e) => { - const { scrollTop, scrollHeight, clientHeight } = e.target - const firstPage = pagesRef.current[0] - const lastPage = pagesRef.current[pagesRef.current.length - 1] - - if ( - lastPage?.isSkeletonPage && - scrollHeight - scrollTop - clientHeight < 100 && - !lazyLoading - ) { - loadNextPage(scrollEl) - } else if ( - firstPage?.isSkeletonPage && - scrollTop < 100 && - !lazyLoading - ) { - loadPreviousPage(scrollEl) + const observer = new IntersectionObserver( + (entries) => { + for (const entry of entries) { + if (!entry.isIntersecting) continue + const boundary = entry.target.dataset.skeletonBoundary + if (boundary === 'next') { + loadNextPage(scrollEl) + } else if (boundary === 'previous') { + loadPreviousPage(scrollEl) + } + } + }, + { + root: scrollEl, + rootMargin: `${SCROLL_THRESHOLD}px`, + threshold: 0 } - } + ) - scrollEl.addEventListener('scroll', handleCardsScroll) - return () => scrollEl.removeEventListener('scroll', handleCardsScroll) - }, [cards, pages, lazyLoading, loadNextPage, loadPreviousPage]) + container + .querySelectorAll('[data-skeleton-boundary]') + .forEach((el) => observer.observe(el)) + + return () => observer.disconnect() + }, [cards, skeletonBoundaryIds, loadNextPage, loadPreviousPage]) const renderCards = () => { return ( @@ -1146,9 +1163,20 @@ const ObjectTable = forwardRef( if (record?._id == undefined) { return null } + + const skeletonBoundary = + record._id === skeletonBoundaryIds.next + ? 'next' + : record._id === skeletonBoundaryIds.previous + ? 'previous' + : undefined + return ( -
+