mirror of
https://github.com/gosticks/react-table.git
synced 2026-07-01 10:00:03 +00:00
fix: fix path getters, better plugin hook integration, renaming things
This commit is contained in:
@@ -73,7 +73,7 @@ function Table({ columns, data }) {
|
||||
{column.canGroupBy ? (
|
||||
// If the column can be grouped, let's add a toggle
|
||||
<span {...column.getGroupByToggleProps()}>
|
||||
{column.grouped ? '🛑' : '👊'}
|
||||
{column.isGrouped ? '🛑' : '👊'}
|
||||
</span>
|
||||
) : null}
|
||||
{column.render('Header')}
|
||||
@@ -90,7 +90,7 @@ function Table({ columns, data }) {
|
||||
{row.cells.map(cell => {
|
||||
return (
|
||||
<td {...cell.getCellProps()}>
|
||||
{cell.grouped ? (
|
||||
{cell.isGrouped ? (
|
||||
<>
|
||||
<span
|
||||
style={{
|
||||
@@ -102,9 +102,9 @@ function Table({ columns, data }) {
|
||||
</span>
|
||||
{cell.render('Cell')} ({row.subRows.length})
|
||||
</>
|
||||
) : cell.aggregated ? (
|
||||
) : cell.isAggregated ? (
|
||||
cell.render('Aggregated')
|
||||
) : cell.repeatedValue ? null : (
|
||||
) : cell.isRepeatedValue ? null : (
|
||||
cell.render('Cell')
|
||||
)}
|
||||
</td>
|
||||
|
||||
@@ -120,7 +120,7 @@ function App() {
|
||||
{
|
||||
id: 'selectedStatus',
|
||||
Cell: ({ row }) => (
|
||||
<div>{row.selected ? 'Selected' : 'Not Selected'}</div>
|
||||
<div>{row.isSelected ? 'Selected' : 'Not Selected'}</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
|
||||
@@ -56,7 +56,7 @@ function Table({ columns, data }) {
|
||||
{column.render('Header')}
|
||||
{/* Add a sort direction indicator */}
|
||||
<span>
|
||||
{column.sorted ? (column.sortedDesc ? ' 🔽' : ' 🔼') : ''}
|
||||
{column.isSorted ? (column.isSortedDesc ? ' 🔽' : ' 🔼') : ''}
|
||||
</span>
|
||||
</th>
|
||||
))}
|
||||
|
||||
@@ -17,7 +17,7 @@ addActions('toggleExpanded', 'useExpanded')
|
||||
|
||||
const propTypes = {
|
||||
manualExpandedKey: PropTypes.string,
|
||||
nestExpandedRows: PropTypes.bool,
|
||||
paginateExpandedRows: PropTypes.bool,
|
||||
}
|
||||
|
||||
export const useExpanded = hooks => {
|
||||
@@ -34,16 +34,16 @@ function useMain(instance) {
|
||||
debug,
|
||||
rows,
|
||||
manualExpandedKey = 'expanded',
|
||||
paginateExpandedRows = true,
|
||||
hooks,
|
||||
state: [{ expanded }, setState],
|
||||
nestExpandedRows,
|
||||
} = instance
|
||||
|
||||
const toggleExpandedByPath = (path, set) => {
|
||||
return setState(old => {
|
||||
const { expanded } = old
|
||||
const existing = getBy(expanded, path)
|
||||
set = getFirstDefined(set, !existing)
|
||||
set = getFirstDefined(set, existing ? undefined : true)
|
||||
return {
|
||||
...old,
|
||||
expanded: setBy(expanded, path, set),
|
||||
@@ -85,13 +85,16 @@ function useMain(instance) {
|
||||
(row.original && row.original[manualExpandedKey]) ||
|
||||
getBy(expanded, row.path)
|
||||
|
||||
if (!nestExpandedRows || (nestExpandedRows && row.depth === 0)) {
|
||||
expandedRows.push(row)
|
||||
}
|
||||
expandedRows.push(row)
|
||||
|
||||
row.canExpand = row.subRows && !!row.subRows.length
|
||||
|
||||
if (row.isExpanded && row.subRows && row.subRows.length) {
|
||||
if (
|
||||
paginateExpandedRows &&
|
||||
row.isExpanded &&
|
||||
row.subRows &&
|
||||
row.subRows.length
|
||||
) {
|
||||
row.subRows.forEach(handleRow)
|
||||
}
|
||||
|
||||
@@ -101,7 +104,7 @@ function useMain(instance) {
|
||||
rows.forEach(handleRow)
|
||||
|
||||
return expandedRows
|
||||
}, [debug, rows, manualExpandedKey, expanded, nestExpandedRows])
|
||||
}, [debug, rows, manualExpandedKey, expanded, paginateExpandedRows])
|
||||
|
||||
const expandedDepth = findExpandedDepth(expanded)
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
applyPropHooks,
|
||||
defaultGroupByFn,
|
||||
getFirstDefined,
|
||||
ensurePluginOrder,
|
||||
} from '../utils'
|
||||
|
||||
defaultState.groupBy = []
|
||||
@@ -50,9 +51,18 @@ useGroupBy.pluginName = 'useGroupBy'
|
||||
function columnsBeforeHeaderGroups(columns, { state: [{ groupBy }] }) {
|
||||
// Sort grouped columns to the start of the column list
|
||||
// before the headers are built
|
||||
|
||||
const groupByColumns = groupBy.map(g => columns.find(col => col.id === g))
|
||||
const nonGroupByColumns = columns.filter(col => !groupBy.includes(col.id))
|
||||
|
||||
// If a groupByBoundary column is found, place the groupBy's after it
|
||||
const groupByBoundaryColumnIndex =
|
||||
columns.findIndex(column => column.groupByBoundary) + 1
|
||||
|
||||
return [
|
||||
...groupBy.map(g => columns.find(col => col.id === g)),
|
||||
...columns.filter(col => !groupBy.includes(col.id)),
|
||||
...nonGroupByColumns.slice(0, groupByBoundaryColumnIndex),
|
||||
...groupByColumns,
|
||||
...nonGroupByColumns.slice(groupByBoundaryColumnIndex),
|
||||
]
|
||||
}
|
||||
|
||||
@@ -69,12 +79,15 @@ function useMain(instance) {
|
||||
disableGrouping,
|
||||
aggregations: userAggregations = {},
|
||||
hooks,
|
||||
plugins,
|
||||
state: [{ groupBy }, setState],
|
||||
} = instance
|
||||
|
||||
ensurePluginOrder(plugins, [], 'useGroupBy', ['useExpanded'])
|
||||
|
||||
columns.forEach(column => {
|
||||
const { id, accessor, disableGrouping: columnDisableGrouping } = column
|
||||
column.grouped = groupBy.includes(id)
|
||||
column.isGrouped = groupBy.includes(id)
|
||||
column.groupedIndex = groupBy.indexOf(id)
|
||||
|
||||
column.canGroupBy = accessor
|
||||
@@ -137,11 +150,12 @@ function useMain(instance) {
|
||||
hooks.prepareRow.push(row => {
|
||||
row.cells.forEach(cell => {
|
||||
// Grouped cells are in the groupBy and the pivot cell for the row
|
||||
cell.grouped = cell.column.grouped && cell.column.id === row.groupByID
|
||||
cell.isGrouped = cell.column.isGrouped && cell.column.id === row.groupByID
|
||||
// Repeated cells are any columns in the groupBy that are not grouped
|
||||
cell.repeatedValue = !cell.grouped && cell.column.grouped
|
||||
cell.isRepeatedValue = !cell.isGrouped && cell.column.isGrouped
|
||||
// Aggregated cells are not grouped, not repeated, but still have subRows
|
||||
cell.aggregated = !cell.grouped && !cell.repeatedValue && row.canExpand
|
||||
cell.isAggregated =
|
||||
!cell.isGrouped && !cell.isRepeatedValue && row.canExpand
|
||||
})
|
||||
return row
|
||||
})
|
||||
@@ -218,7 +232,7 @@ function useMain(instance) {
|
||||
// Recurse to sub rows before aggregation
|
||||
groupedRows = Object.entries(groupedRows).map(
|
||||
([groupByVal, subRows], index) => {
|
||||
const path = [...parentPath, groupByVal]
|
||||
const path = [...parentPath, `${columnID}:${groupByVal}`]
|
||||
|
||||
subRows = groupRecursively(subRows, depth + 1, path)
|
||||
|
||||
@@ -228,6 +242,7 @@ function useMain(instance) {
|
||||
)
|
||||
|
||||
const row = {
|
||||
isAggregated: true,
|
||||
groupByID: columnID,
|
||||
groupByVal,
|
||||
values,
|
||||
|
||||
@@ -14,6 +14,7 @@ addActions('pageChange', 'pageSizeChange')
|
||||
const propTypes = {
|
||||
// General
|
||||
manualPagination: PropTypes.bool,
|
||||
paginateExpandedRows: PropTypes.bool,
|
||||
}
|
||||
|
||||
// SSR has issues with useLayoutEffect still, so use useEffect during SSR
|
||||
@@ -32,28 +33,30 @@ function useMain(instance) {
|
||||
PropTypes.checkPropTypes(propTypes, instance, 'property', 'usePagination')
|
||||
|
||||
const {
|
||||
data,
|
||||
rows,
|
||||
manualPagination,
|
||||
disablePageResetOnDataChange,
|
||||
debug,
|
||||
plugins,
|
||||
pageCount: userPageCount,
|
||||
paginateExpandedRows = true,
|
||||
state: [{ pageSize, pageIndex, filters, groupBy, sortBy }, setState],
|
||||
} = instance
|
||||
|
||||
ensurePluginOrder(
|
||||
plugins,
|
||||
['useFilters', 'useGroupBy', 'useSortBy'],
|
||||
['useFilters', 'useGroupBy', 'useSortBy', 'useExpanded'],
|
||||
'usePagination',
|
||||
[]
|
||||
)
|
||||
|
||||
const rowDep = manualPagination || disablePageResetOnDataChange ? null : rows
|
||||
const rowDep = manualPagination ? null : data
|
||||
|
||||
const isPageIndexMountedRef = React.useRef()
|
||||
|
||||
useLayoutEffect(() => {
|
||||
if (isPageIndexMountedRef.current) {
|
||||
if (isPageIndexMountedRef.current && !disablePageResetOnDataChange) {
|
||||
setState(
|
||||
old => ({
|
||||
...old,
|
||||
@@ -63,38 +66,51 @@ function useMain(instance) {
|
||||
)
|
||||
}
|
||||
isPageIndexMountedRef.current = true
|
||||
}, [setState, rowDep, filters, groupBy, sortBy])
|
||||
}, [setState, rowDep, filters, groupBy, sortBy, disablePageResetOnDataChange])
|
||||
|
||||
const pages = React.useMemo(() => {
|
||||
if (manualPagination) {
|
||||
return undefined
|
||||
}
|
||||
if (process.env.NODE_ENV === 'development' && debug)
|
||||
console.info('getPages')
|
||||
|
||||
// Create a new pages with the first page ready to go.
|
||||
const pages = rows.length ? [] : [[]]
|
||||
|
||||
// Start the pageIndex and currentPage cursors
|
||||
let cursor = 0
|
||||
|
||||
while (cursor < rows.length) {
|
||||
const end = cursor + pageSize
|
||||
pages.push(rows.slice(cursor, end))
|
||||
cursor = end
|
||||
}
|
||||
|
||||
return pages
|
||||
}, [debug, manualPagination, pageSize, rows])
|
||||
|
||||
const pageCount = manualPagination ? userPageCount : pages.length
|
||||
const pageCount = manualPagination
|
||||
? userPageCount
|
||||
: Math.ceil(rows.length / pageSize)
|
||||
|
||||
const pageOptions = React.useMemo(
|
||||
() => (pageCount > 0 ? [...new Array(pageCount)].map((d, i) => i) : []),
|
||||
[pageCount]
|
||||
)
|
||||
|
||||
const page = manualPagination ? rows : pages[pageIndex]
|
||||
const page = React.useMemo(() => {
|
||||
let page
|
||||
|
||||
if (manualPagination) {
|
||||
page = rows
|
||||
} else {
|
||||
if (process.env.NODE_ENV === 'development' && debug)
|
||||
console.info('getPage')
|
||||
|
||||
const pageStart = pageSize * pageIndex
|
||||
const pageEnd = pageStart + pageSize
|
||||
|
||||
page = rows.slice(pageStart, pageEnd)
|
||||
}
|
||||
|
||||
if (paginateExpandedRows) {
|
||||
return page
|
||||
}
|
||||
|
||||
const expandedPage = []
|
||||
|
||||
const handleRow = row => {
|
||||
expandedPage.push(row)
|
||||
|
||||
if (row.subRows && row.subRows.length && row.isExpanded) {
|
||||
row.subRows.forEach(handleRow)
|
||||
}
|
||||
}
|
||||
|
||||
page.forEach(handleRow)
|
||||
|
||||
return expandedPage
|
||||
}, [debug, manualPagination, pageIndex, pageSize, paginateExpandedRows, rows])
|
||||
|
||||
const canPreviousPage = pageIndex > 0
|
||||
const canNextPage = pageCount === -1 || pageIndex < pageCount - 1
|
||||
|
||||
@@ -134,7 +150,6 @@ function useMain(instance) {
|
||||
|
||||
return {
|
||||
...instance,
|
||||
pages,
|
||||
pageOptions,
|
||||
pageCount,
|
||||
page,
|
||||
|
||||
@@ -25,7 +25,7 @@ function useMain(instance) {
|
||||
|
||||
const {
|
||||
hooks,
|
||||
manualRowSelectedKey = 'selected',
|
||||
manualRowSelectedKey = 'isSelected',
|
||||
plugins,
|
||||
rowPaths,
|
||||
state: [{ selectedRows }, setState],
|
||||
@@ -59,7 +59,7 @@ function useMain(instance) {
|
||||
// in a flat object
|
||||
const exists = old.selectedRows.includes(key)
|
||||
const shouldExist = typeof set !== 'undefined' ? set : !exists
|
||||
let newSelectedRows = new Set(selectedRows)
|
||||
let newSelectedRows = new Set(old.selectedRows)
|
||||
|
||||
if (!exists && shouldExist) {
|
||||
newSelectedRows.add(key)
|
||||
@@ -94,10 +94,49 @@ function useMain(instance) {
|
||||
}
|
||||
|
||||
hooks.prepareRow.push(row => {
|
||||
row.canSelect = !!row.original
|
||||
// Aggregate rows have entirely different select logic
|
||||
if (row.isAggregated) {
|
||||
const subRowPaths = row.subRows.map(row => row.path)
|
||||
row.isSelected = subRowPaths.every(path =>
|
||||
selectedRows.includes(path.join('.'))
|
||||
)
|
||||
row.toggleRowSelected = set => {
|
||||
set = typeof set !== 'undefined' ? set : !row.isSelected
|
||||
console.log(subRowPaths)
|
||||
subRowPaths.forEach(path => {
|
||||
toggleRowSelected(path, set)
|
||||
})
|
||||
}
|
||||
row.getToggleRowSelectedProps = props => {
|
||||
let checked = false
|
||||
|
||||
if (row.canSelect) {
|
||||
row.selected = selectedRows.includes(row.path.join('.'))
|
||||
if (row.original && row.original[manualRowSelectedKey]) {
|
||||
checked = true
|
||||
} else {
|
||||
checked = row.isSelected
|
||||
}
|
||||
|
||||
return mergeProps(
|
||||
{
|
||||
onChange: e => {
|
||||
row.toggleRowSelected(e.target.checked)
|
||||
},
|
||||
style: {
|
||||
cursor: 'pointer',
|
||||
},
|
||||
checked,
|
||||
title: 'Toggle Row Selected',
|
||||
},
|
||||
applyPropHooks(
|
||||
instance.hooks.getToggleRowSelectedProps,
|
||||
row,
|
||||
instance
|
||||
),
|
||||
props
|
||||
)
|
||||
}
|
||||
} else {
|
||||
row.isSelected = selectedRows.includes(row.path.join('.'))
|
||||
row.toggleRowSelected = set => toggleRowSelected(row.path, set)
|
||||
row.getToggleRowSelectedProps = props => {
|
||||
let checked = false
|
||||
@@ -105,7 +144,7 @@ function useMain(instance) {
|
||||
if (row.original && row.original[manualRowSelectedKey]) {
|
||||
checked = true
|
||||
} else {
|
||||
checked = selectedRows.includes(row.path.join('.'))
|
||||
checked = row.isSelected
|
||||
}
|
||||
|
||||
return mergeProps(
|
||||
|
||||
@@ -64,7 +64,7 @@ function useMain(instance) {
|
||||
plugins,
|
||||
} = instance
|
||||
|
||||
ensurePluginOrder(plugins, [], 'useSortBy', ['useFilters'])
|
||||
ensurePluginOrder(plugins, ['useFilters'], 'useSortBy', [])
|
||||
// Add custom hooks
|
||||
hooks.getSortByToggleProps = []
|
||||
|
||||
@@ -197,9 +197,10 @@ function useMain(instance) {
|
||||
)
|
||||
}
|
||||
|
||||
column.sorted = sortBy.find(d => d.id === id)
|
||||
const columnSort = sortBy.find(d => d.id === id)
|
||||
column.isSorted = !!columnSort
|
||||
column.sortedIndex = sortBy.findIndex(d => d.id === id)
|
||||
column.sortedDesc = column.sorted ? column.sorted.desc : undefined
|
||||
column.isSortedDesc = column.isSorted ? columnSort.desc : undefined
|
||||
})
|
||||
|
||||
const sortedRows = React.useMemo(() => {
|
||||
|
||||
18
src/utils.js
18
src/utils.js
@@ -196,6 +196,7 @@ export function defaultGroupByFn(rows, columnID) {
|
||||
}
|
||||
|
||||
export function setBy(obj = {}, path, value) {
|
||||
path = makePathArray(path)
|
||||
const recurse = (obj, depth = 0) => {
|
||||
const key = path[depth]
|
||||
const target = typeof obj[key] !== 'object' ? {} : obj[key]
|
||||
@@ -347,11 +348,18 @@ This usually means you need to need to name your plugin hook by setting the 'plu
|
||||
//
|
||||
|
||||
function makePathArray(obj) {
|
||||
return flattenDeep(obj)
|
||||
.join('.')
|
||||
.replace(/\[/g, '.')
|
||||
.replace(/\]/g, '')
|
||||
.split('.')
|
||||
return (
|
||||
flattenDeep(obj)
|
||||
// remove all periods in parts
|
||||
.map(d => String(d).replace('.', '_'))
|
||||
// join parts using period
|
||||
.join('.')
|
||||
// replace brackets with periods
|
||||
.replace(/\[/g, '.')
|
||||
.replace(/\]/g, '')
|
||||
// split it back out on periods
|
||||
.split('.')
|
||||
)
|
||||
}
|
||||
|
||||
function flattenDeep(arr, newArr = []) {
|
||||
|
||||
Reference in New Issue
Block a user