UNPKG

react-table

Version:

Hooks for building lightweight, fast and extendable datagrids for React

295 lines (247 loc) 7.38 kB
/* istanbul ignore file */ import { actions, makePropGetter, ensurePluginOrder, useMountedLayoutEffect, useGetLatest, } from '../publicUtils' import { flattenColumns, getFirstDefined } from '../utils' // Actions actions.resetPivot = 'resetPivot' actions.togglePivot = 'togglePivot' export const _UNSTABLE_usePivotColumns = hooks => { hooks.getPivotToggleProps = [defaultGetPivotToggleProps] hooks.stateReducers.push(reducer) hooks.useInstanceAfterData.push(useInstanceAfterData) hooks.allColumns.push(allColumns) hooks.accessValue.push(accessValue) hooks.materializedColumns.push(materializedColumns) hooks.materializedColumnsDeps.push(materializedColumnsDeps) hooks.visibleColumns.push(visibleColumns) hooks.visibleColumnsDeps.push(visibleColumnsDeps) hooks.useInstance.push(useInstance) hooks.prepareRow.push(prepareRow) } _UNSTABLE_usePivotColumns.pluginName = 'usePivotColumns' const defaultPivotColumns = [] const defaultGetPivotToggleProps = (props, { header }) => [ props, { onClick: header.canPivot ? e => { e.persist() header.togglePivot() } : undefined, style: { cursor: header.canPivot ? 'pointer' : undefined, }, title: 'Toggle Pivot', }, ] // Reducer function reducer(state, action, previousState, instance) { if (action.type === actions.init) { return { pivotColumns: defaultPivotColumns, ...state, } } if (action.type === actions.resetPivot) { return { ...state, pivotColumns: instance.initialState.pivotColumns || defaultPivotColumns, } } if (action.type === actions.togglePivot) { const { columnId, value: setPivot } = action const resolvedPivot = typeof setPivot !== 'undefined' ? setPivot : !state.pivotColumns.includes(columnId) if (resolvedPivot) { return { ...state, pivotColumns: [...state.pivotColumns, columnId], } } return { ...state, pivotColumns: state.pivotColumns.filter(d => d !== columnId), } } } function useInstanceAfterData(instance) { instance.allColumns.forEach(column => { column.isPivotSource = instance.state.pivotColumns.includes(column.id) }) } function allColumns(columns, { instance }) { columns.forEach(column => { column.isPivotSource = instance.state.pivotColumns.includes(column.id) column.uniqueValues = new Set() }) return columns } function accessValue(value, { column }) { if (column.uniqueValues && typeof value !== 'undefined') { column.uniqueValues.add(value) } return value } function materializedColumns(materialized, { instance }) { const { allColumns, state } = instance if (!state.pivotColumns.length || !state.groupBy || !state.groupBy.length) { return materialized } const pivotColumns = state.pivotColumns .map(id => allColumns.find(d => d.id === id)) .filter(Boolean) const sourceColumns = allColumns.filter( d => !d.isPivotSource && !state.groupBy.includes(d.id) && !state.pivotColumns.includes(d.id) ) const buildPivotColumns = (depth = 0, parent, pivotFilters = []) => { const pivotColumn = pivotColumns[depth] if (!pivotColumn) { return sourceColumns.map(sourceColumn => { // TODO: We could offer support here for renesting pivoted // columns inside copies of their header groups. For now, // that seems like it would be (1) overkill on nesting, considering // you already get nesting for every pivot level and (2) // really hard. :) return { ...sourceColumn, canPivot: false, isPivoted: true, parent, depth: depth, id: `${parent ? `${parent.id}.${sourceColumn.id}` : sourceColumn.id}`, accessor: (originalRow, i, row) => { if (pivotFilters.every(filter => filter(row))) { return row.values[sourceColumn.id] } }, } }) } const uniqueValues = Array.from(pivotColumn.uniqueValues).sort() return uniqueValues.map(uniqueValue => { const columnGroup = { ...pivotColumn, Header: pivotColumn.PivotHeader || typeof pivotColumn.header === 'string' ? `${pivotColumn.Header}: ${uniqueValue}` : uniqueValue, isPivotGroup: true, parent, depth, id: parent ? `${parent.id}.${pivotColumn.id}.${uniqueValue}` : `${pivotColumn.id}.${uniqueValue}`, pivotValue: uniqueValue, } columnGroup.columns = buildPivotColumns(depth + 1, columnGroup, [ ...pivotFilters, row => row.values[pivotColumn.id] === uniqueValue, ]) return columnGroup }) } const newMaterialized = flattenColumns(buildPivotColumns()) return [...materialized, ...newMaterialized] } function materializedColumnsDeps( deps, { instance: { state: { pivotColumns, groupBy }, }, } ) { return [...deps, pivotColumns, groupBy] } function visibleColumns(visibleColumns, { instance: { state } }) { visibleColumns = visibleColumns.filter(d => !d.isPivotSource) if (state.pivotColumns.length && state.groupBy && state.groupBy.length) { visibleColumns = visibleColumns.filter( column => column.isGrouped || column.isPivoted ) } return visibleColumns } function visibleColumnsDeps(deps, { instance }) { return [...deps, instance.state.pivotColumns, instance.state.groupBy] } function useInstance(instance) { const { columns, allColumns, flatHeaders, // pivotFn = defaultPivotFn, // manualPivot, getHooks, plugins, dispatch, autoResetPivot = true, manaulPivot, disablePivot, defaultCanPivot, } = instance ensurePluginOrder(plugins, ['useGroupBy'], 'usePivotColumns') const getInstance = useGetLatest(instance) allColumns.forEach(column => { const { accessor, defaultPivot: defaultColumnPivot, disablePivot: columnDisablePivot, } = column column.canPivot = accessor ? getFirstDefined( column.canPivot, columnDisablePivot === true ? false : undefined, disablePivot === true ? false : undefined, true ) : getFirstDefined( column.canPivot, defaultColumnPivot, defaultCanPivot, false ) if (column.canPivot) { column.togglePivot = () => instance.togglePivot(column.id) } column.Aggregated = column.Aggregated || column.Cell }) const togglePivot = (columnId, value) => { dispatch({ type: actions.togglePivot, columnId, value }) } flatHeaders.forEach(header => { header.getPivotToggleProps = makePropGetter( getHooks().getPivotToggleProps, { instance: getInstance(), header, } ) }) const getAutoResetPivot = useGetLatest(autoResetPivot) useMountedLayoutEffect(() => { if (getAutoResetPivot()) { dispatch({ type: actions.resetPivot }) } }, [dispatch, manaulPivot ? null : columns]) Object.assign(instance, { togglePivot, }) } function prepareRow(row) { row.allCells.forEach(cell => { // Grouped cells are in the pivotColumns and the pivot cell for the row cell.isPivoted = cell.column.isPivoted }) }