diff --git a/examples/grid-lite/components-react/src/App.tsx b/examples/grid-lite/components-react/src/App.tsx index ac64cd3..cdb84e1 100644 --- a/examples/grid-lite/components-react/src/App.tsx +++ b/examples/grid-lite/components-react/src/App.tsx @@ -9,7 +9,9 @@ import { DataTable, ColumnDefaults, Column, - Description + Description, + Pagination, + Header } from '@highcharts/grid-lite-react'; function App() { @@ -61,6 +63,13 @@ function App() { console.info('(callback) grid:', grid); }; + // Pagination + // const [paginationEnabled, setPaginationEnabled] = useState(false); + + // const onPaginationClick = () => { + // setPaginationEnabled(true); + // }; + return ( <> Grid Caption v2.1 +
Grid Description - {/* Grid Pagination */} + - +
+ + {/* */} +
); } diff --git a/examples/grid-lite/components-react/src/index.css b/examples/grid-lite/components-react/src/index.css index 04bacd8..9a63579 100644 --- a/examples/grid-lite/components-react/src/index.css +++ b/examples/grid-lite/components-react/src/index.css @@ -18,9 +18,16 @@ body { padding: 20px; } +#controls { + margin-top: 20px; + display: flex; + gap: 10px; +} + @media (prefers-color-scheme: dark) { body { background-color: #121212; color: #ffffff; } } + diff --git a/packages/grid-lite-react/src/index.ts b/packages/grid-lite-react/src/index.ts index 4272e3d..79cbac6 100644 --- a/packages/grid-lite-react/src/index.ts +++ b/packages/grid-lite-react/src/index.ts @@ -11,7 +11,15 @@ import GridLite from '@highcharts/grid-lite'; export { default as Grid } from './Grid'; export { default as GridLite } from './Grid'; -export { Caption, Data, ColumnDefaults, Column, Description } from '@highcharts/grid-shared-react'; +export { + Caption, + Data, + ColumnDefaults, + Column, + Description, + Pagination, + Header +} from '@highcharts/grid-shared-react'; export { DataTable, DataConnector } from '@highcharts/grid-lite'; export { merge } from '@highcharts/grid-lite/es-modules/Shared/Utilities.js'; export type { @@ -26,6 +34,10 @@ export type { ColumnOptionsProps, ColumnDataType, ColumnSortingOrder, - CellValueGetterContext + CellValueGetterContext, + PaginationProps, + HeaderProps, + GroupedHeaderOptions, + HeaderCellAccessibilityProps } from '@highcharts/grid-shared-react'; export type GridOptions = GridLite.Options; diff --git a/packages/grid-pro-react/src/index.ts b/packages/grid-pro-react/src/index.ts index a8a0200..7ba334f 100644 --- a/packages/grid-pro-react/src/index.ts +++ b/packages/grid-pro-react/src/index.ts @@ -11,7 +11,15 @@ import GridPro from '@highcharts/grid-pro'; export { default as Grid } from './Grid'; export { default as GridPro } from './Grid'; -export { Caption, Data, ColumnDefaults, Column, Description } from '@highcharts/grid-shared-react'; +export { + Caption, + Data, + ColumnDefaults, + Column, + Description, + Pagination, + Header +} from '@highcharts/grid-shared-react'; export { DataTable, DataConnector } from '@highcharts/grid-pro'; export { merge } from '@highcharts/grid-pro/es-modules/Shared/Utilities.js'; export type { @@ -26,6 +34,10 @@ export type { ColumnOptionsProps, ColumnDataType, ColumnSortingOrder, - CellValueGetterContext + CellValueGetterContext, + PaginationProps, + HeaderProps, + GroupedHeaderOptions, + HeaderCellAccessibilityProps } from '@highcharts/grid-shared-react'; export type GridOptions = GridPro.Options; diff --git a/packages/grid-shared-react/src/components/options/header/Header.tsx b/packages/grid-shared-react/src/components/options/header/Header.tsx new file mode 100644 index 0000000..b952ef3 --- /dev/null +++ b/packages/grid-shared-react/src/components/options/header/Header.tsx @@ -0,0 +1,19 @@ +/** + * Grid React integration. + * Copyright (c) 2025, Highsoft + * + * A valid license is required for using this software. + * See highcharts.com/license + * + */ + +import type { HeaderProps } from './headerProps'; + +export function Header(_props: HeaderProps) { + return null; +} + +Header._GridReact = { + type: 'Grid_Option', + gridOption: 'header' +}; diff --git a/packages/grid-shared-react/src/components/options/header/headerProps.ts b/packages/grid-shared-react/src/components/options/header/headerProps.ts new file mode 100644 index 0000000..86f57bb --- /dev/null +++ b/packages/grid-shared-react/src/components/options/header/headerProps.ts @@ -0,0 +1,36 @@ +/** + * Grid React integration. + * Copyright (c) 2025, Highsoft + * + * A valid license is required for using this software. + * See highcharts.com/license + * + */ + +/** + * Accessibility options for a header cell in the header tree. + */ +export interface HeaderCellAccessibilityProps { + description?: string; +} + +/** + * Header node in the `header` tree. A group (with `columns`) or a leaf + * (with `columnId`). Mirrors Grid Core `GroupedHeaderOptions`. + */ +export interface GroupedHeaderOptions { + accessibility?: HeaderCellAccessibilityProps; + format?: string; + className?: string; + columnId?: string; + columns?: Array; +} + +export interface HeaderProps { + /** + * Header tree: column order, inclusion, and grouping. + * Each entry is a column id (`string`) or a {@link GroupedHeaderOptions} + * object. Maps to Grid Core `options.header`. + */ + header?: Array; +} diff --git a/packages/grid-shared-react/src/components/options/header/index.ts b/packages/grid-shared-react/src/components/options/header/index.ts new file mode 100644 index 0000000..e25312d --- /dev/null +++ b/packages/grid-shared-react/src/components/options/header/index.ts @@ -0,0 +1,15 @@ +/** + * Grid React integration. + * Copyright (c) 2025, Highsoft + * + * A valid license is required for using this software. + * See highcharts.com/license + * + */ + +export { Header } from './Header'; +export type { + HeaderProps, + GroupedHeaderOptions, + HeaderCellAccessibilityProps +} from './headerProps'; diff --git a/packages/grid-shared-react/src/components/options/index.ts b/packages/grid-shared-react/src/components/options/index.ts index dbbf7f4..36984b3 100644 --- a/packages/grid-shared-react/src/components/options/index.ts +++ b/packages/grid-shared-react/src/components/options/index.ts @@ -22,3 +22,11 @@ export type { } from './columns/columnProps'; export { Description } from './description'; export type { DescriptionProps } from './description'; +export { Pagination } from './pagination'; +export type { PaginationProps } from './pagination'; +export { Header } from './header'; +export type { + HeaderProps, + GroupedHeaderOptions, + HeaderCellAccessibilityProps +} from './header'; diff --git a/packages/grid-shared-react/src/components/options/pagination/Pagination.tsx b/packages/grid-shared-react/src/components/options/pagination/Pagination.tsx new file mode 100644 index 0000000..8a169d7 --- /dev/null +++ b/packages/grid-shared-react/src/components/options/pagination/Pagination.tsx @@ -0,0 +1,19 @@ +/** + * Grid React integration. + * Copyright (c) 2025, Highsoft + * + * A valid license is required for using this software. + * See highcharts.com/license + * + */ + +import type { PaginationProps } from './paginationProps'; + +export function Pagination(_props: PaginationProps) { + return null; +} + +Pagination._GridReact = { + type: 'Grid_Option', + gridOption: 'pagination' +}; diff --git a/packages/grid-shared-react/src/components/options/pagination/index.ts b/packages/grid-shared-react/src/components/options/pagination/index.ts new file mode 100644 index 0000000..d5d79cb --- /dev/null +++ b/packages/grid-shared-react/src/components/options/pagination/index.ts @@ -0,0 +1,11 @@ +/** + * Grid React integration. + * Copyright (c) 2025, Highsoft + * + * A valid license is required for using this software. + * See highcharts.com/license + * + */ + +export { Pagination } from './Pagination'; +export type { PaginationProps } from './paginationProps'; diff --git a/packages/grid-shared-react/src/components/options/pagination/paginationProps.ts b/packages/grid-shared-react/src/components/options/pagination/paginationProps.ts new file mode 100644 index 0000000..a5f9a93 --- /dev/null +++ b/packages/grid-shared-react/src/components/options/pagination/paginationProps.ts @@ -0,0 +1,57 @@ +/** + * Grid React integration. + * Copyright (c) 2025, Highsoft + * + * A valid license is required for using this software. + * See highcharts.com/license + * + */ + +export interface PaginationProps { + /** + * Whether pagination should be rendered. + * Defaults to `true` when the `` component is used. + * Pass `false` to disable pagination while keeping other options. + */ + enabled?: boolean; + /** + * The current page number. + */ + page?: number; + /** + * Number of rows per page. + */ + pageSize?: number; + /** + * Alignment of pagination elements within the wrapper. + */ + align?: 'left' | 'center' | 'right' | 'distributed'; + /** + * Whether to show the page information text. + */ + pageInfo?: boolean; + /** + * Whether to show the page size selector. + */ + pageSizeSelector?: boolean; + /** + * Available options for the page size selector dropdown. + */ + pageSizeOptions?: number[]; + /** + * Whether to show numbered page buttons. + */ + pageButtons?: boolean; + /** + * Maximum number of page number buttons to show before using ellipsis. + */ + pageButtonsCount?: number; + /** + * Whether to show the first and last page navigation buttons. + */ + firstLast?: boolean; + /** + * Whether to show the previous and next page navigation buttons. + */ + previousNext?: boolean; +} diff --git a/packages/grid-shared-react/src/index.ts b/packages/grid-shared-react/src/index.ts index 3814da0..4cfc3ca 100644 --- a/packages/grid-shared-react/src/index.ts +++ b/packages/grid-shared-react/src/index.ts @@ -12,7 +12,15 @@ import { GridType, GridInstance } from './hooks/useGrid'; import type { GridProps, GridRefHandle } from './components/BaseGrid'; export { BaseGrid }; -export { Caption, Data, ColumnDefaults, Column, Description } from './components/options'; +export { + Caption, + Data, + ColumnDefaults, + Column, + Description, + Pagination, + Header +} from './components/options'; export { getChildProps } from './utils/getChildProps'; export type { CaptionProps, @@ -24,6 +32,10 @@ export type { ColumnOptionsProps, ColumnDataType, ColumnSortingOrder, - CellValueGetterContext + CellValueGetterContext, + PaginationProps, + HeaderProps, + GroupedHeaderOptions, + HeaderCellAccessibilityProps } from './components/options'; export type { GridType, GridInstance, GridProps, GridRefHandle }; diff --git a/packages/grid-shared-react/src/utils/getChildProps.test.tsx b/packages/grid-shared-react/src/utils/getChildProps.test.tsx deleted file mode 100644 index 1498a4e..0000000 --- a/packages/grid-shared-react/src/utils/getChildProps.test.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import { describe, it, expect } from 'vitest'; -import { Data } from '../components/options/data/Data'; -import { getChildProps } from './getChildProps'; - -describe('getChildProps', () => { - it('maps Data columns to options.data.columns', () => { - const columns = { - name: ['Alice', 'Bob'], - age: [23, 34] - }; - - expect(getChildProps()).toEqual({ - data: { - columns - } - }); - }); - - it('maps all Data props to options.data', () => { - const columns = { - name: ['Alice'] - }; - const connector = { id: 'csv' }; - const dataTable = { id: 'table-1' }; - - expect( - getChildProps( - - ) - ).toEqual({ - data: { - providerType: 'local', - autogenerateColumns: false, - columns, - connector, - dataTable, - updateOnChange: true, - idColumn: 'id' - } - }); - }); -}); diff --git a/packages/grid-shared-react/src/utils/getChildProps.ts b/packages/grid-shared-react/src/utils/getChildProps.ts index 572376d..4564d86 100644 --- a/packages/grid-shared-react/src/utils/getChildProps.ts +++ b/packages/grid-shared-react/src/utils/getChildProps.ts @@ -9,7 +9,8 @@ import { Fragment, isValidElement, ReactElement, ReactNode } from 'react'; import type { BaseGridOptionsComponent, BaseGridOptions } from '../components/BaseGridOptions'; -import { normalizeColumnOptions } from './mappers/columnOptions'; +import { normalizeColumnOptions } from './mappers/column'; +import { normalizePaginationOptions } from './mappers/pagination'; function objInsert( obj: Record, @@ -203,6 +204,20 @@ export function getChildProps(children: ReactNode): Record { return; } + if (meta.gridOption === 'pagination') { + optionsFromChildren.pagination = normalizePaginationOptions(props); + return; + } + + if (meta.gridOption === 'header') { + const { header, children: _ignored } = props; + + if (header !== void 0) { + optionsFromChildren.header = header; + } + return; + } + const optionParent = optionsFromChildren[meta.gridOption] ?? ( optionsFromChildren[meta.gridOption] = meta.isArrayType ? [] : {} ); diff --git a/packages/grid-shared-react/src/utils/mappers/columnOptions.ts b/packages/grid-shared-react/src/utils/mappers/column/columnOptions.ts similarity index 90% rename from packages/grid-shared-react/src/utils/mappers/columnOptions.ts rename to packages/grid-shared-react/src/utils/mappers/column/columnOptions.ts index 300c39c..f1ae833 100644 --- a/packages/grid-shared-react/src/utils/mappers/columnOptions.ts +++ b/packages/grid-shared-react/src/utils/mappers/column/columnOptions.ts @@ -7,7 +7,7 @@ * */ -import { mapPrefixedProps } from './mapPrefixedProps'; +import { mapPrefixedProps } from '../mapPrefixedProps'; /** Flat prop prefix → nested Grid option key for columns. */ const COLUMN_PROP_PREFIXES = { diff --git a/packages/grid-shared-react/src/utils/mappers/column/index.ts b/packages/grid-shared-react/src/utils/mappers/column/index.ts new file mode 100644 index 0000000..01a620a --- /dev/null +++ b/packages/grid-shared-react/src/utils/mappers/column/index.ts @@ -0,0 +1,10 @@ +/** + * Grid React integration. + * Copyright (c) 2025, Highsoft + * + * A valid license is required for using this software. + * See highcharts.com/license + * + */ + +export { normalizeColumnOptions } from './columnOptions'; diff --git a/packages/grid-shared-react/src/utils/mappers/pagination/index.ts b/packages/grid-shared-react/src/utils/mappers/pagination/index.ts new file mode 100644 index 0000000..de3e9f4 --- /dev/null +++ b/packages/grid-shared-react/src/utils/mappers/pagination/index.ts @@ -0,0 +1,10 @@ +/** + * Grid React integration. + * Copyright (c) 2025, Highsoft + * + * A valid license is required for using this software. + * See highcharts.com/license + * + */ + +export { normalizePaginationOptions } from './paginationOptions'; diff --git a/packages/grid-shared-react/src/utils/mappers/pagination/paginationOptions.ts b/packages/grid-shared-react/src/utils/mappers/pagination/paginationOptions.ts new file mode 100644 index 0000000..0db2d55 --- /dev/null +++ b/packages/grid-shared-react/src/utils/mappers/pagination/paginationOptions.ts @@ -0,0 +1,84 @@ +/** + * Grid React integration. + * Copyright (c) 2025, Highsoft + * + * A valid license is required for using this software. + * See highcharts.com/license + * + */ + +import type { PaginationProps } from '../../../components/options/pagination/paginationProps'; + +export function normalizePaginationOptions( + props: Record +): Record { + const { + pageInfo, + pageSizeSelector, + pageSizeOptions, + pageButtons, + pageButtonsCount, + firstLast, + previousNext, + enabled, + page, + pageSize, + align + } = props as PaginationProps; + + const result: Record = { + enabled: enabled ?? true + }; + + if (page !== void 0) { + result.page = page; + } + if (pageSize !== void 0) { + result.pageSize = pageSize; + } + if (align !== void 0) { + result.align = align; + } + + const controls: Record = {}; + + if (pageInfo !== void 0) { + controls.pageInfo = pageInfo; + } + + if (pageSizeSelector === false) { + controls.pageSizeSelector = false; + } else if (pageSizeOptions !== void 0) { + controls.pageSizeSelector = { + enabled: true, + options: pageSizeOptions + }; + } else if (pageSizeSelector !== void 0) { + controls.pageSizeSelector = pageSizeSelector; + } + + if (pageButtons === false) { + controls.pageButtons = false; + } else if (pageButtonsCount !== void 0) { + controls.pageButtons = { + enabled: true, + count: pageButtonsCount + }; + } else if (pageButtons !== void 0) { + controls.pageButtons = pageButtons; + } + + if (firstLast !== void 0) { + controls.firstLastButtons = firstLast; + } + + if (previousNext !== void 0) { + controls.previousNextButtons = previousNext; + } + + if (Object.keys(controls).length > 0) { + result.controls = controls; + } + + return result; +}