mirror of
https://github.com/gosticks/react-table.git
synced 2025-10-16 11:55:36 +00:00
206 lines
5.5 KiB
JavaScript
Executable File
206 lines
5.5 KiB
JavaScript
Executable File
import PropTypes from 'prop-types'
|
|
//
|
|
import { flexRender, applyHooks, applyPropHooks, mergeProps } from '../utils'
|
|
|
|
import { useTableState } from './useTableState'
|
|
|
|
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
|
|
}
|
|
|
|
export const useTable = (props, ...plugins) => {
|
|
// Validate props
|
|
PropTypes.checkPropTypes(propTypes, props, 'property', 'useTable')
|
|
|
|
// Destructure props
|
|
let { data = [], state: userState, debug } = props
|
|
|
|
debug = process.env.NODE_ENV === 'production' ? false : debug
|
|
|
|
// Always provide a default state
|
|
const defaultState = useTableState()
|
|
|
|
// But use the users state if provided
|
|
const state = userState || defaultState
|
|
|
|
// These are hooks that plugins can use right before render
|
|
const hooks = {
|
|
beforeRender: [],
|
|
columns: [],
|
|
headers: [],
|
|
headerGroups: [],
|
|
rows: [],
|
|
row: [],
|
|
renderableRows: [],
|
|
getTableProps: [],
|
|
getRowProps: [],
|
|
getHeaderRowProps: [],
|
|
getHeaderProps: [],
|
|
getCellProps: []
|
|
}
|
|
|
|
// The initial api
|
|
let api = {
|
|
...props,
|
|
data,
|
|
state,
|
|
hooks
|
|
}
|
|
|
|
if (debug) console.time('hooks')
|
|
// Loop through plugins to build the api out
|
|
api = plugins.filter(Boolean).reduce((prev, next) => next(prev), api)
|
|
if (debug) console.timeEnd('hooks')
|
|
|
|
// Run the beforeRender hook
|
|
if (debug) console.time('hooks.beforeRender')
|
|
applyHooks(api.hooks.beforeRender, undefined, api)
|
|
if (debug) console.timeEnd('hooks.beforeRender')
|
|
|
|
api.columns.forEach(column => {
|
|
column.visible =
|
|
typeof column.show === 'function' ? column.show(api) : !!column.show
|
|
})
|
|
|
|
if (debug) console.time('hooks.columns')
|
|
api.columns = applyHooks(api.hooks.columns, api.columns, api)
|
|
if (debug) console.timeEnd('hooks.columns')
|
|
|
|
if (debug) console.time('hooks.headers')
|
|
api.headers = applyHooks(api.hooks.headers, api.headers, api)
|
|
if (debug) console.timeEnd('hooks.headers')
|
|
;[...api.columns, ...api.headers].forEach(column => {
|
|
// Give columns/headers rendering power
|
|
column.render = (type, userProps = {}) => {
|
|
if (!type) {
|
|
throw new Error(renderErr)
|
|
}
|
|
return flexRender(column[type], {
|
|
...api,
|
|
...column,
|
|
...userProps
|
|
})
|
|
}
|
|
|
|
// Give columns/headers getHeaderProps
|
|
column.getHeaderProps = props =>
|
|
mergeProps(
|
|
{
|
|
key: ['header', column.id].join('_'),
|
|
colSpan: column.columns ? column.columns.length : 1
|
|
},
|
|
applyPropHooks(api.hooks.getHeaderProps, column, api),
|
|
props
|
|
)
|
|
})
|
|
|
|
if (debug) console.time('hooks.headerGroups')
|
|
api.headerGroups = applyHooks(
|
|
api.hooks.headerGroups,
|
|
api.headerGroups,
|
|
api
|
|
).filter((headerGroup, i) => {
|
|
// Filter out any headers and headerGroups that don't have visible columns
|
|
headerGroup.headers = headerGroup.headers.filter(header => {
|
|
const recurse = columns =>
|
|
columns.filter(column => {
|
|
if (column.columns) {
|
|
return recurse(column.columns)
|
|
}
|
|
return column.visible
|
|
}).length
|
|
if (header.columns) {
|
|
return recurse(header.columns)
|
|
}
|
|
return header.visible
|
|
})
|
|
|
|
// Give headerGroups getRowProps
|
|
if (headerGroup.headers.length) {
|
|
headerGroup.getRowProps = (props = {}) =>
|
|
mergeProps(
|
|
{
|
|
key: [`header${i}`].join('_')
|
|
},
|
|
applyPropHooks(api.hooks.getHeaderRowProps, headerGroup, api),
|
|
props
|
|
)
|
|
return true
|
|
}
|
|
|
|
return false
|
|
})
|
|
if (debug) console.timeEnd('hooks.headerGroups')
|
|
|
|
// Run the rows (this could be a dangerous hook with a ton of data)
|
|
if (debug) console.time('hooks.rows')
|
|
api.rows = applyHooks(api.hooks.rows, api.rows, api)
|
|
if (debug) console.timeEnd('hooks.rows')
|
|
|
|
// The prepareRow function is absolutely necessary and MUST be called on
|
|
// any rows the user wishes to be displayed.
|
|
|
|
api.prepareRow = row => {
|
|
const { path } = row
|
|
row.getRowProps = props =>
|
|
mergeProps(
|
|
{ key: ['row', ...path].join('_') },
|
|
applyPropHooks(api.hooks.getRowProps, row, api),
|
|
props
|
|
)
|
|
|
|
// need to apply any row specific hooks (useExpanded requires this)
|
|
applyHooks(api.hooks.row, row, api)
|
|
|
|
const visibleColumns = api.columns.filter(column => column.visible)
|
|
|
|
// Build the cells for each row
|
|
row.cells = visibleColumns.map(column => {
|
|
const cell = {
|
|
column,
|
|
row,
|
|
value: row.values[column.id]
|
|
}
|
|
|
|
cell.getCellProps = props => {
|
|
const columnPathStr = [path, column.id].join('_')
|
|
return mergeProps(
|
|
{
|
|
key: ['cell', columnPathStr].join('_')
|
|
},
|
|
applyPropHooks(api.hooks.getCellProps, cell, api),
|
|
props
|
|
)
|
|
}
|
|
|
|
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.'
|
|
)
|
|
}
|
|
return flexRender(column[type], {
|
|
...api,
|
|
...cell,
|
|
...userProps
|
|
})
|
|
}
|
|
|
|
return cell
|
|
})
|
|
}
|
|
|
|
api.getTableProps = userProps =>
|
|
mergeProps(applyPropHooks(api.hooks.getTableProps, api), userProps)
|
|
|
|
api.getRowProps = userProps =>
|
|
mergeProps(applyPropHooks(api.hooks.getRowProps, api), userProps)
|
|
|
|
return api
|
|
}
|