1.1.2 • Published 1 year ago

@nadavshaar/react-grid-table v1.1.2

Weekly downloads
106
License
MIT
Repository
github
Last release
1 year ago

react-grid-table

A modular table, based on a CSS grid layout, optimized for fast configuration and deep customization.

NPM Downloads

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

Demo

Install

npm i @nadavshaar/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 '@nadavshaar/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

Support this package

paypal

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

nametypedescriptiondefault value
columns*array of objectscolumns configuration (details)
rowsarray of objectsrows data (details)
selectedRowsIdsarray of idsthe ids of all selected rows (details)
searchTextstringtext for search""
getIsRowSelectablefunctiona callback function that returns whether row selection for the current row should be selectable or disabledrow => true
getIsRowEditablefunctiona callback function that returns whether row editing for the current row should be allowed or notrow => true
editRowIdanythe id of the row that should switch to inline editing mode, (more details about row editing)null
pagenumbercurrent page number1
pageSizenumberthe selected page size20
sortobjectsort 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{ }
isLoadingbooleanwhether to display the loaderfalse

Configuration props

nametypedescriptiondefault value
rowIdFieldstringthe name of the field in the row's data that should be used as the row identifier - must be unique'id'
minColumnResizeWidthnumberminimum width for all columns while resizing (doesn't apply to 'checkbox' column)70
minSearchCharsnumberthe minimum characters to type before search will apply2
isHeaderStickybooleanwhether the table header cells will stick to the top when scrolling, or nottrue
isPaginatedbooleandetermine whether the pagination controls sholuld be shown in the footer and if the rows data should split into pagestrue
enableColumnsReorderbooleanwhether to allow column drag & drop for repositioningtrue
highlightSearchbooleanwhether to highlight the search termtrue
showSearchbooleanwhether to show the search component in the headertrue
showRowsInformationbooleanwhether to show the rows information component (located at the left side of the footer)true
showColumnVisibilityManagerbooleanwhether to display the columns visibility management button (located at the top right of the header)true
pageSizesarray of numberspage size options20, 50, 100
isVirtualScrollbooleanwhether to render items in a virtual scroll to enhance performance (useful when you have lots of rows in a page)true
selectAllModestringcontrols 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'
iconsobject of nodescustom icons config{ sortAscending, sortDescending, clearSelection, columnVisibility, search, loader }
textsobjectconfig 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' }
componentsobjectThis prop gives you the ability to override the internal components with your own custom components (see full list of supported components){ Default components }
additionalPropsobjectThis prop gives you the ability to pass props to the table's components/modules (see full list of supported additionalProps)additionalProps={{ header: { ... } }}

Event props

nametypedescriptionusage
onColumnsChangefunctiontriggers when the columns has been changedcolumns => { ... }
onSelectedRowsChangefunctiontriggers when rows selection has been changedselectedRowsIds => { ... }
onPageChangefunctiontriggers when page is changednextPage => { ... }
onPageSizeChangefunctiontriggers when page size is changednewPageSize => { ... }
onSearchTextChangefunctiontriggers when search text changedsearchText => { ... }
onSortChangefunctiontriggers when sort changed({colId, isAsc}) => { ... }
onRowClickfunctiontriggers when a row is clicked({ rowIndex, data, column, isEdit, event }, tableManager) => { ... }
onEditRowIdChangefunctiontriggers when rowEditId changedrowEditId => { ... }
onLoadfunctiontriggers when tableManager is initialized (details)tableManager => { ... }
onColumnResizeStartfunctiontriggers when column resize starts({event, target, column}) => { ... }
onColumnResizefunctiontriggers when column resize occur({event, target, column}) => { ... }
onColumnResizeEndfunctiontriggers when column resize ended({event, target, column}) => { ... }
onColumnReorderStartfunctiontriggers on column drag. the sort data supplied by react-sortable-hoc using the onSortStart prop(sortData, tableManager) => { ... }
onColumnReorderEndfunctiontriggers 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

nametypedescriptionusage/default value
onRowsRequestfunctiontriggers when new rows should be fetchedsee example
onRowsChangefunctiontriggers when the rows have changedsee example
onTotalRowsChangefunctiontriggers when the total number of rows have changedsee example
onRowsResetfunctiontriggers when the accumulated rows needs to be reset (when searching or sorting)see example
batchSizenumberdefines the amount of rows that will be requested by onRowsRequest propthe page size of the table
requestDebounceTimeoutnumberdefines the amount of debouncing time for triggering the onRowsRequest prop300
totalRowsnumberthe 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:

nametypedescriptiondefault value
id*anya unique identifier for the column, setting it to 'checkbox' will generate a rows selction column (more details about checkbox column)---
fieldstringthe name of the field as in the row data---
labelstringthe label to display in the header cellthe field property
pinnedbooleanwhether the column will be pinned to the side, supported only in the first and last columnsfalse
visiblebooleanwhether to display the columntrue
classNamestringa custom class selector for all column cells""
widthstringthe initial width of the column in grid values (full list of values)"200px"
minResizeWidthnumberthe minimum width of the column when resizingminColumnResizeWidth prop
maxResizeWidthnumber, nullthe maximum width of the column when resizingnull
getValuefunctionused for getting the cell value (usefull when the cell value is not a string - details)({value, column}) => value
setValuefunctionused for updating the cell value (usefull when the cell value is not a string - details)({ value, data, setRow, column }) => { setRow({ ...data, [column.field]: value}) }
searchablebooleanwhether to apply search filtering on the columntrue
editablebooleanwhether to allow editing for the columntrue
sortablebooleanwhether to allow sort for the columntrue
resizablebooleanwhether to allow resizing for the columntrue
searchfunctionthe search function for the column({value, searchText}) => value.toString().toLowerCase().includes(searchText.toLowerCase())
sortfunctionthe 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; }
cellRendererfunctionused for custom rendering the cell component ({ tableManager, value, data, column, colIndex, rowIndex }) => ( children )---
headerCellRendererfunctionused for custom rendering the header cell component ({ tableManager, column }) => ( children )---
editorCellRendererfunctionused for custom rendering the cell component in edit mode ({ tableManager, value, data, column, colIndex, rowIndex, onChange }) => ( children )---
placeHolderRendererfunctionused 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:

