chore: pagination components

This commit is contained in:
VipinDevelops
2025-12-29 17:06:08 +05:30
parent fc6024535c
commit 28cd2efad0
4 changed files with 86 additions and 10 deletions

View File

@@ -0,0 +1,15 @@
import { Loader } from "@plane/ui";
export function PageListLoader() {
return (
<Loader className="relative flex items-center gap-2 p-3 py-4 border-b border-custom-border-200">
<Loader.Item width={`${250 + 10 * Math.floor(Math.random() * 10)}px`} height="22px" />
<div className="ml-auto relative flex items-center gap-2">
<Loader.Item width="60px" height="22px" />
<Loader.Item width="22px" height="22px" />
<Loader.Item width="22px" height="22px" />
<Loader.Item width="22px" height="22px" />
</div>
</Loader>
);
}

View File

@@ -1,13 +1,19 @@
import { useCallback, useRef, useState } from "react";
import { observer } from "mobx-react";
import { useParams } from "next/navigation";
// types
import type { TPageNavigationTabs } from "@plane/types";
import { Loader } from "@plane/ui";
// components
import { ListLayout } from "@/components/core/list";
// hooks
import { useIntersectionObserver } from "@/hooks/use-intersection-observer";
// plane web hooks
import type { EPageStoreType } from "@/plane-web/hooks/store";
import { usePageStore } from "@/plane-web/hooks/store";
// local imports
import { PageListBlock } from "./block";
import { PageListLoader } from "./loader";
type TPagesListRoot = {
pageType: TPageNavigationTabs;
@@ -16,17 +22,58 @@ type TPagesListRoot = {
export const PagesListRoot = observer(function PagesListRoot(props: TPagesListRoot) {
const { pageType, storeType } = props;
// params
const { workspaceSlug, projectId } = useParams();
// store hooks
const { getCurrentProjectFilteredPageIdsByTab } = usePageStore(storeType);
const { getCurrentProjectFilteredPageIdsByTab, fetchPagesList, getPaginationInfo, getPaginationLoader } =
usePageStore(storeType);
// pagination hooks
const paginationInfo = getPaginationInfo(pageType);
const paginationLoader = getPaginationLoader(pageType);
const hasNextPage = paginationInfo.hasNextPage;
const isFetchingNextPage = paginationLoader === "pagination";
// state for intersection observer element
const [intersectionElement, setIntersectionElement] = useState<HTMLDivElement | null>(null);
// ref for container - we'll wrap ListLayout
const containerRef = useRef<HTMLDivElement | null>(null);
// derived values
const filteredPageIds = getCurrentProjectFilteredPageIdsByTab(pageType);
// Function to fetch next page
const fetchNextPage = useCallback(() => {
if (!workspaceSlug || !projectId || !hasNextPage || isFetchingNextPage) {
return;
}
// Use fetchPagesList with cursor from pagination info
void fetchPagesList(
workspaceSlug.toString(),
projectId.toString(),
pageType,
paginationInfo.nextCursor ?? undefined
);
}, [workspaceSlug, projectId, hasNextPage, isFetchingNextPage, fetchPagesList, pageType, paginationInfo.nextCursor]);
// Set up intersection observer to trigger loading more pages
useIntersectionObserver(
containerRef,
isFetchingNextPage ? null : intersectionElement,
fetchNextPage,
`100% 0% 100% 0%`
);
if (!filteredPageIds) return <></>;
return (
<ListLayout>
{filteredPageIds.map((pageId) => (
<PageListBlock key={pageId} pageId={pageId} storeType={storeType} />
))}
</ListLayout>
<div ref={containerRef} className="size-full">
<ListLayout>
{filteredPageIds.map((pageId) => (
<PageListBlock key={pageId} pageId={pageId} storeType={storeType} />
))}
{hasNextPage && <div ref={setIntersectionElement}>{isFetchingNextPage && <PageListLoader />}</div>}
</ListLayout>
</div>
);
});

View File

@@ -156,6 +156,11 @@ export const PagesListMainContent = observer(function PagesListMainContent(props
/>
);
}
if (loader === "mutation-loader" && (!filteredPageIds || filteredPageIds.length === 0)) {
return <PageLoader />;
}
// if no pages match the filter criteria
if (filteredPageIds?.length === 0)
return (

View File

@@ -19,11 +19,20 @@ type TPageView = {
export const PagesListView = observer(function PagesListView(props: TPageView) {
const { children, pageType, projectId, storeType, workspaceSlug } = props;
// store hooks
const { isAnyPageAvailable, fetchPagesList } = usePageStore(storeType);
// fetching pages list
const { isAnyPageAvailable, fetchPagesList, filters } = usePageStore(storeType);
// fetching pages list - include filters and sorting in SWR key to refetch when they change
useSWR(
workspaceSlug && projectId && pageType ? `PROJECT_PAGES_${projectId}` : null,
workspaceSlug && projectId && pageType ? () => fetchPagesList(workspaceSlug, projectId, pageType) : null
workspaceSlug && projectId && pageType
? `PROJECT_PAGES_${projectId}_${pageType}_${filters.searchQuery || ""}_${filters.sortKey || ""}_${
filters.sortBy || ""
}_${JSON.stringify(filters.filters || {})}`
: null,
workspaceSlug && projectId && pageType ? () => fetchPagesList(workspaceSlug, projectId, pageType) : null,
{
revalidateOnFocus: true,
revalidateIfStale: true,
}
);
// pages loader