Normalize API method names and row modesl, add expandAll functionality including prop getter

This commit is contained in:
Tanner Linsley 2020-02-15 12:43:09 -07:00
parent 2bf99aaea0
commit 083e81dc81
26 changed files with 386 additions and 204 deletions

View File

@ -1,20 +1,20 @@
{
"dist/index.js": {
"bundled": 113244,
"minified": 52531,
"gzipped": 13840
"bundled": 131178,
"minified": 61448,
"gzipped": 15699
},
"dist/index.es.js": {
"bundled": 112307,
"minified": 51695,
"gzipped": 13674,
"bundled": 130236,
"minified": 60607,
"gzipped": 15525,
"treeshaked": {
"rollup": {
"code": 80,
"import_statements": 21
},
"webpack": {
"code": 8471
"code": 8457
}
}
},

View File

@ -7,7 +7,7 @@
- Renamed `instance.flatColumns` to `instance.allColumns` which now accumulates ALL columns created for the table, visible or not.
- Added the `instance.visibleColumns` object
- Fix an issue where `useAsyncDebounce` would crash when passed arguments
- Started development on the `usePivotColumns` plugin, which can be tested currently using the `_UNSTABLE_usePivoteColumns` export.
- Started development on the `usePivotColumns` plugin, which can be tested currently using the `_UNSTABLE_usePivotColumns` export.
- Renamed `cell.isRepeatedValue` to `cell.isPlaceholder`
- Removed `useConsumeHookGetter` as it was inefficient most of the time and noisy
- All hooks are now "consumed" right after main plugin functions are run. This means that any attempt to add a plugin after that will result in a runtime error (for good reason, since using hook points should not be a conditional or async operation)
@ -25,6 +25,20 @@
- Fixed an issue where `useGlobalFilter` could be placed after `usePagination`
- Added the sort order direction as a parameter to the sortMethod function
- Fixed an issue where user filter types were not being referenced correctly
- Renamed the `row.getExpandedToggleProps` to `row.getToggleRowExpandedProps`
- Renamed the `row.toggleExpanded` method to `row.toggleRowExpanded`
- Added the `instance.toggleRowExpanded` and `instance.toggleAllRowsExpanded` methods
- Added the `instance.getToggleAllRowsExpandedProps` prop getter
- Added the `instance.filteredRowsById` property
- Added the `instance.preFilteredRowsById` property
- useFilters now properly updates the `instance.rowsById` property
- Added the `instance.globalFilteredRowsById` property
- Added the `instance.preGlobalFilteredRowsById` property
- useGlobalFilter now properly updates the `instance.rowsById` property
- Added the `instance.nonGroupedFlatRows` property
- Added the `instance.nonGroupedRowsById` property
- Added the `instance.onlyGroupedFlatRows` property
- Added the `instance.onlyGroupedRowsById` property
## 7.0.0-rc.15

View File

@ -41,6 +41,13 @@ The following properties are available on the table instance returned from `useT
- `rows: Array<Row>`
- An array of **expanded** rows.
- `toggleRowExpanded: Function(rowId, isExpanded?)`
- A function to toggle whether a row is expanded or not. The `isExpanded` boolean is optional, otherwise it will be a true toggle action
- `toggleAllRowsExpanded: Function(isExpanded?)`
- A function to toggle whether all of the rows in the table are expanded or not. The `isExpanded` boolean is optional, otherwise it will be a true toggle action
- `isAllRowsExpanded`
- `getToggleAllRowsExpandedProps: Function(userProps) => props`
- A prop getter function that returns all necessary props for an element to be clicked and toggle all of the rows expanded or not.
### Row Properties
@ -48,7 +55,7 @@ The following additional properties are available on every `row` object returned
- `isExpanded: Bool`
- If `true`, this row is in an expanded state.
- `toggleExpanded: Function(?isExpanded: Bool) => void`
- `toggleRowExpanded: Function(?isExpanded: Bool) => void`
- This function will toggle the expanded state of a row between `true` and `false` or, if an `isExpanded` boolean is passed to the function, it will be set as the new `isExpanded` value.
- Rows with a hard-coded `manualExpandedKey` (defaults to `expanded`) set to `true` are not affected by this function or the internal expanded state.

View File

@ -93,11 +93,6 @@ The following options are supported on any column object you can pass to `column
- 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.
- `show: Bool`
- Optional
- Defaults to `undefined`
- If set to `true`, will take priority over any state in `hiddenColumns` and force this column to be visible at all times.
- If set to `false` will take priority over any state in `hiddenColumns` and force this column to be hidden at all times.
- `width: Int`
- Optional
- Defaults to `150`

View File

@ -88,14 +88,18 @@ function App() {
() => [
{
// Build our expander column
Header: () => null, // No header, please
id: 'expander', // Make sure it has an ID
Header: ({ getToggleAllRowsExpandedProps, isAllRowsExpanded }) => (
<span {...getToggleAllRowsExpandedProps()}>
{isAllRowsExpanded ? '👇' : '👉'}
</span>
),
Cell: ({ row }) =>
// Use the row.canExpand and row.getExpandedToggleProps prop getter
// Use the row.canExpand and row.getToggleRowExpandedProps prop getter
// to build the toggle for expanding a row
row.canExpand ? (
<span
{...row.getExpandedToggleProps({
{...row.getToggleRowExpandedProps({
style: {
// We can even use the row.depth property
// and paddingLeft to indicate the depth

View File

@ -97,7 +97,7 @@ function Table({ columns, data }) {
return (
<span
{...row.getExpandedToggleProps({
{...row.getToggleRowExpandedProps({
style: {
// We can even use the row.depth property
// and paddingLeft to indicate the depth

View File

@ -103,7 +103,7 @@ function Table({ columns, data }) {
{cell.isGrouped ? (
// If it's a grouped cell, add an expander and row count
<>
<span {...row.getExpandedToggleProps()}>
<span {...row.getToggleRowExpandedProps()}>
{row.isExpanded ? '👇' : '👉'}
</span>{' '}
{cell.render('Cell')} ({row.subRows.length})

View File

@ -378,7 +378,7 @@ function Table({ columns, data, updateMyData, skipPageReset }) {
{cell.isGrouped ? (
// If it's a grouped cell, add an expander and row count
<>
<span {...row.getExpandedToggleProps()}>
<span {...row.getToggleRowExpandedProps()}>
{row.isExpanded ? '👇' : '👉'}
</span>{' '}
{cell.render('Cell', { editable: false })} (

View File

@ -8095,10 +8095,10 @@ react-scripts@3.0.1:
optionalDependencies:
fsevents "2.0.6"
react-table@next:
version "7.0.0-alpha.7"
resolved "https://registry.yarnpkg.com/react-table/-/react-table-7.0.0-alpha.7.tgz#0cb6da6f32adb397e68505b7cdd4880d15d73017"
integrity sha512-oXE9RRkE2CFk1OloNCSTPQ9qxOdujgkCoW5b/srbJsBog/ySkWuozBTQkxH1wGNmnSxGyTrTxJqXdXPQam7VAw==
react-table@latest:
version "7.0.0-rc.15"
resolved "https://registry.yarnpkg.com/react-table/-/react-table-7.0.0-rc.15.tgz#bb855e4e2abbb4aaf0ed2334404a41f3ada8e13a"
integrity sha512-ofMOlgrioHhhvHjvjsQkxvfQzU98cqwy6BjPGNwhLN1vhgXeWi0mUGreaCPvRenEbTiXsQbMl4k3Xmx3Mut8Rw==
react@^16.8.6:
version "16.8.6"

View File

@ -380,7 +380,7 @@ function Table({ columns, data, updateMyData, skipReset }) {
{cell.isGrouped ? (
// If it's a grouped cell, add an expander and row count
<>
<span {...row.getExpandedToggleProps()}>
<span {...row.getToggleRowExpandedProps()}>
{row.isExpanded ? '👇' : '👉'}
</span>{' '}
{cell.render('Cell', { editable: false })} (

View File

@ -4,7 +4,7 @@ import {
useTable,
useGroupBy,
useExpanded,
_UNSTABLE_usePivoteColumns,
_UNSTABLE_usePivotColumns,
} from 'react-table'
import dayjs from 'dayjs'
import localizedFormat from 'dayjs/plugin/localizedFormat'
@ -98,8 +98,8 @@ function Table({ columns, data }) {
data,
},
useGroupBy,
_UNSTABLE_usePivoteColumns,
useExpanded // useGroupBy and _UNSTABLE_usePivoteColumns would be pretty useless without useExpanded ;)
_UNSTABLE_usePivotColumns,
useExpanded // useGroupBy and _UNSTABLE_usePivotColumns would be pretty useless without useExpanded ;)
)
// We don't want to render all of the rows for this example, so cap
@ -197,7 +197,7 @@ function Table({ columns, data }) {
<td {...cell.getCellProps()}>
{cell.isGrouped ? (
<>
<span {...row.getExpandedToggleProps()}>
<span {...row.getToggleRowExpandedProps()}>
{row.isExpanded ? '👇' : '👉'} {cell.render('Cell')}{' '}
({row.subRows.length})
</span>

View File

@ -120,9 +120,9 @@ function App() {
id: 'expander', // It needs an ID
Cell: ({ row }) => (
// Use Cell to render an expander for each row.
// We can use the getExpandedToggleProps prop-getter
// We can use the getToggleRowExpandedProps prop-getter
// to build the expander.
<span {...row.getExpandedToggleProps()}>
<span {...row.getToggleRowExpandedProps()}>
{row.isExpanded ? '👇' : '👉'}
</span>
),

View File

@ -1,11 +1,12 @@
import React from 'react'
//
import {
linkColumnStructure,
flattenColumns,
assignColumnAccessor,
accessRowsForColumn,
unpreparedAccessWarning,
makeHeaderGroups,
decorateColumn,
dedupeBy,
@ -488,10 +489,12 @@ function calculateHeaderWidths(headers, left = 0) {
header.totalLeft = left
if (subHeaders && subHeaders.length) {
const [totalMinWidth, totalWidth, totalMaxWidth, totalFlexWidth] = calculateHeaderWidths(
subHeaders,
left
)
const [
totalMinWidth,
totalWidth,
totalMaxWidth,
totalFlexWidth,
] = calculateHeaderWidths(subHeaders, left)
header.totalMinWidth = totalMinWidth
header.totalWidth = totalWidth
header.totalMaxWidth = totalMaxWidth
@ -516,3 +519,94 @@ function calculateHeaderWidths(headers, left = 0) {
return [sumTotalMinWidth, sumTotalWidth, sumTotalMaxWidth, sumTotalFlexWidth]
}
function accessRowsForColumn({
data,
rows,
flatRows,
rowsById,
column,
getRowId,
getSubRows,
accessValueHooks,
getInstance,
}) {
// Access the row's data column-by-column
// We do it this way so we can incrementally add materialized
// columns after the first pass and avoid excessive looping
const accessRow = (originalRow, rowIndex, depth = 0, parent, parentRows) => {
// Keep the original reference around
const original = originalRow
const id = getRowId(originalRow, rowIndex, parent)
let row = rowsById[id]
// If the row hasn't been created, let's make it
if (!row) {
row = {
id,
original,
index: rowIndex,
depth,
cells: [{}], // This is a dummy cell
}
// Override common array functions (and the dummy cell's getCellProps function)
// to show an error if it is accessed without calling prepareRow
row.cells.map = unpreparedAccessWarning
row.cells.filter = unpreparedAccessWarning
row.cells.forEach = unpreparedAccessWarning
row.cells[0].getCellProps = unpreparedAccessWarning
// Create the cells and values
row.values = {}
// Push this row into the parentRows array
parentRows.push(row)
// Keep track of every row in a flat array
flatRows.push(row)
// Also keep track of every row by its ID
rowsById[id] = row
// Get the original subrows
row.originalSubRows = getSubRows(originalRow, rowIndex)
// Then recursively access them
if (row.originalSubRows) {
const subRows = []
row.originalSubRows.forEach((d, i) =>
accessRow(d, i, depth + 1, row, subRows)
)
// Keep the new subRows array on the row
row.subRows = subRows
}
} else if (row.subRows) {
// If the row exists, then it's already been accessed
// Keep recursing, but don't worry about passing the
// accumlator array (those rows already exist)
row.originalSubRows.forEach((d, i) => accessRow(d, i, depth + 1, row))
}
// If the column has an accessor, use it to get a value
if (column.accessor) {
row.values[column.id] = column.accessor(originalRow, rowIndex, row)
}
// Allow plugins to manipulate the column value
row.values[column.id] = reduceHooks(
accessValueHooks,
row.values[column.id],
{
row,
column,
instance: getInstance(),
},
true
)
}
data.forEach((originalRow, rowIndex) =>
accessRow(originalRow, rowIndex, 0, undefined, rows)
)
}

View File

@ -6,7 +6,7 @@ export { useGlobalFilter } from './plugin-hooks/useGlobalFilter'
export { useGroupBy } from './plugin-hooks/useGroupBy'
export { useSortBy } from './plugin-hooks/useSortBy'
export { usePagination } from './plugin-hooks/usePagination'
export { usePivotColumns as _UNSTABLE_usePivoteColumns } from './plugin-hooks/usePivotColumns'
export { _UNSTABLE_usePivotColumns } from './plugin-hooks/_UNSTABLE_usePivotColumns'
export { useRowSelect } from './plugin-hooks/useRowSelect'
export { useRowState } from './plugin-hooks/useRowState'
export { useColumnOrder } from './plugin-hooks/useColumnOrder'

View File

@ -14,7 +14,7 @@ import { flattenColumns, getFirstDefined } from '../utils'
actions.resetPivot = 'resetPivot'
actions.togglePivot = 'togglePivot'
export const usePivotColumns = hooks => {
export const _UNSTABLE_usePivotColumns = hooks => {
hooks.getPivotToggleProps = [defaultGetPivotToggleProps]
hooks.stateReducers.push(reducer)
hooks.useInstanceAfterData.push(useInstanceAfterData)
@ -28,7 +28,7 @@ export const usePivotColumns = hooks => {
hooks.prepareRow.push(prepareRow)
}
usePivotColumns.pluginName = 'usePivotColumns'
_UNSTABLE_usePivotColumns.pluginName = 'usePivotColumns'
const defaultPivotColumns = []

View File

@ -75,7 +75,7 @@ function App() {
cursor: 'pointer',
paddingLeft: `${row.depth * 2}rem`,
}}
onClick={() => row.toggleExpanded()}
onClick={() => row.toggleRowExpanded()}
>
{row.isExpanded ? 'Collapse' : 'Expand'} Row {row.id}
</span>

View File

@ -113,7 +113,7 @@ function Table({ columns, data }) {
style={{
cursor: 'pointer',
}}
onClick={() => row.toggleExpanded()}
onClick={() => row.toggleRowExpanded()}
>
{row.isExpanded ? '👇' : '👉'}
</span>

View File

@ -2,7 +2,6 @@ import React from 'react'
import { render, fireEvent } from '../../../test-utils/react-testing'
import { useTable } from '../../hooks/useTable'
import { useRowState } from '../useRowState'
import { useGlobalFilter } from '../useGlobalFilter'
const data = [
{
@ -67,8 +66,7 @@ function Table({ columns, data }) {
initialRowStateAccessor: () => ({ count: 0 }),
initialCellStateAccessor: () => ({ count: 0 }),
},
useRowState,
useGlobalFilter
useRowState
)
return (

View File

@ -5,19 +5,18 @@ import { expandRows } from '../utils'
import {
useGetLatest,
actions,
functionalUpdate,
useMountedLayoutEffect,
makePropGetter,
} from '../publicUtils'
// Actions
actions.toggleExpanded = 'toggleExpanded'
actions.toggleAllExpanded = 'toggleAllExpanded'
actions.setExpanded = 'setExpanded'
actions.resetExpanded = 'resetExpanded'
actions.toggleRowExpanded = 'toggleRowExpanded'
actions.toggleAllRowsExpanded = 'toggleAllRowsExpanded'
export const useExpanded = hooks => {
hooks.getExpandedToggleProps = [defaultGetExpandedToggleProps]
hooks.getToggleAllRowsExpandedProps = [defaultGetToggleAllRowsExpandedProps]
hooks.getToggleRowExpandedProps = [defaultGetToggleRowExpandedProps]
hooks.stateReducers.push(reducer)
hooks.useInstance.push(useInstance)
hooks.prepareRow.push(prepareRow)
@ -25,17 +24,29 @@ export const useExpanded = hooks => {
useExpanded.pluginName = 'useExpanded'
const defaultGetExpandedToggleProps = (props, { row }) => [
const defaultGetToggleAllRowsExpandedProps = (props, { instance }) => [
props,
{
onClick: e => {
e.persist()
row.toggleExpanded()
instance.toggleAllRowsExpanded()
},
style: {
cursor: 'pointer',
},
title: 'Toggle Expanded',
title: 'Toggle All Rows Expanded',
},
]
const defaultGetToggleRowExpandedProps = (props, { row }) => [
props,
{
onClick: () => {
row.toggleRowExpanded()
},
style: {
cursor: 'pointer',
},
title: 'Toggle Row Expanded',
},
]
@ -55,14 +66,32 @@ function reducer(state, action, previousState, instance) {
}
}
if (action.type === actions.setExpanded) {
if (action.type === actions.toggleAllRowsExpanded) {
const { value } = action
const { isAllRowsExpanded, rowsById } = instance
const expandAll = typeof value !== 'undefined' ? value : !isAllRowsExpanded
if (expandAll) {
const expanded = {}
Object.keys(rowsById).forEach(rowId => {
expanded[rowId] = true
})
return {
...state,
expanded,
}
}
return {
...state,
expanded: functionalUpdate(action.expanded, state.expanded),
expanded: {},
}
}
if (action.type === actions.toggleExpanded) {
if (action.type === actions.toggleRowExpanded) {
const { id, value: setExpanded } = action
const exists = state.expanded[id]
@ -93,16 +122,28 @@ function useInstance(instance) {
const {
data,
rows,
rowsById,
manualExpandedKey = 'expanded',
paginateExpandedRows = true,
expandSubRows = true,
autoResetExpanded = true,
getHooks,
state: { expanded },
dispatch,
} = instance
const getAutoResetExpanded = useGetLatest(autoResetExpanded)
let isAllRowsExpanded = Boolean(
Object.keys(rowsById).length && Object.keys(expanded).length
)
if (isAllRowsExpanded) {
if (Object.keys(rowsById).some(id => !expanded[id])) {
isAllRowsExpanded = false
}
}
// Bypass any effects from firing when this changes
useMountedLayoutEffect(() => {
if (getAutoResetExpanded()) {
@ -110,13 +151,18 @@ function useInstance(instance) {
}
}, [dispatch, data])
const toggleExpanded = React.useCallback(
const toggleRowExpanded = React.useCallback(
(id, value) => {
dispatch({ type: actions.toggleExpanded, id, value })
dispatch({ type: actions.toggleRowExpanded, id, value })
},
[dispatch]
)
const toggleAllRowsExpanded = React.useCallback(
value => dispatch({ type: actions.toggleAllRowsExpanded, value }),
[dispatch]
)
const expandedRows = React.useMemo(() => {
if (paginateExpandedRows) {
return expandRows(rows, { manualExpandedKey, expanded, expandSubRows })
@ -129,20 +175,30 @@ function useInstance(instance) {
expanded,
])
const getInstance = useGetLatest(instance)
const getToggleAllRowsExpandedProps = makePropGetter(
getHooks().getToggleAllRowsExpandedProps,
{ instance: getInstance() }
)
Object.assign(instance, {
preExpandedRows: rows,
expandedRows,
rows: expandedRows,
toggleExpanded,
expandedDepth,
isAllRowsExpanded,
toggleRowExpanded,
toggleAllRowsExpanded,
getToggleAllRowsExpandedProps,
})
}
function prepareRow(row, { instance: { getHooks }, instance }) {
row.toggleExpanded = set => instance.toggleExpanded(row.id, set)
row.toggleRowExpanded = set => instance.toggleRowExpanded(row.id, set)
row.getExpandedToggleProps = makePropGetter(
getHooks().getExpandedToggleProps,
row.getToggleRowExpandedProps = makePropGetter(
getHooks().getToggleRowExpandedProps,
{
instance,
row,

View File

@ -108,7 +108,9 @@ function reducer(state, action, previousState, instance) {
filterTypes
)
if (shouldAutoRemoveFilter(filterMethod.autoRemove, filter.value, column)) {
if (
shouldAutoRemoveFilter(filterMethod.autoRemove, filter.value, column)
) {
return false
}
return true
@ -122,6 +124,7 @@ function useInstance(instance) {
data,
rows,
flatRows,
rowsById,
allColumns,
filterTypes: userFilterTypes,
manualFilters,
@ -175,12 +178,17 @@ function useInstance(instance) {
column.filterValue = found && found.value
})
const [filteredRows, filteredFlatRows] = React.useMemo(() => {
const [
filteredRows,
filteredFlatRows,
filteredRowsById,
] = React.useMemo(() => {
if (manualFilters || !filters.length) {
return [rows, flatRows]
return [rows, flatRows, rowsById]
}
const filteredFlatRows = []
const filteredRowsById = {}
// Filters top level and nested rows
const filterRows = (rows, depth = 0) => {
@ -231,6 +239,7 @@ function useInstance(instance) {
// would be required to do that recursion in some scenarios
filteredRows = filteredRows.map(row => {
filteredFlatRows.push(row)
filteredRowsById[row.id] = row
if (!row.subRows) {
return row
}
@ -246,8 +255,16 @@ function useInstance(instance) {
return filteredRows
}
return [filterRows(rows), filteredFlatRows]
}, [manualFilters, filters, rows, flatRows, allColumns, userFilterTypes])
return [filterRows(rows), filteredFlatRows, filteredRowsById]
}, [
manualFilters,
filters,
rows,
flatRows,
rowsById,
allColumns,
userFilterTypes,
])
React.useMemo(() => {
// Now that each filtered column has it's partially filtered rows,
@ -275,10 +292,13 @@ function useInstance(instance) {
Object.assign(instance, {
preFilteredRows: rows,
preFilteredFlatRows: flatRows,
preFilteredRowsById: rowsById,
filteredRows,
filteredFlatRows,
filteredRowsById,
rows: filteredRows,
flatRows: filteredFlatRows,
rowsById: filteredRowsById,
setFilter,
setAllFilters,
})

View File

@ -61,6 +61,7 @@ function useInstance(instance) {
data,
rows,
flatRows,
rowsById,
allColumns,
filterTypes: userFilterTypes,
globalFilter,
@ -88,12 +89,17 @@ function useInstance(instance) {
// cache for each row group (top-level rows, and each row's recursive subrows)
// This would make multi-filtering a lot faster though. Too far?
const [globalFilteredRows, globalFilteredFlatRows] = React.useMemo(() => {
const [
globalFilteredRows,
globalFilteredFlatRows,
globalFilteredRowsById,
] = React.useMemo(() => {
if (manualGlobalFilter || typeof globalFilterValue === 'undefined') {
return [rows, flatRows]
return [rows, flatRows, rowsById]
}
const filteredFlatRows = []
const filteredRowsById = {}
const filterMethod = getFilterMethod(
globalFilter,
@ -114,6 +120,7 @@ function useInstance(instance) {
globalFilterValue
).map(row => {
filteredFlatRows.push(row)
filteredRowsById[row.id] = row
return {
...row,
@ -125,15 +132,16 @@ function useInstance(instance) {
})
}
return [filterRows(rows), filteredFlatRows]
return [filterRows(rows), filteredFlatRows, filteredRowsById]
}, [
manualGlobalFilter,
globalFilterValue,
globalFilter,
userFilterTypes,
rows,
flatRows,
rowsById,
allColumns,
globalFilterValue,
])
const getAutoResetGlobalFilter = useGetLatest(autoResetGlobalFilter)
@ -147,10 +155,13 @@ function useInstance(instance) {
Object.assign(instance, {
preGlobalFilteredRows: rows,
preGlobalFilteredFlatRows: flatRows,
preGlobalFilteredRowsById: rowsById,
globalFilteredRows,
globalFilteredFlatRows,
globalFilteredRowsById,
rows: globalFilteredRows,
flatRows: globalFilteredFlatRows,
rowsById: globalFilteredRowsById,
setGlobalFilter,
})
}

View File

@ -13,6 +13,9 @@ import {
useGetLatest,
} from '../publicUtils'
const emptyArray = []
const emptyObject = {}
// Actions
actions.resetGroupBy = 'resetGroupBy'
actions.toggleGroupBy = 'toggleGroupBy'
@ -119,6 +122,7 @@ function useInstance(instance) {
data,
rows,
flatRows,
rowsById,
allColumns,
flatHeaders,
groupByFn = defaultGroupByFn,
@ -179,9 +183,25 @@ function useInstance(instance) {
)
})
const [groupedRows, groupedFlatRows] = React.useMemo(() => {
const [
groupedRows,
groupedFlatRows,
groupedRowsById,
onlyGroupedFlatRows,
onlyGroupedRowsById,
nonGroupedFlatRows,
nonGroupedRowsById,
] = React.useMemo(() => {
if (manualGroupBy || !groupBy.length) {
return [rows, flatRows]
return [
rows,
flatRows,
rowsById,
emptyArray,
emptyObject,
flatRows,
rowsById,
]
}
// Ensure that the list of filtered columns exist
@ -252,6 +272,11 @@ function useInstance(instance) {
}
let groupedFlatRows = []
const groupedRowsById = {}
const onlyGroupedFlatRows = []
const onlyGroupedRowsById = {}
const nonGroupedFlatRows = []
const nonGroupedRowsById = {}
// Recursively group the data
const groupUpRecursively = (rows, depth = 0, parentId) => {
@ -293,7 +318,17 @@ function useInstance(instance) {
index,
}
groupedFlatRows.push(row, ...subRows)
subRows.forEach(subRow => {
groupedFlatRows.push(subRow)
groupedRowsById[subRow.id] = subRow
if (subRow.isGrouped) {
onlyGroupedFlatRows.push(subRow)
onlyGroupedRowsById[subRow.id] = subRow
} else {
nonGroupedFlatRows.push(subRow)
nonGroupedRowsById[subRow.id] = subRow
}
})
return row
}
@ -304,13 +339,34 @@ function useInstance(instance) {
const groupedRows = groupUpRecursively(rows)
groupedRows.forEach(subRow => {
groupedFlatRows.push(subRow)
groupedRowsById[subRow.id] = subRow
if (subRow.isGrouped) {
onlyGroupedFlatRows.push(subRow)
onlyGroupedRowsById[subRow.id] = subRow
} else {
nonGroupedFlatRows.push(subRow)
nonGroupedRowsById[subRow.id] = subRow
}
})
// Assign the new data
return [groupedRows, groupedFlatRows]
return [
groupedRows,
groupedFlatRows,
groupedRowsById,
onlyGroupedFlatRows,
onlyGroupedRowsById,
nonGroupedFlatRows,
nonGroupedRowsById,
]
}, [
manualGroupBy,
groupBy,
rows,
flatRows,
rowsById,
allColumns,
userAggregations,
groupByFn,
@ -327,10 +383,17 @@ function useInstance(instance) {
Object.assign(instance, {
preGroupedRows: rows,
preGroupedFlatRow: flatRows,
preGroupedRowsById: rowsById,
groupedRows,
groupedFlatRows,
groupedRowsById,
onlyGroupedFlatRows,
onlyGroupedRowsById,
nonGroupedFlatRows,
nonGroupedRowsById,
rows: groupedRows,
flatRows: groupedFlatRows,
rowsById: groupedRowsById,
toggleGroupBy,
})
}

View File

@ -77,7 +77,15 @@ function useInstance(instance) {
pageCount: userPageCount,
paginateExpandedRows = true,
expandSubRows = true,
state: { pageSize, pageIndex, expanded, globalFilter, filters, groupBy, sortBy },
state: {
pageSize,
pageIndex,
expanded,
globalFilter,
filters,
groupBy,
sortBy,
},
dispatch,
data,
manualPagination,
@ -103,10 +111,10 @@ function useInstance(instance) {
}, [
dispatch,
manualPagination ? null : data,
manualPagination || manualGlobalFilter ? null : globalFilter,
manualPagination || manualFilters ? null : filters,
manualPagination || manualGroupBy ? null : groupBy,
manualPagination || manualSortBy ? null : sortBy,
manualGlobalFilter ? null : globalFilter,
manualFilters ? null : filters,
manualGroupBy ? null : groupBy,
manualSortBy ? null : sortBy,
])
const pageCount = manualPagination

View File

@ -86,7 +86,11 @@ function reducer(state, action, previousState, instance) {
if (action.type === actions.toggleAllRowsSelected) {
const { value: setSelected } = action
const { isAllRowsSelected, flatRowsById } = instance
const {
isAllRowsSelected,
rowsById,
nonGroupedRowsById = rowsById,
} = instance
const selectAll =
typeof setSelected !== 'undefined' ? setSelected : !isAllRowsSelected
@ -94,7 +98,7 @@ function reducer(state, action, previousState, instance) {
if (selectAll) {
const selectedRowIds = {}
Object.keys(flatRowsById).forEach(rowId => {
Object.keys(nonGroupedRowsById).forEach(rowId => {
selectedRowIds[rowId] = true
})
@ -112,12 +116,12 @@ function reducer(state, action, previousState, instance) {
if (action.type === actions.toggleRowSelected) {
const { id, value: setSelected } = action
const { flatGroupedRowsById, selectSubRows = true } = instance
const { rowsById, selectSubRows = true } = instance
// Join the ids of deep rows
// to make a key, then manage all of the keys
// in a flat object
const row = flatGroupedRowsById[id]
const row = rowsById[id]
const isSelected = row.isSelected
const shouldExist =
typeof setSelected !== 'undefined' ? setSelected : !isSelected
@ -129,7 +133,7 @@ function reducer(state, action, previousState, instance) {
let newSelectedRowIds = { ...state.selectedRowIds }
const handleRowById = id => {
const row = flatGroupedRowsById[id]
const row = rowsById[id]
if (!row.isGrouped) {
if (!isSelected && shouldExist) {
@ -159,7 +163,8 @@ function useInstance(instance) {
rows,
getHooks,
plugins,
flatRows,
rowsById,
nonGroupedRowsById = rowsById,
autoResetSelectedRows = true,
state: { selectedRowIds },
selectSubRows = true,
@ -173,20 +178,6 @@ function useInstance(instance) {
[]
)
const [flatRowsById, flatGroupedRowsById] = React.useMemo(() => {
const all = {}
const grouped = {}
flatRows.forEach(row => {
if (!row.isGrouped) {
all[row.id] = row
}
grouped[row.id] = row
})
return [all, grouped]
}, [flatRows])
const selectedFlatRows = React.useMemo(() => {
const selectedFlatRows = []
@ -206,11 +197,11 @@ function useInstance(instance) {
}, [rows, selectSubRows, selectedRowIds])
let isAllRowsSelected = Boolean(
Object.keys(flatRowsById).length && Object.keys(selectedRowIds).length
Object.keys(nonGroupedRowsById).length && Object.keys(selectedRowIds).length
)
if (isAllRowsSelected) {
if (Object.keys(flatRowsById).some(id => !selectedRowIds[id])) {
if (Object.keys(nonGroupedRowsById).some(id => !selectedRowIds[id])) {
isAllRowsSelected = false
}
}
@ -229,8 +220,7 @@ function useInstance(instance) {
)
const toggleRowSelected = React.useCallback(
(id, value) =>
dispatch({ type: actions.toggleRowSelected, id, value }),
(id, value) => dispatch({ type: actions.toggleRowSelected, id, value }),
[dispatch]
)
@ -242,13 +232,11 @@ function useInstance(instance) {
)
Object.assign(instance, {
flatRowsById,
flatGroupedRowsById,
selectedFlatRows,
isAllRowsSelected,
toggleRowSelected,
toggleAllRowsSelected,
getToggleAllRowsSelectedProps,
isAllRowsSelected,
})
}

View File

@ -180,6 +180,7 @@ function useInstance(instance) {
const {
data,
rows,
flatRows,
allColumns,
orderByFn = defaultOrderByFn,
sortTypes: userSortTypes,
@ -194,7 +195,7 @@ function useInstance(instance) {
autoResetSortBy = true,
} = instance
ensurePluginOrder(plugins, ['useFilters'], 'useSortBy', [])
ensurePluginOrder(plugins, ['useFilters'], 'useSortBy', ['useExpanded'])
// Updates sorting based on a columnId, desc flag and multi flag
const toggleSortBy = React.useCallback(
@ -249,11 +250,13 @@ function useInstance(instance) {
column.isSortedDesc = column.isSorted ? columnSort.desc : undefined
})
const sortedRows = React.useMemo(() => {
const [sortedRows, sortedFlatRows] = React.useMemo(() => {
if (manualSortBy || !sortBy.length) {
return rows
return [rows, flatRows]
}
const sortedFlatRows = []
// Filter out sortBys that correspond to non existing columns
const availableSortBy = sortBy.filter(sort =>
allColumns.find(col => col.id === sort.id)
@ -314,6 +317,7 @@ function useInstance(instance) {
// If there are sub-rows, sort them
sortedData.forEach(row => {
sortedFlatRows.push(row)
if (!row.subRows || row.subRows.length <= 1) {
return
}
@ -323,8 +327,16 @@ function useInstance(instance) {
return sortedData
}
return sortData(rows)
}, [manualSortBy, sortBy, rows, allColumns, orderByFn, userSortTypes])
return [sortData(rows), sortedFlatRows]
}, [
manualSortBy,
sortBy,
rows,
flatRows,
allColumns,
orderByFn,
userSortTypes,
])
const getAutoResetSortBy = useGetLatest(autoResetSortBy)
@ -336,8 +348,11 @@ function useInstance(instance) {
Object.assign(instance, {
preSortedRows: rows,
preSortedFlatRows: flatRows,
sortedRows,
sortedFlatRows,
rows: sortedRows,
flatRows: sortedFlatRows,
toggleSortBy,
})
}

View File

@ -1,5 +1,5 @@
import React from 'react'
import { defaultColumn, reduceHooks } from './publicUtils'
import { defaultColumn } from './publicUtils'
// Find the depth of the columns
export function findMaxDepth(columns, depth = 0) {
@ -88,97 +88,6 @@ export function decorateColumn(column, userDefaultColumn) {
return column
}
export function accessRowsForColumn({
data,
rows,
flatRows,
rowsById,
column,
getRowId,
getSubRows,
accessValueHooks,
getInstance,
}) {
// Access the row's data column-by-column
// We do it this way so we can incrementally add materialized
// columns after the first pass and avoid excessive looping
const accessRow = (originalRow, rowIndex, depth = 0, parent, parentRows) => {
// Keep the original reference around
const original = originalRow
const id = getRowId(originalRow, rowIndex, parent)
let row = rowsById[id]
// If the row hasn't been created, let's make it
if (!row) {
row = {
id,
original,
index: rowIndex,
depth,
cells: [{}], // This is a dummy cell
}
// Override common array functions (and the dummy cell's getCellProps function)
// to show an error if it is accessed without calling prepareRow
row.cells.map = unpreparedAccessWarning
row.cells.filter = unpreparedAccessWarning
row.cells.forEach = unpreparedAccessWarning
row.cells[0].getCellProps = unpreparedAccessWarning
// Create the cells and values
row.values = {}
// Push this row into the parentRows array
parentRows.push(row)
// Keep track of every row in a flat array
flatRows.push(row)
// Also keep track of every row by its ID
rowsById[id] = row
// Get the original subrows
row.originalSubRows = getSubRows(originalRow, rowIndex)
// Then recursively access them
if (row.originalSubRows) {
const subRows = []
row.originalSubRows.forEach((d, i) =>
accessRow(d, i, depth + 1, row, subRows)
)
// Keep the new subRows array on the row
row.subRows = subRows
}
} else if (row.subRows) {
// If the row exists, then it's already been accessed
// Keep recursing, but don't worry about passing the
// accumlator array (those rows already exist)
row.originalSubRows.forEach((d, i) => accessRow(d, i, depth + 1, row))
}
// If the column has an accessor, use it to get a value
if (column.accessor) {
row.values[column.id] = column.accessor(originalRow, rowIndex, row)
}
// Allow plugins to manipulate the column value
row.values[column.id] = reduceHooks(
accessValueHooks,
row.values[column.id],
{
row,
column,
instance: getInstance(),
},
true
)
}
data.forEach((originalRow, rowIndex) =>
accessRow(originalRow, rowIndex, 0, undefined, rows)
)
}
// Build the header groups from the bottom up
export function makeHeaderGroups(allColumns, defaultColumn) {
const headerGroups = []