Update rendering API and column/header model

This commit is contained in:
tannerlinsley 2019-07-25 10:24:30 -06:00
parent 5bb2e49764
commit 4f13f32023
5 changed files with 145 additions and 33 deletions

121
README.md
View File

@ -294,17 +294,126 @@ The following options are supported via the main options object passed to `useTa
The following options are supported on any column object you can pass to `columns`.
- `accessor: String | Function`
- **Required**
- This string/function is used to build the data model for your column.
- The data returned by an accessor should be **primitive** and sortable.
- If a string is passed, the column's value will be looked up on the original row via that key, eg. If your column's accessor is `firstName` then its value would be read from `row['firstName']`. You can also specify deeply nested values with accessors like `info.hobbies` or even `address[0].street`
- If a function is passed, the column's value will be looked up on the original row using this accessor function, eg. If your column's accessor is `row => row.firstName`, then its value would be determined by passing the row to this function and using the resulting value.
- `id: String`
- **Required if `accessor` is a function**
- This is the unique ID for the column. It is used by reference in things like sorting, grouping, filtering etc.
- If a **string** accessor is used, it defaults as the column ID, but can be overridden if necessary.
- `columns: Array<Column>`
- Optional
- A nested array of columns.
- If defined, the column will act as a header group. Columns can be recursively nested as much as needed.
- `show: Boolean | Function`
- Optional
- Defaults to `true`
- If set to `false`, the column will be hidden.
- If set to a `function`, it will be called with the current table instance and can then return `true` or `false`.
- 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`
- 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`
- 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.
### Table Output
- `columns: Array<Column>`
- A flat array of all final column objects computed from the original columns configuration option.
- `columns[].`
- `headerGroups: Array<Array<Column>>`
The following properties are available on the table instance returned from `useTable`
- `headerGroups: Array<HeaderGroup>`
- An array of normalized header groups, each containing a flattened array of final column objects for that row.
- See [Header Group Properties](#header-group-properties) for more information
- `columns: Array<Column>`
- A **flat** array of all final column objects computed from the original columns configuration option.
- See [Column Properties](#column-properties) for more information
- `headers[] Array<Column>`
- An array of nested final column objects, similar in structure to the original columns configuration option.
- A **nested** array of final column objects, similar in structure to the original columns configuration option.
- See [Column Properties](#column-properties) for more information
- `rows: Array<Row>`
- An array of rows **materialized** from the original `data` array and `columns` passed into the table options
- An array of **materialized row objects** from theriginal `dat oa` array and `columns` passed into the table options
- See [Row Properties](#row-properties) for more information
- `getTableProps: Function(?props)`
- **Required**
- This function is used to resolve any props needed for your table wrapper.
- Custom props may be passed. **NOTE: Custom props will override built-in table props, so be careful!**
### `HeaderGroup` Properties
Header Groups are The following additional properties are available on all `headerGroup`'s, returned by the table instance.
- `headers: Array<Column>`
- **Required**
- The columns in this header group.
- `getRowProps: Function(?props)`
- **Required**
- This function is used to resolve any props needed for this header group's row.
- You can use the `getHeaderRowProps` hook to extend its functionality.
- Custom props may be passed. **NOTE: Custom props will override built-in table props, so be careful!**
### `Column` Properties
The following properties are available on all columns returned by the table instance.
- `id: String`
- The resolved column ID from either the column's `accessor` or the column's hard-coded `id` property
- `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.
- 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)`
- **Required**
- This function is used to resolve any props needed for this column's header cell.
- You can use the `getHeaderProps` hook to extend its functionality.
- Custom props may be passed. **NOTE: Custom props will override built-in table props, so be careful!**
### `Row` Properties
The following additional properties are available on all `row`'s, returned by the table instance.
- `cells: Array<Cell>`
- An array of `Cell` objects containing properties and functions specific to the row and column it belongs to.
- See [Cell Properties](#cell-properties) for more information
- `values: Object<columnID: any>`
- A map of this row's **resolved** values by columnID, eg. `{ firstName: 'Tanner', lastName: 'Linsley' }`
- `getRowProps: Function(?props)`
- **Required**
- This function is used to resolve any props needed for this row.
- You can use the `getRowProps` hook to extend its functionality.
- Custom props may be passed. **NOTE: Custom props will override built-in table props, so be careful!**
### `Cell` Properties
The following additional properties are available on every `Cell` object, returned in an array of `cells` on every row object.
- `column: Column`
- The corresponding column object for this cell
- `row: Row`
- The corresponding row object for this cell
- `value: any`
- The **resolved** value for this cell.
- By default, this value is displayed on the table via the default `Cell` renderer. To override the way a cell displays
- `getCellProps: Function(?props)`
- **Required**
- This function is used to resolve any props needed for this cell.
- You can use the `getCellProps` hook to extend its functionality.
- Custom props may be passed. **NOTE: Custom props will override built-in table props, so be careful!**
- `render: Function(type: String | Function | Component, ?props)`
- This function is used to render content in context of a cell.
- 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.
### Example

View File

@ -118,7 +118,7 @@ function App() {
[]
)
const data = React.useMemo(() => makeData(100), [])
const data = React.useMemo(() => makeData(20), [])
return (
<Styles>

View File

@ -23,7 +23,7 @@ function findMaxDepth(columns, depth = 0) {
}, 0)
}
function decorateColumn(column, parent) {
function decorateColumn(column, parent, depth, index) {
// First check for string accessor
let { id, accessor, Header } = column
@ -37,20 +37,26 @@ function decorateColumn(column, parent) {
id = Header
}
if (!id && column.columns) {
console.error(column)
throw new Error('A column ID (or unique "Header" value) is required!')
}
if (!id) {
// Accessor, but no column id? This is bad.
console.error(column)
throw new Error('A column ID (or string accessor) is required!')
}
column = {
Header: '',
Cell: cell => cell.value,
Header: ({ id }) => id,
Cell: ({ value }) => value,
show: true,
...column,
id,
accessor,
parent,
depth,
index,
}
return column
@ -58,8 +64,8 @@ function decorateColumn(column, parent) {
// Build the visible columns, headers and flat column list
function decorateColumnTree(columns, parent, depth = 0) {
return columns.map(column => {
column = decorateColumn(column, parent)
return columns.map((column, columnIndex) => {
column = decorateColumn(column, parent, depth, columnIndex)
if (column.columns) {
column.columns = decorateColumnTree(column.columns, column, depth + 1)
}

View File

@ -1,20 +1,20 @@
import PropTypes from 'prop-types'
//
import { flexRender, applyHooks, applyPropHooks, mergeProps } from '../utils'
import { applyHooks, applyPropHooks, mergeProps, flexRender } from '../utils'
import { useTableState } from './useTableState'
import { useColumns } from './useColumns'
import { useRows } from './useRows'
const renderErr =
'You must specify a render "type". This could be "Header", "Filter", or any other custom renderers you have set on your column.'
const propTypes = {
// General
data: PropTypes.array.isRequired,
debug: PropTypes.bool,
}
const renderErr =
'You must specify a valid render component. This could be "column.Cell", "column.Header", "column.Filter", "column.Aggregated" or any other custom renderer component.'
export const useTable = (props, ...plugins) => {
// Validate props
PropTypes.checkPropTypes(propTypes, props, 'property', 'useTable')
@ -85,10 +85,13 @@ export const useTable = (props, ...plugins) => {
;[...api.columns, ...api.headers].forEach(column => {
// Give columns/headers rendering power
column.render = (type, userProps = {}) => {
if (!type) {
const Comp = typeof type === 'string' ? column[type] : type
if (!Comp) {
throw new Error(renderErr)
}
return flexRender(column[type], {
return flexRender(Comp, {
...api,
...column,
...userProps,
@ -190,12 +193,13 @@ export const useTable = (props, ...plugins) => {
// Give each cell a renderer function (supports multiple renderers)
cell.render = (type, userProps = {}) => {
if (!type) {
throw new Error(
'You must specify a render "type". This could be "Cell", "Header", "Filter", "Aggregated" or any other custom renderers you have set on your column.'
)
const Comp = typeof type === 'string' ? column[type] : type
if (!Comp) {
throw new Error(renderErr)
}
return flexRender(column[type], {
return flexRender(Comp, {
...api,
...cell,
...userProps,
@ -209,8 +213,5 @@ export const useTable = (props, ...plugins) => {
api.getTableProps = userProps =>
mergeProps(applyPropHooks(api.hooks.getTableProps, api), userProps)
api.getRowProps = userProps =>
mergeProps(applyPropHooks(api.hooks.getRowProps, undefined, api), userProps)
return api
}

View File

@ -89,12 +89,8 @@ export function getElementDimensions(element) {
}
export function flexRender(Comp, props) {
if (typeof Comp === 'function') {
return Object.getPrototypeOf(Comp).isReactComponent ? (
<Comp {...props} />
) : (
Comp(props)
)
if (typeof Comp === 'function' || typeof Comp === 'object') {
return <Comp {...props} />
}
return Comp
}