11import * as React from 'react' ;
22import { useSearchParams } from 'react-router-dom-v5-compat' ;
33
4+ import { useGitOpsTranslation } from '@gitops/utils/hooks/useGitOpsTranslation' ;
5+ import { Pagination , PaginationVariant } from '@patternfly/react-core' ;
46import DataView , { DataViewState } from '@patternfly/react-data-view/dist/esm/DataView' ;
57import DataViewTable , {
68 DataViewTh ,
79 DataViewTr ,
810} from '@patternfly/react-data-view/dist/esm/DataViewTable' ;
9- import { useDataViewSort } from '@patternfly/react-data-view/dist/esm/Hooks' ;
11+ import DataViewToolbar from '@patternfly/react-data-view/dist/esm/DataViewToolbar' ;
12+ import { useDataViewPagination , useDataViewSort } from '@patternfly/react-data-view/dist/esm/Hooks' ;
1013import { ThProps } from '@patternfly/react-table' ;
1114
15+ import { GITOPS_DEFAULT_PER_PAGE , GITOPS_PER_PAGE_OPTIONS } from './gitOpsDataViewPagination' ;
16+
17+ let gitOpsPaginationInstanceCounter = 0 ;
18+
19+ const useGitOpsPaginationWidgetIdBase = ( ) : string => {
20+ const idRef = React . useRef < string > ( ) ;
21+ if ( ! idRef . current ) {
22+ gitOpsPaginationInstanceCounter += 1 ;
23+ idRef . current = `gitops-pagination-${ gitOpsPaginationInstanceCounter } ` ;
24+ }
25+ return idRef . current ;
26+ } ;
27+
1228type BodyStateKey = 'empty' | 'error' ;
1329
1430export type GitOpsDataViewBodyStates = Partial < Record < BodyStateKey , React . ReactNode > > ;
@@ -46,6 +62,28 @@ export type GitOpsDataViewTableProps = {
4662 * the appropriate state based on isEmpty and isError.
4763 */
4864 activeState ?: DataViewState | null ;
65+ /**
66+ * Total number of filtered items. Required when pagination is enabled so the
67+ * pager can show "1-50 of N" against the full result set, not the current page.
68+ */
69+ itemCount ?: number ;
70+ /**
71+ * Optional PF pagination state. When omitted, the table renders every row.
72+ */
73+ pagination ?: GitOpsDataViewPagination ;
74+ } ;
75+
76+ export type GitOpsDataViewPagination = {
77+ page : number ;
78+ perPage : number ;
79+ onSetPage : (
80+ event : React . MouseEvent | React . KeyboardEvent | MouseEvent | undefined ,
81+ newPage : number ,
82+ ) => void ;
83+ onPerPageSelect : (
84+ event : React . MouseEvent | React . KeyboardEvent | MouseEvent | undefined ,
85+ newPerPage : number ,
86+ ) => void ;
4987} ;
5088
5189const mergeBodyStates = (
@@ -73,7 +111,10 @@ export const GitOpsDataViewTable: React.FC<GitOpsDataViewTableProps> = ({
73111 errorState,
74112 bodyStates,
75113 activeState,
114+ itemCount,
115+ pagination,
76116} ) => {
117+ const paginationWidgetIdBase = useGitOpsPaginationWidgetIdBase ( ) ;
77118 const resolvedBodyStates = React . useMemo (
78119 ( ) =>
79120 mergeBodyStates ( bodyStates , {
@@ -99,13 +140,69 @@ export const GitOpsDataViewTable: React.FC<GitOpsDataViewTableProps> = ({
99140 return null ;
100141 } , [ activeState , isEmpty , isError , isLoading ] ) ;
101142
143+ const paginationItemCount = itemCount ?? 0 ;
144+ const showPagination = ! ! pagination && paginationItemCount > 0 && ! isError && ! isLoading ;
145+
102146 return (
103147 < DataView activeState = { resolvedActiveState } >
148+ { showPagination && pagination && (
149+ < DataViewToolbar
150+ pagination = {
151+ < GitOpsPagination
152+ itemCount = { paginationItemCount }
153+ pagination = { pagination }
154+ variant = { PaginationVariant . top }
155+ widgetId = { `${ paginationWidgetIdBase } -top` }
156+ />
157+ }
158+ />
159+ ) }
104160 < DataViewTable columns = { columns } rows = { rows } bodyStates = { resolvedBodyStates } />
161+ { showPagination && pagination && (
162+ < DataViewToolbar
163+ pagination = {
164+ < GitOpsPagination
165+ itemCount = { paginationItemCount }
166+ pagination = { pagination }
167+ variant = { PaginationVariant . bottom }
168+ widgetId = { `${ paginationWidgetIdBase } -bottom` }
169+ />
170+ }
171+ />
172+ ) }
105173 </ DataView >
106174 ) ;
107175} ;
108176
177+ const GitOpsPagination : React . FC < {
178+ itemCount : number ;
179+ pagination : GitOpsDataViewPagination ;
180+ variant : PaginationVariant ;
181+ widgetId : string ;
182+ } > = ( { itemCount, pagination, variant, widgetId } ) => {
183+ const { t } = useGitOpsTranslation ( ) ;
184+
185+ return (
186+ < Pagination
187+ itemCount = { itemCount }
188+ perPageOptions = { GITOPS_PER_PAGE_OPTIONS }
189+ variant = { variant }
190+ widgetId = { widgetId }
191+ titles = { {
192+ paginationAriaLabel : t ( 'Pagination' ) ,
193+ toFirstPageAriaLabel : t ( 'Go to first page' ) ,
194+ toPreviousPageAriaLabel : t ( 'Go to previous page' ) ,
195+ toNextPageAriaLabel : t ( 'Go to next page' ) ,
196+ toLastPageAriaLabel : t ( 'Go to last page' ) ,
197+ itemsPerPage : t ( 'Items per page' ) ,
198+ perPageSuffix : t ( 'per page' ) ,
199+ ofWord : t ( 'of' ) ,
200+ } }
201+ { ...pagination }
202+ />
203+ ) ;
204+ } ;
205+
109206export interface GitOpsDataViewSortConfig {
110207 key : string ;
111208}
@@ -169,4 +266,59 @@ export const useGitOpsDataViewSort = (
169266 } ;
170267} ;
171268
269+ /**
270+ * Client-side pagination for GitOps DataView tables. Matches Console: 10/20/50/100,
271+ * default 50, page and perPage stored in the URL. Resets to page 1 when resetKey changes
272+ * (filters, search, namespace) and clamps when the result set shrinks.
273+ */
274+ export const useGitOpsDataViewPagination = ( {
275+ itemCount,
276+ resetKey,
277+ } : {
278+ itemCount : number ;
279+ resetKey ?: string ;
280+ } ) : GitOpsDataViewPagination => {
281+ const [ searchParams , setSearchParams ] = useSearchParams ( ) ;
282+
283+ const setMergedSearchParams = React . useCallback (
284+ ( params : URLSearchParams ) => {
285+ setSearchParams ( ( prev ) => {
286+ const next = new URLSearchParams ( prev ) ;
287+ params . forEach ( ( value , key ) => {
288+ next . set ( key , value ) ;
289+ } ) ;
290+ return next ;
291+ } ) ;
292+ } ,
293+ [ setSearchParams ] ,
294+ ) ;
295+
296+ const pagination = useDataViewPagination ( {
297+ perPage : GITOPS_DEFAULT_PER_PAGE ,
298+ searchParams,
299+ setSearchParams : setMergedSearchParams ,
300+ } ) ;
301+ const { page, perPage, onSetPage } = pagination ;
302+ const previousResetKey = React . useRef ( resetKey ) ;
303+
304+ React . useEffect ( ( ) => {
305+ if ( resetKey === undefined || previousResetKey . current === resetKey ) {
306+ return ;
307+ }
308+ previousResetKey . current = resetKey ;
309+ if ( page > 1 ) {
310+ onSetPage ( undefined , 1 ) ;
311+ }
312+ } , [ onSetPage , page , resetKey ] ) ;
313+
314+ React . useEffect ( ( ) => {
315+ const maxPage = Math . max ( 1 , Math . ceil ( itemCount / perPage ) || 1 ) ;
316+ if ( page > maxPage ) {
317+ onSetPage ( undefined , maxPage ) ;
318+ }
319+ } , [ itemCount , onSetPage , page , perPage ] ) ;
320+
321+ return pagination ;
322+ } ;
323+
172324type GitOpsSetSearchParams = ReturnType < typeof useSearchParams > [ 1 ] ;
0 commit comments