@p.aleks/react-grid-table
v1.0.23
Published
A modular table, based on a CSS grid layout, optimized for customization.
Downloads
35
Maintainers
Readme
Fork @nadavshaar/react-grid-table
react-grid-table
A modular table, based on a CSS grid layout, optimized for fast configuration and deep customization.
Supported features:
- Async support
- Sort by column
- Column resize
- Column reorder
- Search with highlight
- Pagination
- Row selection
- Inline row editing
- Column pinning (pre-configured)
- Column visibility management
- Virtual scroll
- Sticky header
- Dynamic row height
Live Demo
Install
npm i @p.aleks/react-grid-table
Usage
By default, the table is fully featured even with just a basic configuration of rows and columns.
Example:
import React from "react";
import GridTable from '@p.aleks/react-grid-table';
// custom cell component
const Username = ({ tableManager, value, field, data, column, colIndex, rowIndex }) => {
return (
<div className='rgt-cell-inner' style={{display: 'flex', alignItems: 'center', overflow: 'hidden'}}>
<img src={data.avatar} alt="user avatar" />
<span className='rgt-text-truncate' style={{marginLeft: 10}}>{value}</span>
</div>
)
}
const rows = [
{
"id": 1,
"username": "wotham0",
"gender": "Male",
"last_visited": "12/08/2019",
"test": {"x": 1, "y": 2},
"avatar":"https://robohash.org/atquenihillaboriosam.bmp?size=32x32&set=set1"
},
{
"id": 2,
"username": "dbraddon2",
"gender": "Female",
"last_visited": "16/07/2018",
"test": {"x": 3, "y": 4},
"avatar":"https://robohash.org/etsedex.bmp?size=32x32&set=set1"
},
{
"id": 3,
"username": "dridett3",
"gender": "Male",
"last_visited": "20/11/2016",
"test": {"x": 5, "y": 8},
"avatar":"https://robohash.org/inimpeditquam.bmp?size=32x32&set=set1"
},
{
"id": 4,
"username": "gdefty6",
"gender": "Female",
"last_visited": "03/08/2019",
"test": {"x": 7, "y": 4},
"avatar":"https://robohash.org/nobisducimussaepe.bmp?size=32x32&set=set1"
},
{
"id": 5,
"username": "hbeyer9",
"gender": "Male",
"last_visited": "10/10/2016",
"test": {"x": 2, "y": 2},
"avatar":"https://robohash.org/etconsequatureaque.jpg?size=32x32&set=set1"
}
];
const columns = [
{
id: 1,
field: 'username',
label: 'Username',
cellRenderer: Username,
},
{
id: 2,
field: 'gender',
label: 'Gender',
},
{
id: 3,
field: 'last_visited',
label: 'Last Visited',
sort: ({ a, b, isAscending }) => {
let aa = a.split('/').reverse().join(),
bb = b.split('/').reverse().join();
return aa < bb ? isAscending ? -1 : 1 : (aa > bb ? isAscending ? 1 : -1 : 0);
}
},
{
id: 4,
field: 'test',
label: 'Score',
getValue: ({ value }) => value.x + value.y
}
];
const MyAwesomeTable = () => <GridTable columns={columns} rows={rows} />;
export default MyAwesomeTable;
Docs
Table of contents
- Main components
- Props
- Configuration props
- Event props
- Async props
- The
columns
prop - The
checkbox
column - The
rows
prop - The
components
prop - The
additionalProps
prop - The
tableManager
API - How-To: Sync/Async
- How-To: Row-Editing
- How-To: Styling
Support this package
Main components
HEADER (optional | customizable): search & column visibility management.
TABLE-HEADER: sort, resize & column reorder.
TABLE-BODY: displaying data / loader / no-results, row editing & row selection.
FOOTER (optional | customizable): rows information, rows per page & pagination.
Props
| name | type | description | default value |
|---|---|---|---|
| columns* | array of objects | columns configuration (details) | [ ] |
| rows | array of objects | rows data (details) | [ ] |
| selectedRowsIds | array of ids | the ids of all selected rows (details) | [ ] |
| searchText | string | text for search | "" |
| getIsRowSelectable | function | a callback function that returns whether row selection for the current row should be selectable or disabled | row => true
|
| getIsRowEditable | function | a callback function that returns whether row editing for the current row should be allowed or not | row => true
|
| editRowId | any | the id of the row that should switch to inline editing mode, (more details about row editing) | null |
| classNameContainer | string | a custom class selector for table container | "" |
| page | number | current page number | 1 |
| pageSize | number | the selected page size | 20 |
| sort | object | sort config. accepts colId
for the id of the column that should be sorted, and isAsc
to define the sort direction. example: { colId: 'some-column-id', isAsc: true }
, to unsort simply pass colId
as null
| { } |
| isLoading | boolean | whether to display the loader | false |
Configuration props
| name | type | description | default value |
|---|---|---|---|
| rowIdField | string | the name of the field in the row's data that should be used as the row identifier - must be unique | 'id' |
| minColumnResizeWidth | number | minimum width for all columns while resizing (doesn't apply to 'checkbox' column)| 70 |
| minSearchChars | number | the minimum characters to type before search will apply | 2 |
| isHeaderSticky | boolean | whether the table header cells will stick to the top when scrolling, or not | true |
| isPaginated | boolean | determine whether the pagination controls sholuld be shown in the footer and if the rows data should split into pages | true |
| enableColumnsReorder | boolean | whether to allow column drag & drop for repositioning | true |
| highlightSearch | boolean | whether to highlight the search term | true |
| showSearch | boolean | whether to show the search component in the header | true |
| showRowsInformation | boolean | whether to show the rows information component (located at the left side of the footer) | true |
| showColumnVisibilityManager | boolean | whether to display the columns visibility management button (located at the top right of the header) | true |
| pageSizes | array of numbers | page size options | [20, 50, 100] |
| isVirtualScroll | boolean | whether to render items in a virtual scroll to enhance performance (useful when you have lots of rows in a page) | true |
| selectAllMode | string | controls the type of "All Selection". Available options are page
to select / unselect only the rows in the current page, or all
to select / unselect all rows in all pages. If using an async flow, the all
option will select all available rows, and the page
option combined with batchSize
, will select/unselect all available rows in the page | 'page' |
| icons | object of nodes | custom icons config | { sortAscending, sortDescending, clearSelection, columnVisibility, search, loader } |
| texts | object | config for all UI text, useful for translations or to customize the text | { search: 'Search:', totalRows: 'Total rows:', rows: 'Rows:', selected: 'Selected', rowsPerPage: 'Rows per page:', page: 'Page:', of: 'of', prev: 'Prev', next: 'Next', columnVisibility: 'Column visibility' } |
| components | object | This prop gives you the ability to override the internal components with your own custom components (see full list of supported components) | { Default components } |
| additionalProps | object | This prop gives you the ability to pass props to the table's components/modules (see full list of supported additionalProps) | additionalProps={{ header: { ... } }}
|
Event props
| name | type | description | usage |
|---|---|---|---|
| onColumnsChange | function | triggers when the columns
has been changed | columns => { ... }
|
| onSelectedRowsChange | function | triggers when rows selection has been changed | selectedRowsIds => { ... }
|
| onPageChange | function | triggers when page is changed | nextPage => { ... }
|
| onPageSizeChange | function | triggers when page size is changed | newPageSize => { ... }
|
| onSearchTextChange | function | triggers when search text changed | searchText => { ... }
|
| onSortChange | function | triggers when sort changed | ({colId, isAsc}) => { ... }
|
| onRowClick | function | triggers when a row is clicked | ({ rowIndex, data, column, isEdit, event }, tableManager) => { ... }
|
| onEditRowIdChange | function | triggers when rowEditId
changed | rowEditId => { ... }
|
| onLoad | function | triggers when tableManager
is initialized (details) | tableManager => { ... }
|
| onColumnResizeStart | function | triggers when column resize starts | ({event, target, column}) => { ... }
|
| onColumnResize | function | triggers when column resize occur | ({event, target, column}) => { ... }
|
| onColumnResizeEnd | function | triggers when column resize ended | ({event, target, column}) => { ... }
|
| onColumnReorderStart | function | triggers on column drag. the sort data supplied by react-sortable-hoc using the onSortStart
prop | (sortData, tableManager) => { ... }
|
| onColumnReorderEnd | function | triggers on column drop, and only if the column changed its position. the sort data supplied by react-sortable-hoc using the onSortEnd
prop | (sortData, tableManager) => { ... }
|
Async props
| name | type | description | usage/default value |
|---|---|---|---|
| onRowsRequest | function | triggers when new rows should be fetched | see example |
| onRowsChange | function | triggers when the rows have changed | see example |
| onTotalRowsChange | function | triggers when the total number of rows have changed | see example |
| onRowsReset | function | triggers when the accumulated rows needs to be reset (when searching or sorting) | see example |
| batchSize | number | defines the amount of rows that will be requested by onRowsRequest
prop | the page size of the table |
| requestDebounceTimeout | number | defines the amount of debouncing time for triggering the onRowsRequest
prop | 300 |
| totalRows | number | the total number of rows | --- |
props - detailed
columns
Type: array of objects.
This prop defines the columns configuration.
Each column (except for 'checkbox' column) has support for the following properties:
| name | type | description | default value |
|---|---|---|---|
| id* | any | a unique identifier for the column, setting it to 'checkbox' will generate a rows selction column (more details about checkbox column) | --- |
| field | string | the name of the field as in the row data | --- |
| label | string | the label to display in the header cell | the field
property |
| pinned | boolean | whether the column will be pinned to the side, supported only in the first and last columns | false |
| visible | boolean | whether to display the column | true |
| headerClassName | string | a custom class selector for the header cells | "" |
| className | string | a custom class selector for all column cells | "" |
| width | string | the initial width of the column in grid values (full list of values) | "200px" |
| minResizeWidth | number | the minimum width of the column when resizing | minColumnResizeWidth
prop |
| maxResizeWidth | number, null | the maximum width of the column when resizing | null |
| getValue | function | used for getting the cell value (usefull when the cell value is not a string - details) | ({value, column}) => value
|
| setValue | function | used for updating the cell value (usefull when the cell value is not a string - details) | ({ value, data, setRow, column }) => { setRow({ ...data, [column.field]: value}) }
|
| searchable | boolean | whether to apply search filtering on the column | true |
| editable | boolean | whether to allow editing for the column | true |
| sortable | boolean | whether to allow sort for the column | true |
| resizable | boolean | whether to allow resizing for the column | true |
| search | function | the search function for the column | ({value, searchText}) => value.toString().toLowerCase().includes(searchText.toLowerCase())
|
| sort | function | the sort function for the column | ({a, b, isAscending}) => { let aa = typeof a === 'string' ? a.toLowerCase() : a; let bb = typeof b === 'string' ? b.toLowerCase() : b; if(aa > bb) return isAscending ? 1 : -1; else if(aa < bb) return isAscending ? -1 : 1; return 0; }
|
| cellRenderer | function | used for custom rendering the cell component ({ tableManager, value, data, column, colIndex, rowIndex }) => ( children )
| --- |
| headerCellRenderer | function | used for custom rendering the header cell component ({ tableManager, column }) => ( children )
| --- |
| editorCellRenderer | function | used for custom rendering the cell component in edit mode ({ tableManager, value, data, column, colIndex, rowIndex, onChange }) => ( children )
| --- |
| placeHolderRenderer | function | used for custom rendering the cell's placeholder component that is displayed when loading new rows ({ tableManager, value, data, column, colIndex, rowIndex }) => ( children )
| --- |
Example:
// column config
{
id: 'some-unique-id',
field: 'first_name',
label: 'First Name',
className: '',
pinned: false,
width: '200px',
getValue: ({ tableManager, value, column, rowData }) => value,
setValue: ({ value, data, setRow, column }) => { setRow({ ...data, [column.field]: value}) },
minResizeWidth: 70,
maxResizeWidth: null,
sortable: true,
editable: true,
searchable: true,
visible: true,
resizable: true,
search: ({value, searchText}) => { },
sort: ({a, b, isAscending}) => { },
cellRenderer: ({ tableManager, value, data, column, colIndex, rowIndex }) => ( children ),
headerCellRenderer: ({ tableManager, column }) => ( children ),
editorCellRenderer: ({ tableManager, value, data, column, colIndex, rowIndex, onChange }) => ( children ),
placeHolderRenderer: ({ tableManager, value, data, column, colIndex, rowIndex }) => ( children )
}
checkbox-column
Rows selection is done by a predefined column, simply add a column with an id
of 'checkbox'.
Checkbox column has support for the following properties:
| name | type | description | default value |
|---|---|---|---|
| id* | 'checkbox' | will generate a rows selction column | --- |
| pinned | boolean | whether the column will be pinned to the side, supported only in the first and last columns | false |
| visible | boolean | whether to display the column | true |
| className | string | a custom class for all column cells | "" |
| width | string | the initial width of the column in grid values (full list of values) | "max-content" |
| minResizeWidth | number | the minimum width of the column when resizing | 0 |
| maxResizeWidth | number, null | the maximum width of the column when resizing | null |
| resizable | boolean | whether to allow resizing for the column | false |
| cellRenderer | function | used for custom rendering the checkbox cell | ({ tableManager, value, data, column, colIndex, rowIndex, onChange, disabled}) => ( <input type="checkbox" onChange={ onChange } checked={ value } disabled={ disabled } /> )
|
| headerCellRenderer | function | used for custom rendering the checkbox header cell | ({ tableManager, column, mode, ref, checked, disabled, indeterminate, onChange }) => ( <input type="checkbox" onChange={ onChange } checked={ checked } disabled={ disabled } /> )
|
Example:
// checkbox column config
{
id: 'checkbox',
pinned: true,
className: '',
width: '54px',
minResizeWidth: 0,
maxResizeWidth: null,
resizable: false,
visible: true,
cellRenderer: ({tableManager, value, data, column, colIndex, rowIndex, onChange, disabled}) => ( children )
headerCellRenderer: ({tableManager, column, mode, ref, checked, disabled, indeterminate, onChange}) => ( children )
}
rows
Type: array of objects.
This prop containes the data for the rows.
Each row should have a unique identifier field, which by default is id
, but it can be changed to a different field using the rowIdField
prop.
// Example for a single row data
{
"id": "some-unique-id",
"objectValueField": {"x": 1, "y": 2},
"username":"wotham0",
"first_name":"Waldemar",
"last_name":"Otham",
"avatar":"https://robohash.org/atquenihillaboriosam.bmp?size=32x32&set=set1",
"email":"[email protected]",
"gender":"Male",
"ip_address":"113.75.186.33",
"last_visited":"12/08/2019"
}
Note: If a property value is not of type string, or in cases you don't specify a field for the column, you'll have to use the getValue
function on the column in order to extract the desired value.
Signature: getValue: ({ tableManager, value, column, rowData }) => string
Example:
Let's say the field's value for a cell is an object:
{ ... , fullName: {firstName: 'some-first-name', lastName: 'some-last-name'} }
,
Its getValue
function for displaying the first and last name as a full name, would be:
getValue: ({ value }) => value.firstName + ' ' + value.lastName
The returned value will be used for searching, sorting etc...
components
Type: object.
This prop gives you the ability to override the internal components with your own custom components.
All components are exported so you'll be able to import them from anywhere but you'll be responsible to supply them with their props:
| component | required props | optional props |
|---|---|---|
| Header | tableManager
| --- |
| Search | tableManager
| value
onChange
|
| ColumnVisibility | tableManager
| columns
onChange
|
| HeaderCell | tableManager
| column
|
| HeaderSelectionCell | tableManager
| column
ref
onChange
checked
disabled
|
| Cell | tableManager
| value
|
| EditorCell | tableManager
| value
data
column
colIndex
rowIndex
onChange
|
| SelectionCell | tableManager
| value
disabled
onChange
|
| PlaceHolderCell | tableManager
| --- |
| Loader | tableManager
| --- |
| NoResults | tableManager
| --- |
| DragHandle | --- | --- |
| Footer | tableManager
| --- |
| Information | tableManager
| totalCount
pageSize
pageCount
selectedCount
|
| PageSize | tableManager
| value
onChange
options
|
| Pagination | tableManager
| page
onChange
|
Example: Overriding the header component
const Header = ({tableManager}) => {
const { searchApi, columnsVisibilityApi, columnsApi } = tableManager;
const { searchText, setSearchText } = searchApi;
const { toggleColumnVisibility } = columnsVisibilityApi;
const { columns } = columnsApi;
return (
<div style={{display: 'flex', flexDirection: 'column', padding: '10px 20px', background: '#fff', width: '100%'}}>
<div>
<label htmlFor="my-search" style={{fontWeight: 500, marginRight: 10}}>
Search for:
</label>
<input
name="my-search"
type="search"
value={searchText}
onChange={e => setSearchText(e.target.value)}
style={{width: 300}}
/>
</div>
<div style={{display: 'flex', marginTop: 10}}>
<span style={{ marginRight: 10, fontWeight: 500 }}>Columns:</span>
{
columns.map((column, idx) => (
<div key={idx} style={{flex: 1}}>
<input
id={`checkbox-${idx}`}
type="checkbox"
onChange={ e => toggleColumnVisibility(column.id) }
checked={ column.visible !== false }
/>
<label htmlFor={`checkbox-${idx}`} style={{flex: 1, cursor: 'pointer'}}>
{column.label}
</label>
</div>
))
}
</div>
</div>
)
}
const MyAwesomeTable = props => {
...
return (
<GridTable
...
components={{ Header }}
/>
)
}
additionalProps
Type: object.
This prop gives you the ability to pass props to internal components/modules.
Example Passing props to the cell component:
additionalProps={{ cell: { ... }, ... }}
List of components/modules you can pass props to:
- header
- search
- columnVisibility
- headerCell
- headerSelectionCell
- cell
- editorCell
- selectionCell
- placeHolderCell
- footer
- information
- pageSize
- pagination
- rowVirtualizer
tableManager
This is the API object used by the internal components, you can use it to do anything that the API provides, outside of the component.
API Structure:
- id: A unique identifier for each table component.
- isMounted: Is the table mounted.
- isInitialized: Is the table initialized. Will be set to true once all components are initialized.
- mode: 'sync' or 'async', derived from the supplied props.
- isLoading: Is the table currently loading data.
- config: All the params that defines the table's user-interface and its behavior.
- refs: ref objects for selected elements.
- columnsApi: API of the columns.
- columnsVisibilityApi: API of the columns visibility.
- searchApi: API of the search.
- sortApi: API of the sort.
- rowsApi: API of the rows
- paginationApi: API of the pagination.
- rowSelectionApi: API of the rows selection.
- rowEditApi: API of the row editing.
- rowVirtualizer: API of the rows virtualizer (See full documentation at react-virtual).
- asyncApi: API of the async functionality.
config
| name | type | description | default value |
|---|---|---|---|
| rowIdField | string | the name of the field in the row's data that should be used as the row identifier - must be unique | 'id' |
| minColumnResizeWidth | number | minimum width for all columns while resizing (doesn't apply to 'checkbox' column) | 70 |
| minSearchChars | number | the minimum characters to type before search will apply | 2 |
| isHeaderSticky | boolean | whether the table header cells will stick to the top when scrolling, or not | true |
| isPaginated | boolean | determine whether the pagination controls sholuld be shown in the footer and if the rows data should split into pages | true |
| enableColumnsReorder | boolean | whether to allow column drag & drop for repositioning | true |
| highlightSearch | boolean | whether to highlight the search term | true |
| showSearch | boolean | whether to show the search component in the header | true |
| showRowsInformation | boolean | whether to show the rows information component (located at the left side of the footer) | true |
| showColumnVisibilityManager | boolean | whether to display the columns visibility management button (located at the top right of the header) | true |
| pageSizes | array of numbers | page size options | [20, 50, 100] |
| requestDebounceTimeout | number | defines the amount of debouning time for triggering the onRowsRequest
prop | 300 |
| isVirtualScroll | boolean | whether to render items in a virtual scroll to enhance performance (useful when you have lots of rows in a page) | true |
| tableHasSelection | boolean | wether the table has a checkbox column to control rows selection | --- |
| components | object | the components that are in use by the table (see full list of components) | {Header, Search, ColumnVisibility, HeaderCell, HeaderSelectionCell, Cell, EditorCell, SelectionCell, PlaceHolderCell, Loader, NoResults, Footer, Information, PageSize, Pagination} |
| additionalProps | object | additional props that are passed to the internal components (see full list of additionalProps) | {} |
| icons | object | the icons that are in use by the table | { sortAscending, sortDescending, clearSelection, columnVisibility, search, loader } |
| texts | object | the texts that are in use by the table | { search, totalRows, rows, selected, rowsPerPage, page, of, prev, next, columnVisibility } |
refs
| name | type | description |
|---|---|---|
| rgtRef | object | the ref
object of the wrapper element |
| tableRef | object | the ref
object of the table container element |
columnsApi
| name | type | description | usage |
|---|---|---|---|
| columns | array | columns configuration | --- |
| visibleColumns | array | the columns that are visible | --- |
| setColumns | function | updates the columns | setColumns(columns)
|
columnsVisibilityApi
| name | type | description | usage |
|---|---|---|---|
| toggleColumnVisibility | function | toggles a column's visibility by its id
| toggleColumnVisibility(column.id)
|
searchApi
| name | type | description | usage |
|---|---|---|---|
| searchText | string | text for search | --- |
| validSearchText | string | is an empty string if the searched text did not pass the minSearchChars
, if it does pass, it will be equal to searchText
| --- |
| setSearchText | function | updates the search text | setSearchText('hello')
|
| searchRows | function | filters rows based on the search text, using the search method defined on the columns | searchRows(rows)
|
| valuePassesSearch | function | returns true if a value passes the search for a certain column | valuePassesSearch('hello', column)
|
sortApi
| name | type | description | usage |
|---|---|---|---|
| sort | object | the sort object holds colId
for the id of the column that should be sorted or null
when there is no sort, and isAsc
that defines the sort direction | --- |
| setSort | function | updates the sort object | setSort({colId: 5, isAsc: false})
|
| sortRows | function | sorts rows based on the selected direction using the sort method defined on the column | sortRows(rows)
|
| toggleSort | function | toggles a column's sort steps from ascending, to descending and to none | toggleSort(column.id)
|
rowsApi
| name | type | description | usage |
|---|---|---|---|
| rows | array | the rows data (in sync mode - the rows data after the search filter and the sort) | --- |
| originalRows | array | the rows data untouched (in sync mode - the rows data before the search filter) | --- |
| setRows | function | updates the rows | setRows(rows)
|
| totalRows | number | the total number of rows | --- |
| setTotalRows | function | updates the total number of rows | setTotalRows(1000)
|
paginationApi
| name | type | description | usage |
|---|---|---|---|
| page | number | the current page number | --- |
| setPage | function | updates the page number | setPage(3)
|
| pageSize | number | the selected page size | --- |
| setPageSize | function | updates the page size | setPageSize(20)
|
| pageRows | array | the rows in the current page | --- |
| totalPages | number | the total number of pages | --- |
rowSelectionApi
| name | type | description | usage |
|---|---|---|---|
| selectedRowsIds | array of ids | the ids of all selected rows | --- |
| setSelectedRowsIds | function | updates the selected rows | setSelectedRowsIds([1,3,5])
|
| toggleRowSelection | function | toggles selection of a row by its id
| toggleRowSelection(row.id)
|
| getIsRowSelectable | function | determains whether a row can be selected | getIsRowSelectable(row.id)
|
| selectAll.mode | string | the type of select all, possible modes are page
which only handles selection of the page rows, or all
which handles selection of all rows. If using an async flow, all mode will handle selection of all available rows, and page mode with a controlled batchSize
, will handle selection of all available rows in the page | --- |
| selectAll.disabled | boolean | whether the select all button should be disabled because there are no selectable rows that match the selectAll.mode | --- |
| selectAll.checked | boolean | whether all the rows that match the selectAll.mode are selected | --- |
| selectAll.indeterminate | boolean | whether only some of the rows that match the selectAll.mode are selected | --- |
| selectAll.onChange | function | selects/unselects all rows that match the selectAll.mode | selectAll.onChange()
|
| selectAll.ref | ref | a ref that can be added to the select all checkbox to enable auto setting of indeterminate state | --- |
rowEditApi
| name | type | description | usage |
|---|---|---|---|
| editRow | object | the row's data that is currently being edited | --- |
| editRowId | any | the id of the row that is currently being edited | --- |
| getIsRowEditable | function | determains whether a row can be edited | getIsRowEditable(row)
|
| setEditRow | function | updates the row's data of the currently edited row | setEditRow(row)
|
| setEditRowId | function | updates the row id of the currently edited row, you can pass null
to switch back from edit mode | setEditRowId(row.id)
|
asyncApi
| name | type | description | usage / default value |
|---|---|---|---|
| isLoading | boolean | whether a request for new rows is still pending | --- |
| mergeRowsAt | function | merges array
s of rows at a certain index while filling "holes" with null
s | mergeRowsAt(rows, moreRows, atIndex)
|
| resetRows | function | drops the accumulated rows, which will trigger a new request | resetRows()
|
| batchSize | number | defines the amount of rows that will be requested by onRowsRequest
prop | paginationApi.pageSize
|
How to...
Sync/Async
react-grid-table
supports 4 different data models:
Sync:
Use this flow if you have all the data locally.
Just pass all the data using the rows
prop.
Required props:
| name | type | description | |---|---|---| | rows* | array of objects | rows data (details) |
Example:
export const SyncedTable = () => {
const rows = getRows();
const columns = getColumns();
return (
<GridTable
columns={columns}
rows={rows}
/>
)
}
Async (Uncontrolled):
Use this flow if you need to fetch your data asynchrony, and want react-grid-table
to manage it internally.
All the data is supplied to the table via the onRowsRequest
prop.
Required props:
| name | type | description | |---|---|---| | onRowsRequest* | async function | Should return a promise that resolves to {rows, totalRows} |
Example:
export const AsyncUncontrolledTable = () => {
const columns = getColumns();
const onRowsRequest = async (requestData, tableManager) => {
const response = await fetch(`app/api/rows`, {
method: 'post',
body: {
from: requestData.from,
to: requestData.to,
searchText: requestData.searchText,
sort: requestData.sort,
},
}).then(response => response.json()).catch(console.warn);
if(!response?.items) return;
return {
rows: response.items,
totalRows: response.totalItems
};
}
return (
<GridTable
columns={columns}
onRowsRequest={onRowsRequest}
/>
)
}
Async (Controlled):
Use this flow if you need to fetch your data asynchrony, and want react-grid-table
to manage it internally, but still be able to use it in other places in the app.
All the data is supplied to the table via the onRowsRequest
prop, but is controlled by a parent component via rows
, onRowsChange
, totalRows
& onTotalRowsChange
props.
Required props:
| name | type | description | |---|---|---| | onRowsRequest* | async function | Should return a promise that resolves to {rows, totalRows} | | rows* | array of objects | rows data (details) | | onRowsChange* | function | Should be used to set the current data | | totalRows* | number | Should contain the current data length | | onTotalRowsChange* | function | Should be used to set the current data length |
Example:
export const AsyncControlledTable = () => {
const columns = getColumns();
let [rows, setRows] = useState();
let [totalRows, setTotalRows] = useState();
const onRowsRequest = async (requestData, tableManager) => {
const response = await fetch(`app/api/rows`, {
method: 'post',
body: {
from: requestData.from,
to: requestData.to,
searchText: requestData.searchText,
sort: requestData.sort,
},
}).then(response => response.json()).catch(console.warn);
if(!response?.items) return;
return {
rows: response.items,
totalRows: response.totalItems
};
}
return (
<GridTable
columns={columns}
onRowsRequest={onRowsRequest}
rows={rows}
onRowsChange={setRows}
totalRows={totalRows}
onTotalRowsChange={setTotalRows}
/>
)
}
Async (Managed):
Use it if you need to fetch your data asynchrony, and manage it yourself (Useful when there are other places that should be able to fetch the same data).
All the data is supplied to the table via the rows
prop, which should be updated using the onRowsRequest
prop.
Note: react-grid-table
will not necessarily ask for concurrent data, which means that "holes" in the data are possible. These "holes" needs to be filled with null/undefined items in order to ensure proper functionally.
To achieve this, you can use:
let mergedRows = tableManager.asyncApi.mergeRowsAt(rows, fetchedRows, at)
Required props:
| name | type | description | |---|---|---| | onRowsRequest* | async function | Should update the rows props according to the request | | rows* | array of objects | rows data (details) | | totalRows* | number | Should contain the current data length | | onRowsReset* | function | Should be used to reset the current data. Will be called when sort or searchText change |
Example:
const controller = new AbortController();
export const AsyncManagedTable = () => {
const columns = getColumns();
let rowsRef = useRef([]);
let [totalRows, setTotalRows] = useState();
const onRowsRequest = async (requestData, tableManager) => {
const response = await fetch(`app/api/rows`, {
method: 'post',
body: {
from: requestData.from,
to: requestData.to,
searchText: requestData.searchText,
sort: requestData.sort,
},
signal: controller.signal,
}).then(response => response.json()).catch(console.warn);
if(!response?.items) return;
rowsRef.current = tableManager.asyncApi.mergeRowsAt(rowsRef.current, response.items, requestData.from);
setTotalRows(response.totalItems);
}
const onRowsReset = () => {
rowsRef.current = [];
setTotalRows();
controller.abort();
}
return (
<GridTable
columns={columns}
rows={rowsRef.current}
onRowsRequest={onRowsRequest}
onRowsReset={onRowsReset}
totalRows={totalRows}
requestDebounceTimeout={500}
/>
)
}
Row-Editing
Row editing can be done by rendering the edit button using the cellRenderer
property in the column configuration, then when clicked, it will control the editRowId
prop, then the table will render the editing components for columns that are defined as editable
(true by default), and as was defined in the editorCellRenderer
which by default will render a text input.
Example:
// state
const [rowsData, setRows] = useState(MOCK_DATA);
// 'editRowId' can also be controlled.
// if using controlled state, you can call your 'setEditRowId' instead of 'tableManager.rowEditApi.setEditRowId' to set it directly on your state.
// the table will typicaly call 'onEditRowIdChange' to reset it if 'searchText', 'sort' or 'page' has changed.
// const [editRowId, setEditRowId] = useState(null)
// columns
let columns = [
...,
{
id: 'my-buttons-column',
width: 'max-content',
pinned: true,
sortable: false,
resizable: false,
cellRenderer: ({ tableManager, value, data, column, colIndex, rowIndex }) => (
<button
style={{marginLeft: 20}}
onClick={e => tableManager.rowEditApi.setEditRowId(data.id)}
>✎</button>
),
editorCellRenderer: ({ tableManager, value, data, column, colIndex, rowIndex, onChange }) => (
<div style={{display: 'inline-flex'}}>
<button
style={{marginLeft: 20}}
onClick={e => tableManager.rowEditApi.setEditRowId(null)}
>✖</button>
<button
style={{marginLeft: 10, marginRight: 20}}
onClick={e => {
let rowsClone = [...rowsData];
let updatedRowIndex = rowsClone.findIndex(r => r.id === data.id);
rowsClone[updatedRowIndex] = data;
setRowsData(rowsClone);
tableManager.rowEditApi.setEditRowId(null);
}
}>✔</button>
</div>
)
}
];
// render
<GridTable
columns={columns}
rows={rowsData}
//editRowId={editRowId}
//onEditRowIdChange={setEditRowId}
...
/>
For columns that hold values other than string, you'll need to define the setValue
function on the column so the updated value won't override the original value.
Example:
setValue: ({value, data, setRow, column}) => {
// value: '35',
// data: { ..., columnField: { fieldToUpdate: '27' }}
let rowClone = { ...data };
rowClone[column.field].fieldToUpdate = value;
setRow(rowClone);
}
Styling
Styling is done by CSS classes that you can easily override. the table's components are mapped with pre-defined classes that should cover any situation, and you can add your own custom class per column in the columns
configuration using the className
property.
| Component | All available class selectors |
|---|---|
| Wrapper | rgt-wrapper
|
| Header | rgt-header-container
|
| Search | rgt-search-container
rgt-search-label
rgt-search-icon
rgt-search-input
rgt-search-highlight
|
| Columns Visibility Manager | rgt-columns-manager-wrapper
rgt-columns-manager-button
rgt-columns-manager-button-active
rgt-columns-manager-popover
rgt-columns-manager-popover-open
rgt-columns-manager-popover-row
rgt-columns-manager-popover-title
rgt-columns-manager-popover-body
|
| Table | rgt-container
rgt-container-overlay
|
| Header Cell | rgt-cell-header
rgt-cell-header-[column.field]
rgt-cell-header-checkbox
rgt-cell-header-virtual-col
rgt-cell-header-sortable / rgt-cell-header-not-sortable
rgt-cell-header-sticky
rgt-cell-header-resizable / rgt-cell-header-not-resizable
rgt-cell-header-searchable / rgt-cell-header-not-searchable
rgt-cell-header-pinned
rgt-cell-header-pinned-left / rgt-cell-header-pinned-right
rgt-cell-header-inner-not-pinned-right
[column.className]
rgt-cell-header-inner
rgt-cell-header-inner-checkbox
rgt-resize-handle
rgt-sort-icon
rgt-sort-icon-ascending / rgt-sort-icon-descending
rgt-column-sort-ghost
|
| Cell | rgt-cell
rgt-cell-[column.field]
rgt-row-[rowNumber]
rgt-row-odd / rgt-row-even
rgt-row-hover
rgt-row-selectable / rgt-row-not-selectable
rgt-cell-inner
rgt-cell-checkbox
rgt-cell-virtual
rgt-cell-pinned
rgt-cell-pinned-left / rgt-cell-pinned-right
rgt-cell-editor
rgt-cell-editor-inner
rgt-cell-editor-input
rgt-row-selected
rgt-placeholder-cell
rgt-row-edit
|
| Footer | rgt-footer
rgt-footer-right-container
|
| Pagination | rgt-footer-pagination
rgt-footer-pagination-button
rgt-footer-pagination-input-container
rgt-footer-page-input
|
| Information | rgt-footer-items-information
rgt-footer-clear-selection-button
|
| PageSize | rgt-footer-page-size
rgt-footer-page-size-select
|
| (Utils) | rgt-text-truncate
rgt-clickable
rgt-disabled
rgt-disabled-button
rgt-flex-child
|
License
© MIT