import { useCallback, useMemo } from 'react' import type { TableRow, TableWidgetState } from '../types' import { paginateData } from '../helpers' import { widgetStoreActions } from '../../stores/widget-store' import { useWidgetSelector } from '../../stores/use-widget-selector' import { DEFAULT_PAGE, DEFAULT_ROWS_PER_PAGE, DEFAULT_ROWS_PER_PAGE_OPTIONS, } from '../config' export interface UsePaginationResult { /** Whether pagination is enabled */ paginationEnabled: boolean /** Current page (0-indexed) */ page: number /** Rows per page */ rowsPerPage: number /** Total row count */ total: number /** Available rows per page options */ rowsPerPageOptions: number[] /** Paginated data (for local mode) */ paginatedData: T[] /** Set current page */ setPage: (page: number) => void /** Set rows per page */ setRowsPerPage: (rowsPerPage: number) => void /** Go to first page */ goToFirstPage: () => void /** Go to last page */ goToLastPage: () => void /** Go to next page */ goToNextPage: () => void /** Go to previous page */ goToPreviousPage: () => void } /** * Hook for managing table pagination with support for both local (client-side) and remote (server-side) modes. * State is persisted in the widget store for the given widget ID. * * @param widgetId - The widget ID to persist pagination state under. * @param data - The full data array (used for local pagination and total count fallback). * @returns Pagination state and navigation helpers. * * @example * ```tsx * const { page, setPage, paginatedData, totalPages } = usePagination(widgetId, data) * ``` */ export function usePagination( widgetId: string, data: T[], ): UsePaginationResult { // Get store state const { paginationEnabled, page, rowsPerPage, rowsPerPageOptions, total, mode, } = useWidgetSelector(widgetId, (w) => { const widget = w as TableWidgetState | undefined return { paginationEnabled: !!widget?.pagination, page: widget?.pagination?.page ?? DEFAULT_PAGE, rowsPerPage: widget?.pagination?.rowsPerPage ?? DEFAULT_ROWS_PER_PAGE, rowsPerPageOptions: widget?.pagination?.rowsPerPageOptions ?? DEFAULT_ROWS_PER_PAGE_OPTIONS, total: widget?.pagination?.total ?? data.length, mode: widget?.mode, } }) // Calculate max page const maxPage = Math.max(0, Math.ceil(total / (rowsPerPage ?? 1)) - 1) // Set page with bounds checking const setPage = useCallback( (newPage: number) => { // Read current state directly from store to avoid stale closures const widget = widgetStoreActions.getWidget(widgetId) const currentTotal = widget?.pagination?.total ?? data.length const currentRowsPerPage = widget?.pagination?.rowsPerPage ?? DEFAULT_ROWS_PER_PAGE const currentMaxPage = Math.max( 0, Math.ceil(currentTotal / currentRowsPerPage) - 1, ) const boundedPage = Math.max(0, Math.min(newPage, currentMaxPage)) widgetStoreActions.setWidget(widgetId, { pagination: { ...widget?.pagination, page: boundedPage, }, }) }, [data.length, widgetId], ) // Set rows per page and reset to first page const setRowsPerPage = useCallback( (newRowsPerPage: number) => { const widget = widgetStoreActions.getWidget(widgetId) widgetStoreActions.setWidget(widgetId, { pagination: { ...widget?.pagination, rowsPerPage: newRowsPerPage, page: 0, // Reset to first page when changing rows per page }, }) }, [widgetId], ) // Navigation helpers const goToFirstPage = useCallback(() => setPage(0), [setPage]) const goToLastPage = useCallback(() => setPage(maxPage), [setPage, maxPage]) const goToNextPage = useCallback(() => setPage(page + 1), [setPage, page]) const goToPreviousPage = useCallback(() => setPage(page - 1), [setPage, page]) // Paginated data for local mode const paginatedData = useMemo(() => { if (mode === 'remote' || !paginationEnabled) { // For remote mode, data is already paginated from server return data } return paginateData(data, page, rowsPerPage) }, [data, mode, page, paginationEnabled, rowsPerPage]) return { paginationEnabled, page, rowsPerPage, rowsPerPageOptions, total, paginatedData, setPage, setRowsPerPage, goToFirstPage, goToLastPage, goToNextPage, goToPreviousPage, } }