nametypedescriptiondefault value
id*'checkbox'will generate a rows selction column---
pinnedbooleanwhether the column will be pinned to the side, supported only in the first and last columnsfalse
visiblebooleanwhether to display the columntrue
classNamestringa custom class for all column cells""
widthstringthe initial width of the column in grid values (full list of values)"max-content"
minResizeWidthnumberthe minimum width of the column when resizing0
maxResizeWidthnumber, nullthe maximum width of the column when resizingnull
resizablebooleanwhether to allow resizing for the columnfalse
cellRendererfunctionused for custom rendering the checkbox cell({ tableManager, value, data, column, colIndex, rowIndex, onChange, disabled}) => ( <input type="checkbox" onChange={ onChange } checked={ value } disabled={ disabled } /> )
headerCellRendererfunctionused 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":"wotham0@skyrock.com",
  "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:

componentrequired propsoptional props
HeadertableManager---
SearchtableManagervalue onChange
ColumnVisibilitytableManagercolumns onChange
HeaderCelltableManagercolumn
HeaderSelectionCelltableManagercolumn ref onChange checked disabled
CelltableManagervalue
EditorCelltableManagervalue data column colIndex rowIndex onChange
SelectionCelltableManagervalue disabled onChange
PlaceHolderCelltableManager---
LoadertableManager---
NoResultstableManager---
DragHandle------
FootertableManager---
InformationtableManagertotalCount pageSize pageCount selectedCount
PageSizetableManagervalue onChange options
PaginationtableManagerpage 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

nametypedescriptiondefault value
rowIdFieldstringthe name of the field in the row's data that should be used as the row identifier - must be unique'id'
minColumnResizeWidthnumberminimum width for all columns while resizing (doesn't apply to 'checkbox' column)70
minSearchCharsnumberthe minimum characters to type before search will apply2
isHeaderStickybooleanwhether the table header cells will stick to the top when scrolling, or nottrue
isPaginatedbooleandetermine whether the pagination controls sholuld be shown in the footer and if the rows data should split into pagestrue
enableColumnsReorderbooleanwhether to allow column drag & drop for repositioningtrue
highlightSearchbooleanwhether to highlight the search termtrue
showSearchbooleanwhether to show the search component in the headertrue
showRowsInformationbooleanwhether to show the rows information component (located at the left side of the footer)true
showColumnVisibilityManagerbooleanwhether to display the columns visibility management button (located at the top right of the header)true
pageSizesarray of numberspage size options20, 50, 100
requestDebounceTimeoutnumberdefines the amount of debouning time for triggering the onRowsRequest prop300
isVirtualScrollbooleanwhether to render items in a virtual scroll to enhance performance (useful when you have lots of rows in a page)true
tableHasSelectionbooleanwether the table has a checkbox column to control rows selection---
componentsobjectthe 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}
additionalPropsobjectadditional props that are passed to the internal components (see full list of additionalProps){}
iconsobjectthe icons that are in use by the table{ sortAscending, sortDescending, clearSelection, columnVisibility, search, loader }
textsobjectthe texts that are in use by the table{ search, totalRows, rows, selected, rowsPerPage, page, of, prev, next, columnVisibility }

refs

nametypedescription
rgtRefobjectthe ref object of the wrapper element
tableRefobjectthe ref object of the table container element

columnsApi

nametypedescriptionusage
columnsarraycolumns configuration---
visibleColumnsarraythe columns that are visible---
setColumnsfunctionupdates the columnssetColumns(columns)

columnsVisibilityApi

nametypedescriptionusage
toggleColumnVisibilityfunctiontoggles a column's visibility by its idtoggleColumnVisibility(column.id)

searchApi

