diff --git a/packages/react-components/src/components/table-output-component.tsx b/packages/react-components/src/components/table-output-component.tsx index cd46c1ed4..edb808480 100644 --- a/packages/react-components/src/components/table-output-component.tsx +++ b/packages/react-components/src/components/table-output-component.tsx @@ -12,6 +12,7 @@ import { CellKeyDownEvent } from 'ag-grid-community/dist/lib/events'; import { TableModel } from 'tsp-typescript-client/lib/models/table'; import { SearchFilterRenderer, CellRenderer, LoadingRenderer } from './table-renderer-components'; import { ResponseStatus } from 'tsp-typescript-client'; +import { PaginationBarComponent } from './utils/pagination-bar-component'; type TableOuputState = AbstractOutputState & { tableColumns: ColDef[]; @@ -29,7 +30,9 @@ type TableOutputProps = AbstractOutputProps & { enum Direction { NEXT, - PREVIOUS + PREVIOUS, + FIRST, + LAST } export class TableOutputComponent extends AbstractOutputComponent { @@ -39,6 +42,7 @@ export class TableOutputComponent extends AbstractOutputComponent(); private pagination = true; private paginationPageSize = 500000; + private paginationTotalPages = 0; private showIndexColumn = false; private frameworkComponents: any; private gridApi: GridApi | undefined = undefined; @@ -100,6 +104,7 @@ export class TableOutputComponent extends AbstractOutputComponent= this.paginationPageSize; + this.paginationTotalPages = Math.floor(this.props.nbEvents / this.paginationPageSize); this.onEventClick = this.onEventClick.bind(this); this.onModelUpdated = this.onModelUpdated.bind(this); this.onKeyDown = this.onKeyDown.bind(this); @@ -113,15 +118,21 @@ export class TableOutputComponent extends AbstractOutputComponent this.checkFocus(event)} className={this.props.backgroundTheme === 'light' ? 'ag-theme-balham' : 'ag-theme-balham-dark'} - style={{ height: this.props.style.height, width: this.props.outputWidth }}> + style={{ + height: this.props.style.height, + width: this.props.outputWidth, + display: 'flex', + flexDirection: 'column' + }}> + {this.pagination && + + } ; } @@ -166,7 +185,13 @@ export class TableOutputComponent extends AbstractOutputComponent { if (this.props.nbEvents !== prevProps.nbEvents) { - this.gridApi?.setInfiniteRowCount(this.props.nbEvents); + this.gridApi?.setRowCount(this.props.nbEvents); + const newPagination = this.props.nbEvents >= this.paginationPageSize; + if (newPagination !== this.pagination) { + this.pagination = newPagination; + } + + this.paginationTotalPages = Math.floor(this.props.nbEvents / this.paginationPageSize); } } @@ -489,12 +514,10 @@ export class TableOutputComponent extends AbstractOutputComponent this.endTimestamp ? this.startTimestamp + BigInt(1) : this.startTimestamp); if (index) { - const pageNumber = Math.floor(index / this.paginationPageSize); - this.gridApi.paginationGoToPage(pageNumber); const startIndex = this.startTimestamp > this.endTimestamp ? index - 1 : index; this.selectStartIndex = this.selectStartIndex === -1 ? startIndex : this.selectStartIndex; this.selectEndIndex = (this.enableIndexSelection && this.selectEndIndex === -1) ? startIndex : this.selectEndIndex; - this.gridApi.ensureIndexVisible(this.selectStartIndex); + this.updatePageIndex(index); this.selectRows(); } } else { @@ -691,8 +714,6 @@ export class TableOutputComponent extends AbstractOutputComponent { @@ -711,6 +732,8 @@ export class TableOutputComponent extends AbstractOutputComponent= 0) { const data = await this.findMatchIndex(currRowIndex, direction); if (data !== undefined) { - // Change page if match is not on current page - indexPage = Math.floor(data.index / this.paginationPageSize); - if (indexPage !== this.gridApi.paginationGetCurrentPage()) { - this.gridApi.paginationGoToPage(indexPage); - } - this.gridApi.ensureIndexVisible(data.index); + this.updatePageIndex(data.index); this.selectStartIndex = this.selectEndIndex = data.index; if (this.timestampCol) { this.startTimestamp = this.endTimestamp = BigInt(data.row[this.timestampCol]); @@ -861,4 +872,14 @@ export class TableOutputComponent extends AbstractOutputComponent{this.renderToggleColumnsTable()}} ; } + + private updatePageIndex(rowIndex: number): void { + // Change page if match is not on current page + const indexPage = Math.floor(rowIndex / this.paginationPageSize); + if (indexPage !== this.gridApi?.paginationGetCurrentPage()) { + this.gridApi?.paginationGoToPage(indexPage); + this.forceUpdate(); + } + this.gridApi?.ensureIndexVisible(rowIndex); + } } diff --git a/packages/react-components/src/components/utils/pagination-bar-component.tsx b/packages/react-components/src/components/utils/pagination-bar-component.tsx new file mode 100644 index 000000000..07df497c4 --- /dev/null +++ b/packages/react-components/src/components/utils/pagination-bar-component.tsx @@ -0,0 +1,78 @@ +import * as React from 'react'; +import { GridApi } from 'ag-grid-community'; +import { numberFormat } from './xy-output-component-utils'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faChevronRight } from '@fortawesome/free-solid-svg-icons'; +import { faChevronLeft } from '@fortawesome/free-solid-svg-icons'; + +interface PaginationBarProps { + paginationPageSize: number; + paginationTotalPages: number; + nbEvents: number; + gridApi: GridApi | undefined; +} + +enum Direction { + NEXT, + PREVIOUS, + FIRST, + LAST +} + +export class PaginationBarComponent extends React.Component { + render(): JSX.Element { + const currentPage = this.props.gridApi?.paginationGetCurrentPage() ? this.props.gridApi?.paginationGetCurrentPage() + 1 : 1; + const firstRowRaw = (currentPage - 1) * this.props.paginationPageSize + 1; + const firstRow = numberFormat(firstRowRaw); + const lastRow = currentPage === this.props.paginationTotalPages + 1 ? + numberFormat(this.props.nbEvents) : + numberFormat(firstRowRaw + this.props.paginationPageSize - 1); + + return
+ + {firstRow} to {lastRow} of {numberFormat(this.props.nbEvents)} + + + + + + + + Page {currentPage} of {this.props.paginationTotalPages + 1} + + + + + +
; + } + + private paginationJumpTo(direction: Direction): void { + switch (direction) { + case Direction.FIRST: + this.props.gridApi?.paginationGoToFirstPage(); + break; + case Direction.LAST: + this.props.gridApi?.paginationGoToLastPage(); + break; + case Direction.NEXT: + this.props.gridApi?.paginationGoToNextPage(); + break; + case Direction.PREVIOUS: + this.props.gridApi?.paginationGoToPreviousPage(); + break; + } + + this.forceUpdate(); + } +} diff --git a/packages/react-components/src/components/utils/xy-output-component-utils.tsx b/packages/react-components/src/components/utils/xy-output-component-utils.tsx index 10dbb6df3..41ce669a8 100644 --- a/packages/react-components/src/components/utils/xy-output-component-utils.tsx +++ b/packages/react-components/src/components/utils/xy-output-component-utils.tsx @@ -194,3 +194,7 @@ export function getClosestPointForScatterPlot(params: GetClosestPointParam): XYP return undefined; } + +export function numberFormat(rawNumber: number): string{ + return new Intl.NumberFormat().format(rawNumber); +} diff --git a/packages/react-components/style/output-components-style.css b/packages/react-components/style/output-components-style.css index cdd425c79..fd97c7779 100644 --- a/packages/react-components/style/output-components-style.css +++ b/packages/react-components/style/output-components-style.css @@ -368,3 +368,18 @@ canvas { margin-top: 5px; transform: rotate(180deg); } + +.pagination-bar { + width: calc(100% - 2px); + height: 30px; + display: flex; + color: var(--theia-descriptionForeground); + border: thin solid var(--theia-disabledForeground); + border-top: none; + } + +.pagination-button { + border: none; + background: transparent; + color: var(--theia-foreground); +}