Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3aef7d3379 | ||
|
|
d9a4b6bd85 | ||
|
|
d50ec588bc | ||
|
|
f9242f604d | ||
|
|
59f6a768a7 | ||
|
|
bcd0da7379 | ||
|
|
14ea0abf27 | ||
|
|
696edacd48 | ||
|
|
f665972584 | ||
|
|
deb15c3908 | ||
|
|
c77ffacdc9 | ||
|
|
d4303a2468 | ||
|
|
dc629aedfa | ||
|
|
134f3dc064 | ||
|
|
a667b7cb13 | ||
|
|
4f01c32216 | ||
|
|
5145a632c9 | ||
|
|
8508a6567d | ||
|
|
1e80dd85d6 | ||
|
|
3cbb49fc37 |
@@ -77,7 +77,7 @@ Hooks for building **lightweight, fast and extendable datagrids** for React
|
||||
</a>
|
||||
</td>
|
||||
<td align="center" valign="middle">
|
||||
<a href="https://zappi.com/web" target="_blank">
|
||||
<a href="https://zappi.io/web/" target="_blank">
|
||||
<img width='230' src="https://raw.githubusercontent.com/tannerlinsley/files/master/images/patreon/sponsor-zappi.png">
|
||||
</a>
|
||||
</td>
|
||||
@@ -111,10 +111,9 @@ Hooks for building **lightweight, fast and extendable datagrids** for React
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
<li>Nicholas Kaufmann</li>
|
||||
<li>Pekka</li>
|
||||
</ul>
|
||||
<div>Nicholas Kaufmann</div>
|
||||
<div><a href="https://www.metso.com/">Pekka</a></div>
|
||||
<div>Jon Eickmeier</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
@@ -129,14 +128,12 @@ Hooks for building **lightweight, fast and extendable datagrids** for React
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
<li>Sortmy.games</li>
|
||||
<li>Hugo Meissner</li>
|
||||
<li>Benoit Leger-Derville</li>
|
||||
<li>Thomas Funk</li>
|
||||
<li>Dan Houle</li>
|
||||
<li>David Pickut</li>
|
||||
</ul>
|
||||
<div>Sortmy.games</div>
|
||||
<div>Hugo Meissner</div>
|
||||
<div>Benoit Leger-Derville</div>
|
||||
<div>Thomas Funk</div>
|
||||
<div>Dan Houle</div>
|
||||
<div>David Pickut</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
@@ -146,7 +143,6 @@ Hooks for building **lightweight, fast and extendable datagrids** for React
|
||||
<img width="150" alt="" src="https://raw.githubusercontent.com/tannerlinsley/files/master/images/patreon/become-a-patron.png" />
|
||||
</a>
|
||||
|
||||
|
||||
# Documentation
|
||||
|
||||
- [Installation](#installation)
|
||||
@@ -182,14 +178,18 @@ import {
|
||||
|
||||
# Examples
|
||||
|
||||
- [Basic - Client Side](https://codesandbox.io/s/github/tannerlinsley/react-table/tree/master/examples/basic-client-side)
|
||||
- [Sorting - Client Side](https://codesandbox.io/s/github/tannerlinsley/react-table/tree/master/examples/sorting-client-side)
|
||||
- [Filtering - Client Side](https://codesandbox.io/s/github/tannerlinsley/react-table/tree/master/examples/filtering-client-side)
|
||||
- [Grouping - Client Side](https://codesandbox.io/s/github/tannerlinsley/react-table/tree/master/examples/grouping-client-side)
|
||||
- [Pagination - Client Side](https://codesandbox.io/s/github/tannerlinsley/react-table/tree/master/examples/pagination-client-side)
|
||||
- [Row Selection - Client Side](https://codesandbox.io/s/github/tannerlinsley/react-table/tree/master/examples/row-selection-client-side)
|
||||
- [Expanding - Client Side](https://codesandbox.io/s/github/tannerlinsley/react-table/tree/master/examples/expanding-client-side)
|
||||
- [Sub Components](https://codesandbox.io/s/github/tannerlinsley/react-table/tree/master/examples/sub-components)
|
||||
- Simple
|
||||
- [Basic](https://codesandbox.io/s/github/tannerlinsley/react-table/tree/master/examples/basic)
|
||||
- [Sorting](https://codesandbox.io/s/github/tannerlinsley/react-table/tree/master/examples/sorting)
|
||||
- [Filtering](https://codesandbox.io/s/github/tannerlinsley/react-table/tree/master/examples/filtering)
|
||||
- [Grouping](https://codesandbox.io/s/github/tannerlinsley/react-table/tree/master/examples/grouping)
|
||||
- [Pagination](https://codesandbox.io/s/github/tannerlinsley/react-table/tree/master/examples/pagination)
|
||||
- [Row Selection](https://codesandbox.io/s/github/tannerlinsley/react-table/tree/master/examples/row-selection)
|
||||
- [Expanding](https://codesandbox.io/s/github/tannerlinsley/react-table/tree/master/examples/expanding)
|
||||
- [Sub Components](https://codesandbox.io/s/github/tannerlinsley/react-table/tree/master/examples/sub-components)
|
||||
- [Editable Data](https://codesandbox.io/s/github/tannerlinsley/react-table/tree/master/examples/editable-data)
|
||||
- Controlled
|
||||
- [Pagination (Controlled)](https://codesandbox.io/s/github/tannerlinsley/react-table/tree/master/examples/pagination-controlled)
|
||||
|
||||
# Concepts
|
||||
|
||||
@@ -222,6 +222,8 @@ React Table is essentially a compatible collection of **custom React hooks**:
|
||||
|
||||
- The primary React Table hook
|
||||
- [`useTable`](#usetable)
|
||||
- Utility Hooks
|
||||
- [`useTableState`](#useTableState)
|
||||
- Plugin Hooks
|
||||
- Core Plugin Hooks
|
||||
- [`useTableState`](#useTableState)
|
||||
@@ -231,6 +233,8 @@ React Table is essentially a compatible collection of **custom React hooks**:
|
||||
- [`useExpanded`](#useExpanded)
|
||||
- [`usePagination`](#usePagination)
|
||||
- [`useTokenPagination`](#useTokenPagination)
|
||||
- [`useRowSelect`](#useRowSelect)
|
||||
- [`useRowState`](#useRowState)
|
||||
- Layout Plugin Hooks
|
||||
- [`useFlexLayout`](#useFlexLayout)
|
||||
- [`useAbsoluteLayout`](#useAbsoluteLayout) (coming soon!)
|
||||
@@ -311,6 +315,11 @@ The following options are supported via the main options object passed to `useTa
|
||||
- The default column object for every column passed to React Table.
|
||||
- Column-specific properties will override the properties in this object, eg. `{ ...defaultColumn, ...userColumn }`
|
||||
- This is particularly useful for adding global column properties. For instance, when using the `useFilters` plugin hook, add a default `Filter` renderer for every column, eg.`{ Filter: MyDefaultFilterComponent }`
|
||||
- `initialRowStateKey: String`
|
||||
- Optional
|
||||
- Defaults to `initialState`
|
||||
- This key is used to look for the initial state of a row when initializing the `rowState` for a`data` array.
|
||||
- If the value located at `row[initialRowStateKey]` is falsey, `{}` will be used instead.
|
||||
- `debug: Bool`
|
||||
- Optional
|
||||
- A flag to turn on debug mode.
|
||||
@@ -342,13 +351,13 @@ The following options are supported on any column object you can pass to `column
|
||||
- The data model for hidden columns is still calculated including sorting, filters, and grouping.
|
||||
- `Header: String | Function | React.Component => JSX`
|
||||
- Optional
|
||||
- Defaults to `({ id }) => id`
|
||||
- Defaults to `() => null`
|
||||
- Receives the table instance and column model as props
|
||||
- Must either be a **string or return valid JSX**
|
||||
- If a function/component is passed, it will be used for formatting the header value, eg. You can use a `Header` function to dynamically format the header using any table or column state.
|
||||
- `Cell: Function | React.Component => JSX`
|
||||
- Optional
|
||||
- Defaults to `({ value }) => value`
|
||||
- Defaults to `({ cell: { value } }) => value`
|
||||
- Receives the table instance and cell model as props
|
||||
- Must return valid JSX
|
||||
- This function (or component) is primarily used for formatting the column value, eg. If your column accessor returns a date object, you can use a `Cell` function to format that date to a readable format.
|
||||
@@ -384,6 +393,14 @@ The following properties are available on the table instance returned from `useT
|
||||
- `flatRows: Array<Row>`
|
||||
- An array of all rows, including subRows which have been flattened into the order in which they were detected (depth first)
|
||||
- This can be helpful in calculating total row counts that must include subRows
|
||||
- `setRowState: Function(rowPath, updater: Function | any) => void`
|
||||
- This function can be used to update the internal state for any row.
|
||||
- Pass it a valid `rowPath` array and `updater`. The `updater` may be a value or function, similar to `React.useState`'s usage.
|
||||
- If `updater` is a function, it will be passed the previous value
|
||||
- `setCellState: Function(rowPath, columnID, updater: Function | any) => void`
|
||||
- This function can be used to update the internal state for any cell.
|
||||
- Pass it a valid `rowPath` array, `columnID` and `updater`. The `updater` may be a value or function, similar to `React.useState`'s usage.
|
||||
- If `updater` is a function, it will be passed the previous value
|
||||
|
||||
### `HeaderGroup` Properties
|
||||
|
||||
@@ -407,7 +424,8 @@ The following properties are available on every `Column` object returned by the
|
||||
- `visible: Boolean`
|
||||
- The resolved visible state for the column, derived from the column's `show` property
|
||||
- `render: Function(type: String | Function | Component, ?props)`
|
||||
- This function is used to render content in context of a column.
|
||||
- This function is used to render content with the added context of a column.
|
||||
- The entire table `instance` will be passed to the renderer with the addition of a `column` property, containing a reference to the column
|
||||
- If `type` is a string, will render using the `column[type]` renderer. React Table ships with default `Header` renderers. Other renderers like `Filter` are available via hooks like `useFilters`.
|
||||
- If a function or component is passed instead of a string, it will be be passed the table instance and column model as props and is expected to return any valid JSX.
|
||||
- `getHeaderProps: Function(?props)`
|
||||
@@ -439,6 +457,10 @@ The following additional properties are available on every `row` object returned
|
||||
- This array is used with plugin hooks like `useExpanded` and `useGroupBy` to compute expanded states for individual rows.
|
||||
- `subRows: Array<Row>`
|
||||
- If subRows were detect on the original data object, this will be an array of those materialized row objects.
|
||||
- `state: Object`
|
||||
|
||||
- The current state of the row. It's lifespan is attached to that of the original `data` array. When the raw `data` is changed, this state value is reset to the row's initial value (using the `initialRowStateKey` option).
|
||||
- Can be updated via `instance.setRowState` or the row's `setState` function.
|
||||
|
||||
### Cell Properties
|
||||
|
||||
@@ -458,7 +480,8 @@ The following additional properties are available on every `Cell` object returne
|
||||
- Custom props may be passed. **NOTE: Custom props will override built-in table props, so be careful!**
|
||||
- `render: Function(type: String | Function | Component, ?props)`
|
||||
- **Required**
|
||||
- This function is used to render content in context of a cell.
|
||||
- This function is used to render content with the added context of a cell.
|
||||
- The entire table `instance` will be passed to the renderer with the addition of `column`, `row` and `cell` properties, containing a reference to each respective item.
|
||||
- If `type` is a string, will render using the `column[type]` renderer. React Table ships with a default `Cell` renderer. Other renderers like `Aggregated` are available via hooks like `useFilters`.
|
||||
- If a function or component is passed instead of a string, it will be be passed the table instance and cell model as props and is expected to return any valid JSX.
|
||||
|
||||
@@ -566,6 +589,11 @@ The following options are supported via the main options object passed to `useTa
|
||||
- Disables sorting for every column in the entire table.
|
||||
- `disableMultiSort: Bool`
|
||||
- Disables multi-sorting for the entire table.
|
||||
- `isMultiSortEvent: Function`
|
||||
- Allows to override default multisort behaviour(i.e. multisort applies when shift key is presssed), if this function is provided then returned boolean value from this function will make decision whether newly applied sort action will be considered as multisort or not.
|
||||
- Receives `event` as argument.
|
||||
- `maxMultiSortColCount: Number`
|
||||
- Limit on max number of columns for multisort, e.g. if set to 3, and suppose table is sorted by `[A, B, C]` and then clicking `D` for sorting should result in table sorted by `[B, C , D]`
|
||||
- `disableSortRemove: Bool`
|
||||
- If true, the un-sorted state will not be available to columns once they have been sorted.
|
||||
- `disableMultiRemove: Bool`
|
||||
@@ -771,7 +799,7 @@ The following properties are available on every `Column` object returned by the
|
||||
- An column-level function used to update the filter value for this column
|
||||
- `filterValue: any`
|
||||
- The current filter value for this column, resolved from the table state's `filters` object
|
||||
- `preFilteredRows: Array<row>`
|
||||
- `preFilteredColumnRows: Array<row>`
|
||||
- The array of rows that were originally passed to this columns filter **before** they were filtered.
|
||||
- This array of rows can be useful if building faceted filter options.
|
||||
|
||||
@@ -1038,7 +1066,7 @@ function App() {
|
||||
// then sum any of those counts if they are
|
||||
// aggregated further
|
||||
aggregate: ['sum', 'count'],
|
||||
Aggregated: ({ value }) => `${value} Names`,
|
||||
Aggregated: ({ cell: { value } }) => `${value} Names`,
|
||||
},
|
||||
{
|
||||
Header: 'Last Name',
|
||||
@@ -1048,7 +1076,7 @@ function App() {
|
||||
// being aggregated, then sum those counts if
|
||||
// they are aggregated further
|
||||
aggregate: ['sum', 'uniqueCount'],
|
||||
Aggregated: ({ value }) => `${value} Unique Names`,
|
||||
Aggregated: ({ cell: { value } }) => `${value} Unique Names`,
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -1060,14 +1088,14 @@ function App() {
|
||||
accessor: 'age',
|
||||
// Aggregate the average age of visitors
|
||||
aggregate: 'average',
|
||||
Aggregated: ({ value }) => `${value} (avg)`,
|
||||
Aggregated: ({ cell: { value } }) => `${value} (avg)`,
|
||||
},
|
||||
{
|
||||
Header: 'Visits',
|
||||
accessor: 'visits',
|
||||
// Aggregate the sum of all visits
|
||||
aggregate: 'sum',
|
||||
Aggregated: ({ value }) => `${value} (total)`,
|
||||
Aggregated: ({ cell: { value } }) => `${value} (total)`,
|
||||
},
|
||||
{
|
||||
Header: 'Status',
|
||||
@@ -1078,7 +1106,7 @@ function App() {
|
||||
accessor: 'progress',
|
||||
// Use our custom roundedMedian aggregator
|
||||
aggregate: roundedMedian,
|
||||
Aggregated: ({ value }) => `${value} (med)`,
|
||||
Aggregated: ({ cell: { value } }) => `${value} (med)`,
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -1294,7 +1322,7 @@ The following options are supported via the main options object passed to `useTa
|
||||
- Normally, any changes detected to `rows`, `state.filters`, `state.groupBy`, or `state.sortBy` will trigger the `pageIndex` to be reset to `0`
|
||||
- If set to `true`, the `pageIndex` will not be automatically set to `0` when these dependencies change.
|
||||
|
||||
### Instance Variables
|
||||
### Instance Properties
|
||||
|
||||
The following values are provided to the table `instance`:
|
||||
|
||||
@@ -1513,6 +1541,225 @@ function App() {
|
||||
|
||||
> Documentation Coming Soon...
|
||||
|
||||
## `useRowSelect`
|
||||
|
||||
- Plugin Hook
|
||||
- Optional
|
||||
|
||||
`useRowSelect` is the hook that implements **basic row selection**. For more information on row selection, see [Row Selection](TODO)
|
||||
|
||||
### Table Options
|
||||
|
||||
The following options are supported via the main options object passed to `useTable(options)`
|
||||
|
||||
- `state[0].selectedRows: Array<RowPathKey>`
|
||||
- Optional
|
||||
- Defaults to `[]`
|
||||
- If a row's path key (eg. a row path of `[1, 3, 2]` would have a path key of `1.3.2`) is found in this array, it will have a selected state.
|
||||
- `manualRowSelectedKey: String`
|
||||
- Optional
|
||||
- Defaults to `selected`
|
||||
- If this key is found on the **original** data row, and it is true, this row will be manually selected
|
||||
|
||||
### Instance Properties
|
||||
|
||||
The following values are provided to the table `instance`:
|
||||
|
||||
- `toggleRowSelected: Function(rowPath: String, ?set: Bool) => void`
|
||||
- Use this function to toggle a row's selected state.
|
||||
- Optionally pass `true` or `false` to set it to that state
|
||||
- `toggleRowSelectedAll: Function(?set: Bool) => void`
|
||||
- Use this function to toggle all rows as select or not
|
||||
- Optionally pass `true` or `false` to set all rows to that state
|
||||
- `getToggleAllRowsSelectedProps: Function(props) => props`
|
||||
- Use this function to get the props needed for a **select all checkbox**.
|
||||
- Props:
|
||||
- `onChange: Function()`
|
||||
- `style.cursor: 'pointer'`
|
||||
- `checked: Bool`
|
||||
- `title: 'Toggle All Rows Selected'`
|
||||
- `allRowsSelected: Bool`
|
||||
- Will be `true` if all rows are selected.
|
||||
- If at least one row is not selected, will be `false`
|
||||
|
||||
### Example
|
||||
|
||||
```js
|
||||
function Table({ columns, data }) {
|
||||
// Use the state and functions returned from useTable to build your UI
|
||||
const {
|
||||
getTableProps,
|
||||
headerGroups,
|
||||
rows,
|
||||
prepareRow,
|
||||
state: [{ selectedRows }],
|
||||
} = useTable(
|
||||
{
|
||||
columns,
|
||||
data,
|
||||
},
|
||||
useRowSelect
|
||||
)
|
||||
|
||||
// Render the UI for your table
|
||||
return (
|
||||
<>
|
||||
<table {...getTableProps()}>
|
||||
<thead>
|
||||
{headerGroups.map(headerGroup => (
|
||||
<tr {...headerGroup.getHeaderGroupProps()}>
|
||||
{headerGroup.headers.map(column => (
|
||||
<th {...column.getHeaderProps()}>{column.render('Header')}</th>
|
||||
))}
|
||||
</tr>
|
||||
))}
|
||||
</thead>
|
||||
<tbody>
|
||||
{rows.map(
|
||||
(row, i) =>
|
||||
prepareRow(row) || (
|
||||
<tr {...row.getRowProps()}>
|
||||
{row.cells.map(cell => {
|
||||
return (
|
||||
<td {...cell.getCellProps()}>{cell.render('Cell')}</td>
|
||||
)
|
||||
})}
|
||||
</tr>
|
||||
)
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
<p>Selected Rows: {selectedRows.length}</p>
|
||||
<pre>
|
||||
<code>{JSON.stringify({ selectedRows }, null, 2)}</code>
|
||||
</pre>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
function App() {
|
||||
const columns = React.useMemo(
|
||||
() => [
|
||||
// Let's make a column for selection
|
||||
{
|
||||
id: 'selection',
|
||||
// The header can use the table's getToggleAllRowsSelectedProps method
|
||||
// to render a checkbox
|
||||
Header: ({ getToggleAllRowsSelectedProps }) => (
|
||||
<div>
|
||||
<input type="checkbox" {...getToggleAllRowsSelectedProps()} />
|
||||
</div>
|
||||
),
|
||||
// The cell can use the individual row's getToggleRowSelectedProps method
|
||||
// to the render a checkbox
|
||||
Cell: ({ row }) => (
|
||||
<div>
|
||||
<input type="checkbox" {...row.getToggleRowSelectedProps()} />
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
Header: 'Name',
|
||||
columns: [
|
||||
{
|
||||
Header: 'First Name',
|
||||
accessor: 'firstName',
|
||||
},
|
||||
{
|
||||
Header: 'Last Name',
|
||||
accessor: 'lastName',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
Header: 'Info',
|
||||
columns: [
|
||||
{
|
||||
Header: 'Age',
|
||||
accessor: 'age',
|
||||
},
|
||||
{
|
||||
Header: 'Visits',
|
||||
accessor: 'visits',
|
||||
},
|
||||
{
|
||||
Header: 'Status',
|
||||
accessor: 'status',
|
||||
},
|
||||
{
|
||||
Header: 'Profile Progress',
|
||||
accessor: 'progress',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
[]
|
||||
)
|
||||
|
||||
const data = React.useMemo(() => makeData(10), [])
|
||||
|
||||
return <Table columns={columns} data={data} />
|
||||
}
|
||||
```
|
||||
|
||||
## `useRowState`
|
||||
|
||||
- Plugin Hook
|
||||
- Optional
|
||||
|
||||
`useRowState` is a plugin hook that implements **basic state management for _prepared_ rows and their cells**.
|
||||
|
||||
### Table Options
|
||||
|
||||
The following options are supported via the main options object passed to `useTable(options)`
|
||||
|
||||
- `state[0].rowState: Object<RowPathKey:Object<any, cellState: {columnID: Object}>>`
|
||||
- Optional
|
||||
- Defaults to `{}`
|
||||
- If a row's path key (eg. a row path of `[1, 3, 2]` would have a path key of `1.3.2`) is found in this array, it will have the state of the value corresponding to that key.
|
||||
- Individual row states can contain anything, but they also contain a `cellState` key, which provides cell-level state based on column ID's to every
|
||||
**prepared** cell in the table.
|
||||
- `initialRowStateAccessor: Function`
|
||||
- Optional
|
||||
- This function may optionally return the initial state for a row.
|
||||
- If this function is defined, it will be passed a `Row` object, from which you can return a value to use as the initial state, eg. `row => row.original.initialState`
|
||||
|
||||
### Instance Properties
|
||||
|
||||
The following values are provided to the table `instance`:
|
||||
|
||||
- `setRowState: Function(rowPath: Array<string>, updater: Function | Any) => void`
|
||||
- Use this function to programmatically update the state of a row.
|
||||
- `updater` can be a function or value. If a `function` is passed, it will receive the current value and expect a new one to be returned.
|
||||
- `setCellState: Function(rowPath: Array<string>, columnID: String, updater: Function | Any) => void`
|
||||
- Use this function to programmatically update the cell of a row.
|
||||
- `updater` can be a function or value. If a `function` is passed, it will receive the current value and expect a new one to be returned.
|
||||
|
||||
### Row Properties
|
||||
|
||||
The following additional properties are available on every **prepared** `row` object returned by the table instance.
|
||||
|
||||
- `state: Object`
|
||||
- This is the state object for each row, pre-mapped to the row from the table state's `rowState` object via `rowState[row.path.join('.')]`
|
||||
- May also contain a `cellState` key/value pair, which is used to provide individual cell states to this row's cells
|
||||
- `setState: Function(updater: Function | any)`
|
||||
- Use this function to programmatically update the state of a row.
|
||||
- `updater` can be a function or value. If a `function` is passed, it will receive the current value and expect a new one to be returned.
|
||||
|
||||
### Cell Properties
|
||||
|
||||
The following additional properties are available on every `Cell` object returned in an array of `cells` on every row object.
|
||||
|
||||
- `state: Object`
|
||||
- This is the state object for each cell, pre-mapped to the cell from the table state's `rowState` object via `rowState[row.path.join('.')].cellState[columnID]`
|
||||
- `setState: Function(updater: Function | any)`
|
||||
- Use this function to programmatically update the state of a cell.
|
||||
- `updater` can be a function or value. If a `function` is passed, it will receive the current value and expect a new one to be returned.
|
||||
|
||||
### Example
|
||||
|
||||
> Have an example of using useRowState? Submit a PR to add it here!
|
||||
|
||||
## `useTableState`
|
||||
|
||||
- Optional
|
||||
|
||||
@@ -13,4 +13,5 @@ module.exports = {
|
||||
rootDir: path.resolve(__dirname, '../../'),
|
||||
roots: ['<rootDir>/src', __dirname],
|
||||
transformIgnorePatterns: ['node_modules'],
|
||||
snapshotSerializers: [require.resolve('snapshot-diff/serializer.js')],
|
||||
}
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
import '@testing-library/jest-dom/extend-expect'
|
||||
import 'snapshot-diff/extend-expect'
|
||||
|
||||
|
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 3.8 KiB |
@@ -0,0 +1,6 @@
|
||||
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app) and Rescripts.
|
||||
|
||||
You can:
|
||||
|
||||
- [Open this example in a new CodeSandbox](https://codesandbox.io/s/github/tannerlinsley/react-table/tree/master/examples/editable-data)
|
||||
- `yarn` and `yarn start` to run and edit the example
|
||||
|
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 3.8 KiB |
@@ -0,0 +1,275 @@
|
||||
import React from 'react'
|
||||
import styled from 'styled-components'
|
||||
import { useTable, usePagination } from 'react-table'
|
||||
|
||||
import makeData from './makeData'
|
||||
|
||||
const Styles = styled.div`
|
||||
padding: 1rem;
|
||||
|
||||
table {
|
||||
border-spacing: 0;
|
||||
border: 1px solid black;
|
||||
|
||||
tr {
|
||||
:last-child {
|
||||
td {
|
||||
border-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
th,
|
||||
td {
|
||||
margin: 0;
|
||||
padding: 0.5rem;
|
||||
border-bottom: 1px solid black;
|
||||
border-right: 1px solid black;
|
||||
|
||||
:last-child {
|
||||
border-right: 0;
|
||||
}
|
||||
|
||||
input {
|
||||
font-size: 1rem;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
border: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.pagination {
|
||||
padding: 0.5rem;
|
||||
}
|
||||
`
|
||||
|
||||
// Create an editable cell renderer
|
||||
const EditableCell = ({
|
||||
value: initialValue,
|
||||
row: { index },
|
||||
column: { id },
|
||||
updateMyData, // This is a custom function that we supplied to our table instance
|
||||
}) => {
|
||||
// We need to keep and update the state of the cell normally
|
||||
const [value, setValue] = React.useState(initialValue)
|
||||
const onChange = e => {
|
||||
setValue(e.target.value)
|
||||
}
|
||||
// We'll only update the external data when the input is blurred
|
||||
const onBlur = () => {
|
||||
updateMyData(index, id, value)
|
||||
}
|
||||
return <input value={value} onChange={onChange} onBlur={onBlur} />
|
||||
}
|
||||
|
||||
// Set our editable cell renderer as the default Cell renderer
|
||||
const defaultColumn = {
|
||||
Cell: EditableCell,
|
||||
}
|
||||
|
||||
// Be sure to pass our updateMyData and the disablePageResetOnDataChange option
|
||||
function Table({ columns, data, updateMyData, disablePageResetOnDataChange }) {
|
||||
// For this example, we're using pagination to illustrate how to stop
|
||||
// the current page from resetting when our data changes
|
||||
// Otherwise, nothing is different here.
|
||||
const {
|
||||
getTableProps,
|
||||
headerGroups,
|
||||
prepareRow,
|
||||
page,
|
||||
canPreviousPage,
|
||||
canNextPage,
|
||||
pageOptions,
|
||||
pageCount,
|
||||
gotoPage,
|
||||
nextPage,
|
||||
previousPage,
|
||||
setPageSize,
|
||||
state: [{ pageIndex, pageSize }],
|
||||
} = useTable(
|
||||
{
|
||||
columns,
|
||||
data,
|
||||
defaultColumn,
|
||||
disablePageResetOnDataChange,
|
||||
// updateMyData isn't part of the API, but
|
||||
// anything we put into these options will
|
||||
// automatically be available on the instance.
|
||||
// That way we can call this function from our
|
||||
// cell renderer!
|
||||
updateMyData,
|
||||
},
|
||||
usePagination
|
||||
)
|
||||
|
||||
// Render the UI for your table
|
||||
return (
|
||||
<>
|
||||
<table {...getTableProps()}>
|
||||
<thead>
|
||||
{headerGroups.map(headerGroup => (
|
||||
<tr {...headerGroup.getHeaderGroupProps()}>
|
||||
{headerGroup.headers.map(column => (
|
||||
<th {...column.getHeaderProps()}>{column.render('Header')}</th>
|
||||
))}
|
||||
</tr>
|
||||
))}
|
||||
</thead>
|
||||
<tbody>
|
||||
{page.map(
|
||||
(row, i) =>
|
||||
prepareRow(row) || (
|
||||
<tr {...row.getRowProps()}>
|
||||
{row.cells.map(cell => {
|
||||
return (
|
||||
<td {...cell.getCellProps()}>{cell.render('Cell')}</td>
|
||||
)
|
||||
})}
|
||||
</tr>
|
||||
)
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
<div className="pagination">
|
||||
<button onClick={() => gotoPage(0)} disabled={!canPreviousPage}>
|
||||
{'<<'}
|
||||
</button>{' '}
|
||||
<button onClick={() => previousPage()} disabled={!canPreviousPage}>
|
||||
{'<'}
|
||||
</button>{' '}
|
||||
<button onClick={() => nextPage()} disabled={!canNextPage}>
|
||||
{'>'}
|
||||
</button>{' '}
|
||||
<button onClick={() => gotoPage(pageCount - 1)} disabled={!canNextPage}>
|
||||
{'>>'}
|
||||
</button>{' '}
|
||||
<span>
|
||||
Page{' '}
|
||||
<strong>
|
||||
{pageIndex + 1} of {pageOptions.length}
|
||||
</strong>{' '}
|
||||
</span>
|
||||
<span>
|
||||
| Go to page:{' '}
|
||||
<input
|
||||
type="number"
|
||||
defaultValue={pageIndex + 1}
|
||||
onChange={e => {
|
||||
const page = e.target.value ? Number(e.target.value) - 1 : 0
|
||||
gotoPage(page)
|
||||
}}
|
||||
style={{ width: '100px' }}
|
||||
/>
|
||||
</span>{' '}
|
||||
<select
|
||||
value={pageSize}
|
||||
onChange={e => {
|
||||
setPageSize(Number(e.target.value))
|
||||
}}
|
||||
>
|
||||
{[10, 20, 30, 40, 50].map(pageSize => (
|
||||
<option key={pageSize} value={pageSize}>
|
||||
Show {pageSize}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
function App() {
|
||||
const columns = React.useMemo(
|
||||
() => [
|
||||
{
|
||||
Header: 'Name',
|
||||
columns: [
|
||||
{
|
||||
Header: 'First Name',
|
||||
accessor: 'firstName',
|
||||
},
|
||||
{
|
||||
Header: 'Last Name',
|
||||
accessor: 'lastName',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
Header: 'Info',
|
||||
columns: [
|
||||
{
|
||||
Header: 'Age',
|
||||
accessor: 'age',
|
||||
},
|
||||
{
|
||||
Header: 'Visits',
|
||||
accessor: 'visits',
|
||||
},
|
||||
{
|
||||
Header: 'Status',
|
||||
accessor: 'status',
|
||||
},
|
||||
{
|
||||
Header: 'Profile Progress',
|
||||
accessor: 'progress',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
[]
|
||||
)
|
||||
|
||||
const [data, setData] = React.useState(() => makeData(20))
|
||||
|
||||
// We need to keep the table from resetting the pageIndex when we
|
||||
// Update data. So we can keep track of that flag with a ref.
|
||||
const skipPageResetRef = React.useRef(false)
|
||||
|
||||
// When our cell renderer calls updateMyData, we'll use
|
||||
// the rowIndex, columnID and new value to update the
|
||||
// original data
|
||||
const updateMyData = (rowIndex, columnID, value) => {
|
||||
// We also turn on the flag to not reset the page
|
||||
skipPageResetRef.current = true
|
||||
setData(old => {
|
||||
return old.filter((row, index) => {
|
||||
if (index === rowIndex) {
|
||||
return {
|
||||
...old[rowIndex],
|
||||
[columnID]: value,
|
||||
}
|
||||
}
|
||||
return row
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// After data chagnes, we turn the flag back off
|
||||
// so that if data actually changes when we're not
|
||||
// editing it, the page is reset
|
||||
React.useEffect(
|
||||
() => {
|
||||
skipPageResetRef.current = false
|
||||
},
|
||||
[data]
|
||||
)
|
||||
|
||||
// Let's add a data resetter/randomizer to help
|
||||
// illustrate that flow...
|
||||
const resetData = () => setData(makeData(20))
|
||||
|
||||
return (
|
||||
<Styles>
|
||||
<button onClick={resetData}>Reset Data</button>
|
||||
<Table
|
||||
columns={columns}
|
||||
data={data}
|
||||
updateMyData={updateMyData}
|
||||
disablePageResetOnDataChange={skipPageResetRef.current}
|
||||
/>
|
||||
</Styles>
|
||||
)
|
||||
}
|
||||
|
||||
export default App
|
||||
@@ -2,5 +2,5 @@ This project was bootstrapped with [Create React App](https://github.com/faceboo
|
||||
|
||||
You can:
|
||||
|
||||
- [Open this example in a new CodeSandbox](https://codesandbox.io/s/github/tannerlinsley/react-table/tree/master/examples/basic)
|
||||
- [Open this example in a new CodeSandbox](https://codesandbox.io/s/github/tannerlinsley/react-table/tree/master/examples/expanding)
|
||||
- `yarn` and `yarn start` to run and edit the example
|
||||
|
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 3.8 KiB |
@@ -2,5 +2,5 @@ This project was bootstrapped with [Create React App](https://github.com/faceboo
|
||||
|
||||
You can:
|
||||
|
||||
- [Open this example in a new CodeSandbox](https://codesandbox.io/s/github/tannerlinsley/react-table/tree/master/examples/basic)
|
||||
- [Open this example in a new CodeSandbox](https://codesandbox.io/s/github/tannerlinsley/react-table/tree/master/examples/filtering)
|
||||
- `yarn` and `yarn start` to run and edit the example
|
||||
|
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 3.8 KiB |
@@ -36,14 +36,16 @@ const Styles = styled.div`
|
||||
`
|
||||
|
||||
// Define a default UI for filtering
|
||||
function DefaultColumnFilter({ filterValue, setFilter }) {
|
||||
function DefaultColumnFilter({ filterValue, preFilteredRows, setFilter }) {
|
||||
const count = preFilteredRows.length
|
||||
|
||||
return (
|
||||
<input
|
||||
value={filterValue || ''}
|
||||
onChange={e => {
|
||||
setFilter(e.target.value || undefined) // Set undefined to remove the filter entirely
|
||||
}}
|
||||
placeholder="Search..."
|
||||
placeholder={`Search ${count} records...`}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -88,6 +90,7 @@ function SelectColumnFilter({ filterValue, setFilter, preFilteredRows, id }) {
|
||||
function SliderColumnFilter({ filterValue, setFilter, preFilteredRows, id }) {
|
||||
// Calculate the min and max
|
||||
// using the preFilteredRows
|
||||
|
||||
const [min, max] = React.useMemo(
|
||||
() => {
|
||||
let min = 0
|
||||
@@ -120,7 +123,25 @@ function SliderColumnFilter({ filterValue, setFilter, preFilteredRows, id }) {
|
||||
// This is a custom UI for our 'between' or number range
|
||||
// filter. It uses two number boxes and filters rows to
|
||||
// ones that have values between the two
|
||||
function NumberRangeColumnFilter({ filterValue = [], setFilter }) {
|
||||
function NumberRangeColumnFilter({
|
||||
filterValue = [],
|
||||
preFilteredRows,
|
||||
setFilter,
|
||||
id,
|
||||
}) {
|
||||
const [min, max] = React.useMemo(
|
||||
() => {
|
||||
let min = 0
|
||||
let max = 0
|
||||
preFilteredRows.forEach(row => {
|
||||
min = Math.min(row.values[id], min)
|
||||
max = Math.max(row.values[id], max)
|
||||
})
|
||||
return [min, max]
|
||||
},
|
||||
[id, preFilteredRows]
|
||||
)
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
@@ -134,7 +155,7 @@ function NumberRangeColumnFilter({ filterValue = [], setFilter }) {
|
||||
const val = e.target.value
|
||||
setFilter((old = []) => [val ? parseInt(val, 10) : undefined, old[1]])
|
||||
}}
|
||||
placeholder="Min"
|
||||
placeholder={`Min (${min})`}
|
||||
style={{
|
||||
width: '70px',
|
||||
marginRight: '0.5rem',
|
||||
@@ -148,7 +169,7 @@ function NumberRangeColumnFilter({ filterValue = [], setFilter }) {
|
||||
const val = e.target.value
|
||||
setFilter((old = []) => [old[0], val ? parseInt(val, 10) : undefined])
|
||||
}}
|
||||
placeholder="Max"
|
||||
placeholder={`Max (${max})`}
|
||||
style={{
|
||||
width: '70px',
|
||||
marginLeft: '0.5rem',
|
||||
@@ -2,5 +2,5 @@ This project was bootstrapped with [Create React App](https://github.com/faceboo
|
||||
|
||||
You can:
|
||||
|
||||
- [Open this example in a new CodeSandbox](https://codesandbox.io/s/github/tannerlinsley/react-table/tree/master/examples/basic)
|
||||
- [Open this example in a new CodeSandbox](https://codesandbox.io/s/github/tannerlinsley/react-table/tree/master/examples/grouping)
|
||||
- `yarn` and `yarn start` to run and edit the example
|
||||
|
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 3.8 KiB |
@@ -0,0 +1,6 @@
|
||||
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app) and Rescripts.
|
||||
|
||||
You can:
|
||||
|
||||
- [Open this example in a new CodeSandbox](https://codesandbox.io/s/github/tannerlinsley/react-table/tree/master/examples/pagination-controlled)
|
||||
- `yarn` and `yarn start` to run and edit the example
|
||||
|
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 3.8 KiB |
@@ -0,0 +1,276 @@
|
||||
import React from 'react'
|
||||
import styled from 'styled-components'
|
||||
import { useTable, useTableState, usePagination } from 'react-table'
|
||||
|
||||
import makeData from './makeData'
|
||||
|
||||
const Styles = styled.div`
|
||||
padding: 1rem;
|
||||
|
||||
table {
|
||||
border-spacing: 0;
|
||||
border: 1px solid black;
|
||||
|
||||
tr {
|
||||
:last-child {
|
||||
td {
|
||||
border-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
th,
|
||||
td {
|
||||
margin: 0;
|
||||
padding: 0.5rem;
|
||||
border-bottom: 1px solid black;
|
||||
border-right: 1px solid black;
|
||||
|
||||
:last-child {
|
||||
border-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.pagination {
|
||||
padding: 0.5rem;
|
||||
}
|
||||
`
|
||||
|
||||
// Let's add a fetchData method to our Table component that will be used to fetch
|
||||
// new data when pagination state changes
|
||||
// We can also add a loading state to let our table know it's loading new data
|
||||
function Table({
|
||||
columns,
|
||||
data,
|
||||
fetchData,
|
||||
loading,
|
||||
pageCount: controlledPageCount,
|
||||
}) {
|
||||
// Use useTableState to hoist the state (and state updater) out of the table and control it
|
||||
const tableState = useTableState({ pageIndex: 0 })
|
||||
|
||||
// Now we can get our table state from the hoisted table state tuple
|
||||
const [{ pageIndex, pageSize }] = tableState
|
||||
|
||||
// Listen for changes in pagination and use the state to fetch our new data
|
||||
React.useEffect(
|
||||
() => {
|
||||
fetchData({ pageIndex, pageSize })
|
||||
},
|
||||
[fetchData, pageIndex, pageSize]
|
||||
)
|
||||
|
||||
const {
|
||||
getTableProps,
|
||||
headerGroups,
|
||||
prepareRow,
|
||||
page,
|
||||
canPreviousPage,
|
||||
canNextPage,
|
||||
pageOptions,
|
||||
pageCount,
|
||||
gotoPage,
|
||||
nextPage,
|
||||
previousPage,
|
||||
setPageSize,
|
||||
} = useTable(
|
||||
{
|
||||
columns,
|
||||
data,
|
||||
state: tableState, // Pass our hoisted table state
|
||||
manualPagination: true, // Tell the usePagination
|
||||
// hook that we'll handle our own data fetching
|
||||
// This means we'll also have to provide our own
|
||||
// pageCount.
|
||||
pageCount: controlledPageCount,
|
||||
},
|
||||
usePagination
|
||||
)
|
||||
|
||||
// Render the UI for your table
|
||||
return (
|
||||
<>
|
||||
<pre>
|
||||
<code>
|
||||
{JSON.stringify(
|
||||
{
|
||||
pageIndex,
|
||||
pageSize,
|
||||
pageCount,
|
||||
canNextPage,
|
||||
canPreviousPage,
|
||||
},
|
||||
null,
|
||||
2
|
||||
)}
|
||||
</code>
|
||||
</pre>
|
||||
<table {...getTableProps()}>
|
||||
<thead>
|
||||
{headerGroups.map(headerGroup => (
|
||||
<tr {...headerGroup.getHeaderGroupProps()}>
|
||||
{headerGroup.headers.map(column => (
|
||||
<th {...column.getHeaderProps()}>{column.render('Header')}</th>
|
||||
))}
|
||||
</tr>
|
||||
))}
|
||||
</thead>
|
||||
<tbody>
|
||||
{page.map(
|
||||
(row, i) =>
|
||||
prepareRow(row) || (
|
||||
<tr {...row.getRowProps()}>
|
||||
{row.cells.map(cell => {
|
||||
return (
|
||||
<td {...cell.getCellProps()}>{cell.render('Cell')}</td>
|
||||
)
|
||||
})}
|
||||
</tr>
|
||||
)
|
||||
)}
|
||||
{loading ? (
|
||||
// Use our custom loading state to show a loading indicator
|
||||
<tr>
|
||||
<td>Loading...</td>
|
||||
</tr>
|
||||
) : null}
|
||||
</tbody>
|
||||
</table>
|
||||
{/*
|
||||
Pagination can be built however you'd like.
|
||||
This is just a very basic UI implementation:
|
||||
*/}
|
||||
<div className="pagination">
|
||||
<button onClick={() => gotoPage(0)} disabled={!canPreviousPage}>
|
||||
{'<<'}
|
||||
</button>{' '}
|
||||
<button onClick={() => previousPage()} disabled={!canPreviousPage}>
|
||||
{'<'}
|
||||
</button>{' '}
|
||||
<button onClick={() => nextPage()} disabled={!canNextPage}>
|
||||
{'>'}
|
||||
</button>{' '}
|
||||
<button onClick={() => gotoPage(pageCount - 1)} disabled={!canNextPage}>
|
||||
{'>>'}
|
||||
</button>{' '}
|
||||
<span>
|
||||
Page{' '}
|
||||
<strong>
|
||||
{pageIndex + 1} of {pageOptions.length}
|
||||
</strong>{' '}
|
||||
</span>
|
||||
<span>
|
||||
| Go to page:{' '}
|
||||
<input
|
||||
type="number"
|
||||
defaultValue={pageIndex + 1}
|
||||
onChange={e => {
|
||||
const page = e.target.value ? Number(e.target.value) - 1 : 0
|
||||
gotoPage(page)
|
||||
}}
|
||||
style={{ width: '100px' }}
|
||||
/>
|
||||
</span>{' '}
|
||||
<select
|
||||
value={pageSize}
|
||||
onChange={e => {
|
||||
setPageSize(Number(e.target.value))
|
||||
}}
|
||||
>
|
||||
{[10, 20, 30, 40, 50].map(pageSize => (
|
||||
<option key={pageSize} value={pageSize}>
|
||||
Show {pageSize}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
// Let's simulate a large dataset on the server (outside of our component)
|
||||
const serverData = makeData(10000)
|
||||
|
||||
function App() {
|
||||
const columns = React.useMemo(
|
||||
() => [
|
||||
{
|
||||
Header: 'Name',
|
||||
columns: [
|
||||
{
|
||||
Header: 'First Name',
|
||||
accessor: 'firstName',
|
||||
},
|
||||
{
|
||||
Header: 'Last Name',
|
||||
accessor: 'lastName',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
Header: 'Info',
|
||||
columns: [
|
||||
{
|
||||
Header: 'Age',
|
||||
accessor: 'age',
|
||||
},
|
||||
{
|
||||
Header: 'Visits',
|
||||
accessor: 'visits',
|
||||
},
|
||||
{
|
||||
Header: 'Status',
|
||||
accessor: 'status',
|
||||
},
|
||||
{
|
||||
Header: 'Profile Progress',
|
||||
accessor: 'progress',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
[]
|
||||
)
|
||||
|
||||
// We'll start our table without any data
|
||||
const [data, setData] = React.useState([])
|
||||
const [loading, setLoading] = React.useState(false)
|
||||
const [pageCount, setPageCount] = React.useState(0)
|
||||
|
||||
const fetchData = React.useCallback(({ pageSize, pageIndex }) => {
|
||||
// This will get called when the table needs new data
|
||||
// You could fetch your data from literally anywhere,
|
||||
// even a server. But for this example, we'll just fake it.
|
||||
|
||||
// Set the loading state
|
||||
setLoading(true)
|
||||
|
||||
// We'll even set a delay to simulate a server here
|
||||
setTimeout(() => {
|
||||
const startRow = pageSize * pageIndex
|
||||
const endRow = startRow + pageSize
|
||||
setData(serverData.slice(startRow, endRow))
|
||||
|
||||
// Your server could send back total page count.
|
||||
// For now we'll just fake it, too
|
||||
setPageCount(Math.ceil(serverData.length / pageSize))
|
||||
|
||||
setLoading(false)
|
||||
}, 1000)
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<Styles>
|
||||
<Table
|
||||
columns={columns}
|
||||
data={data}
|
||||
fetchData={fetchData}
|
||||
loading={loading}
|
||||
pageCount={pageCount}
|
||||
/>
|
||||
</Styles>
|
||||
)
|
||||
}
|
||||
|
||||
export default App
|
||||