nametypedescriptionusage
searchTextstringtext for search---
validSearchTextstringis an empty string if the searched text did not pass the minSearchChars, if it does pass, it will be equal to searchText---
setSearchTextfunctionupdates the search textsetSearchText('hello')
searchRowsfunctionfilters rows based on the search text, using the search method defined on the columnssearchRows(rows)
valuePassesSearchfunctionreturns true if a value passes the search for a certain columnvaluePassesSearch('hello', column)

sortApi

nametypedescriptionusage
sortobjectthe 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---
setSortfunctionupdates the sort objectsetSort({colId: 5, isAsc: false})
sortRowsfunctionsorts rows based on the selected direction using the sort method defined on the columnsortRows(rows)
toggleSortfunctiontoggles a column's sort steps from ascending, to descending and to nonetoggleSort(column.id)

rowsApi

nametypedescriptionusage
rowsarraythe rows data (in sync mode - the rows data after the search filter and the sort)---
originalRowsarraythe rows data untouched (in sync mode - the rows data before the search filter)---
setRowsfunctionupdates the rowssetRows(rows)
totalRowsnumberthe total number of rows---
setTotalRowsfunctionupdates the total number of rowssetTotalRows(1000)

paginationApi

nametypedescriptionusage
pagenumberthe current page number---
setPagefunctionupdates the page numbersetPage(3)
pageSizenumberthe selected page size---
setPageSizefunctionupdates the page sizesetPageSize(20)
pageRowsarraythe rows in the current page---
totalPagesnumberthe total number of pages---

rowSelectionApi

nametypedescriptionusage
selectedRowsIdsarray of idsthe ids of all selected rows---
setSelectedRowsIdsfunctionupdates the selected rowssetSelectedRowsIds([1,3,5])
toggleRowSelectionfunctiontoggles selection of a row by its idtoggleRowSelection(row.id)
getIsRowSelectablefunctiondetermains whether a row can be selectedgetIsRowSelectable(row.id)
selectAll.modestringthe 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.disabledbooleanwhether the select all button should be disabled because there are no selectable rows that match the selectAll.mode---
selectAll.checkedbooleanwhether all the rows that match the selectAll.mode are selected---
selectAll.indeterminatebooleanwhether only some of the rows that match the selectAll.mode are selected---
selectAll.onChangefunctionselects/unselects all rows that match the selectAll.modeselectAll.onChange()
selectAll.refrefa ref that can be added to the select all checkbox to enable auto setting of indeterminate state---

rowEditApi

nametypedescriptionusage
editRowobjectthe row's data that is currently being edited---
editRowIdanythe id of the row that is currently being edited---
getIsRowEditablefunctiondetermains whether a row can be editedgetIsRowEditable(row)
setEditRowfunctionupdates the row's data of the currently edited rowsetEditRow(row)
setEditRowIdfunctionupdates the row id of the currently edited row, you can pass null to switch back from edit modesetEditRowId(row.id)

asyncApi

nametypedescriptionusage / default value
isLoadingbooleanwhether a request for new rows is still pending---
mergeRowsAtfunctionmerges arrays of rows at a certain index while filling "holes" with nullsmergeRowsAt(rows, moreRows, atIndex)
resetRowsfunctiondrops the accumulated rows, which will trigger a new requestresetRows()
batchSizenumberdefines the amount of rows that will be requested by onRowsRequest proppaginationApi.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:

nametypedescription
rows*array of objectsrows 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:

nametypedescription
onRowsRequest*async functionShould 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:

nametypedescription
onRowsRequest*async functionShould return a promise that resolves to {rows, totalRows}
rows*array of objectsrows data (details)
onRowsChange*functionShould be used to set the current data
totalRows*numberShould contain the current data length
onTotalRowsChange*functionShould 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:

nametypedescription
onRowsRequest*async functionShould update the rows props according to the request
rows*array of objectsrows data (details)
totalRows*numberShould contain the current data length
onRowsReset*functionShould 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)}
            >&#x270E;</button>
        ),
        editorCellRenderer: ({ tableManager, value, data, column, colIndex, rowIndex, onChange }) => (
            <div style={{display: 'inline-flex'}}>
                <button 
                    style={{marginLeft: 20}} 
                    onClick={e => tableManager.rowEditApi.setEditRowId(null)}
                >&#x2716;</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);
                    }
                }>&#x2714;</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.

ComponentAll available class selectors
Wrapperrgt-wrapper
Headerrgt-header-container
Searchrgt-search-container rgt-search-label rgt-search-icon rgt-search-input rgt-search-highlight
Columns Visibility Managerrgt-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
Tablergt-container rgt-container-overlay
Header Cellrgt-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
Cellrgt-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
Footerrgt-footer rgt-footer-right-container
Paginationrgt-footer-pagination rgt-footer-pagination-button rgt-footer-pagination-input-container rgt-footer-page-input
Informationrgt-footer-items-information rgt-footer-clear-selection-button
PageSizergt-footer-page-size rgt-footer-page-size-select
(Utils)rgt-text-truncate rgt-clickable rgt-disabled rgt-disabled-button rgt-flex-child

License

© MIT