From 66da7e58b7e8560228231a13eaad1884e6ba2c57 Mon Sep 17 00:00:00 2001 From: Tanner Linsley Date: Wed, 4 Dec 2019 01:45:05 -0500 Subject: [PATCH] Convert to core hook, use new reducerHanndler/actions --- .size-snapshot.json | 14 +-- docs/api.md | 9 +- src/hooks/useColumnVisibility.js | 113 ++++++++++++++++++++++++ src/hooks/useTable.js | 14 +-- src/index.js | 6 +- src/plugin-hooks/useColumnOrder.js | 3 +- src/plugin-hooks/useColumnVisibility.js | 71 --------------- src/plugin-hooks/useExpanded.js | 3 +- src/plugin-hooks/useFilters.js | 3 +- src/plugin-hooks/useGroupBy.js | 3 +- src/plugin-hooks/usePagination.js | 3 +- src/plugin-hooks/useResizeColumns.js | 3 +- src/plugin-hooks/useRowSelect.js | 3 +- src/plugin-hooks/useRowState.js | 10 ++- src/plugin-hooks/useSortBy.js | 10 ++- src/utils.js | 39 ++------ 16 files changed, 164 insertions(+), 143 deletions(-) create mode 100644 src/hooks/useColumnVisibility.js delete mode 100644 src/plugin-hooks/useColumnVisibility.js diff --git a/.size-snapshot.json b/.size-snapshot.json index 931f944..501adcf 100644 --- a/.size-snapshot.json +++ b/.size-snapshot.json @@ -1,20 +1,20 @@ { "dist/index.js": { - "bundled": 96781, - "minified": 46762, - "gzipped": 12303 + "bundled": 98780, + "minified": 48107, + "gzipped": 12496 }, "dist/index.es.js": { - "bundled": 96216, - "minified": 46270, - "gzipped": 12187, + "bundled": 97724, + "minified": 47164, + "gzipped": 12312, "treeshaked": { "rollup": { "code": 78, "import_statements": 21 }, "webpack": { - "code": 11143 + "code": 12434 } } } diff --git a/docs/api.md b/docs/api.md index 6d87fa6..ae3eece 100644 --- a/docs/api.md +++ b/docs/api.md @@ -139,12 +139,6 @@ The following options are supported on any column object you can pass to `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 `() => null` @@ -253,7 +247,8 @@ The following properties are available on every `Column` object returned by the - `id: String` - The resolved column ID from either the column's `accessor` or the column's hard-coded `id` property - `isVisible: Boolean` - - The resolved visible state for the column, derived from the column's `show` property + - Whether the column should be currently visible or not. + - Columns that are not visible are still used for sorting, filtering, etc. - `render: Function(type: String | Function | Component, ?props)` - This function is used to render content with the added context of a column. - The entire table `instance` will be passed to the renderer with the addition of a `column` property, containing a reference to the column diff --git a/src/hooks/useColumnVisibility.js b/src/hooks/useColumnVisibility.js new file mode 100644 index 0000000..e14e1b7 --- /dev/null +++ b/src/hooks/useColumnVisibility.js @@ -0,0 +1,113 @@ +import React from 'react' + +import { actions, reducerHandlers, functionalUpdate } from '../utils' + +const pluginName = 'useColumnVisibility' + +actions.resetColumnVisibility = 'resetColumnVisibility' +actions.setColumnVisibility = 'setColumnVisibility' +actions.toggleColumnVisibility = 'toggleColumnVisibility' + +reducerHandlers[pluginName] = (state, action) => { + if (action.type === actions.init) { + return { + columnVisibility: {}, + ...state, + } + } + + if (action.type === actions.resetColumnVisibility) { + return { + ...state, + columnVisibility: {}, + } + } + + if (action.type === actions.setColumnVisibility) { + if (action.columnId) { + return { + ...state, + columnVisibility: { + ...state.columnVisibility, + [action.columnId]: functionalUpdate( + action.value, + state.columnVisibility[action.columnId] + ), + }, + } + } + + return { + ...state, + columnVisibility: functionalUpdate(action.value, state.columnVisibility), + } + } +} + +export const useColumnVisibility = hooks => { + hooks.useInstanceBeforeDimensions.push(useInstanceBeforeDimensions) + hooks.useInstance.push(useInstance) +} + +useColumnVisibility.pluginName = pluginName + +function useInstanceBeforeDimensions(instance) { + const { + headers, + state: { columnVisibility }, + } = instance + + const handleColumn = (column, parentVisible) => { + column.isVisible = parentVisible && columnVisibility[column.id] !== false + + let totalVisibleHeaderCount = 0 + + if (column.headers && column.headers.length) { + column.headers.forEach( + subColumn => + (totalVisibleHeaderCount += handleColumn(subColumn, column.isVisible)) + ) + } else { + totalVisibleHeaderCount = column.isVisible ? 1 : 0 + } + + column.totalVisibleHeaderCount = totalVisibleHeaderCount + + return totalVisibleHeaderCount + } + + let totalVisibleHeaderCount = 0 + + headers.forEach( + subHeader => (totalVisibleHeaderCount += handleColumn(subHeader, true)) + ) + + return instance +} + +function useInstance(instance) { + const { flatHeaders, dispatch } = instance + + flatHeaders.forEach(column => { + column.setVisibility = value => { + dispatch({ + action: actions.setColumnVisibility, + columnId: column.id, + value, + }) + } + + column.toggleColumnVisibility = () => + dispatch({ type: actions.toggleColumnVisibility, columnId: column.id }) + }) + + const setColumnVisibility = React.useCallback( + value => dispatch({ type: actions.setColumnVisibility, value }), + [dispatch] + ) + + return { + ...instance, + setColumnVisibility, + } +} diff --git a/src/hooks/useTable.js b/src/hooks/useTable.js index 003bd86..631da3d 100755 --- a/src/hooks/useTable.js +++ b/src/hooks/useTable.js @@ -2,23 +2,22 @@ import React from 'react' // import { + actions, + reducerHandlers, applyHooks, applyPropHooks, mergeProps, flexRender, decorateColumnTree, makeHeaderGroups, - flattenBy + flattenBy, } from '../utils' +import { useColumnVisibility } from './useColumnVisibility' + 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 actions = { - init: 'init', -} -export const reducerHandlers = {} - const defaultInitialState = {} const defaultColumnInstance = {} const defaultReducer = (state, action, prevState) => state @@ -38,6 +37,8 @@ export const useTable = (props, ...plugins) => { debug, } = props + plugins = [useColumnVisibility, ...plugins] + debug = process.env.NODE_ENV === 'production' ? false : debug const reducer = (state, action) => { @@ -234,6 +235,7 @@ export const useTable = (props, ...plugins) => { if (process.env.NODE_ENV !== 'production' && debug) console.timeEnd('hooks.useInstanceBeforeDimensions') + // Header Visibility is needed by this point calculateDimensions(instanceRef.current) if (process.env.NODE_ENV !== 'production' && debug) diff --git a/src/index.js b/src/index.js index bc634aa..449a37b 100755 --- a/src/index.js +++ b/src/index.js @@ -1,7 +1,5 @@ -import * as utils from './utils' -export { utils } -export { defaultColumn } from './utils' -export { useTable, actions, reducerHandlers } from './hooks/useTable' +export * from './utils' +export { useTable } from './hooks/useTable' export { useExpanded } from './plugin-hooks/useExpanded' export { useFilters } from './plugin-hooks/useFilters' export { useGroupBy } from './plugin-hooks/useGroupBy' diff --git a/src/plugin-hooks/useColumnOrder.js b/src/plugin-hooks/useColumnOrder.js index d61fbfc..096442c 100644 --- a/src/plugin-hooks/useColumnOrder.js +++ b/src/plugin-hooks/useColumnOrder.js @@ -1,7 +1,6 @@ import React from 'react' -import { actions, reducerHandlers } from '../hooks/useTable' -import { functionalUpdate } from '../utils' +import { reducerHandlers, functionalUpdate, actions } from '../utils' const pluginName = 'useColumnOrder' diff --git a/src/plugin-hooks/useColumnVisibility.js b/src/plugin-hooks/useColumnVisibility.js deleted file mode 100644 index 6c2ed9d..0000000 --- a/src/plugin-hooks/useColumnVisibility.js +++ /dev/null @@ -1,71 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; - -import { defaultState } from '../hooks/useTable'; -import { addActions, actions } from '../actions'; -import { determineHeaderVisibility } from '../utils'; - -addActions('setColumnVisibility'); - -defaultState.hiddenColumns = []; - -const propTypes = {}; - -export const useColumnVisibility = (hooks) => { - hooks.columnsBeforeHeaderGroupsDeps.push((deps, instance) => { - return [...deps, instance.state.hiddenColumns]; - }); - hooks.useBeforeDimensions.push(useBeforeDimensions); -}; - -useColumnVisibility.pluginName = 'useColumnVisibility'; - -function useBeforeDimensions(instance) { - PropTypes.checkPropTypes(propTypes, instance, 'property', 'useColumnVisibility'); - - const { - flatHeaders, - state: { hiddenColumns }, - setState - } = instance; - - const setColumnVisibility = React.useCallback( - (updater) => { - return setState((old) => { - return { - ...old, - hiddenColumns: typeof updater === 'function' ? updater(old.hiddenColumns) : updater - }; - }, actions.setColumnVisibility); - }, - [setState] - ); - - flatHeaders.forEach((column) => { - column.show = !hiddenColumns.includes(column.id); - - column.setColumnVisibility = (show) => { - column.show = show; - const newhiddenColumns = new Set(hiddenColumns); - - if (show) { - newhiddenColumns.delete(column.id); - } else { - newhiddenColumns.add(column.id); - } - - setColumnVisibility(Array.from(newhiddenColumns)); - }; - - column.toggleColumnVisibility = () => { - column.setColumnVisibility(!column.show); - }; - }); - - determineHeaderVisibility(instance); - - return { - ...instance, - setColumnVisibility - }; -} diff --git a/src/plugin-hooks/useExpanded.js b/src/plugin-hooks/useExpanded.js index 4ccf245..6bd5a84 100755 --- a/src/plugin-hooks/useExpanded.js +++ b/src/plugin-hooks/useExpanded.js @@ -1,12 +1,13 @@ import React from 'react' import { + actions, + reducerHandlers, mergeProps, applyPropHooks, expandRows, safeUseLayoutEffect, } from '../utils' -import { actions, reducerHandlers } from '../hooks/useTable' const pluginName = 'useExpanded' diff --git a/src/plugin-hooks/useFilters.js b/src/plugin-hooks/useFilters.js index e271f28..e2e8f49 100755 --- a/src/plugin-hooks/useFilters.js +++ b/src/plugin-hooks/useFilters.js @@ -1,13 +1,14 @@ import React from 'react' import { + actions, + reducerHandlers, getFirstDefined, isFunction, safeUseLayoutEffect, functionalUpdate, } from '../utils' import * as filterTypes from '../filterTypes' -import { actions, reducerHandlers } from '../hooks/useTable' const pluginName = 'useFilters' diff --git a/src/plugin-hooks/useGroupBy.js b/src/plugin-hooks/useGroupBy.js index b1f8121..12f0689 100755 --- a/src/plugin-hooks/useGroupBy.js +++ b/src/plugin-hooks/useGroupBy.js @@ -1,8 +1,9 @@ import React from 'react' import * as aggregations from '../aggregations' -import { actions, reducerHandlers } from '../hooks/useTable' import { + actions, + reducerHandlers, mergeProps, applyPropHooks, defaultGroupByFn, diff --git a/src/plugin-hooks/usePagination.js b/src/plugin-hooks/usePagination.js index 15338e2..8d7c36d 100755 --- a/src/plugin-hooks/usePagination.js +++ b/src/plugin-hooks/usePagination.js @@ -2,8 +2,9 @@ import React from 'react' // -import { actions, reducerHandlers } from '../hooks/useTable' import { + actions, + reducerHandlers, ensurePluginOrder, safeUseLayoutEffect, expandRows, diff --git a/src/plugin-hooks/useResizeColumns.js b/src/plugin-hooks/useResizeColumns.js index 7d069e0..981c1c9 100644 --- a/src/plugin-hooks/useResizeColumns.js +++ b/src/plugin-hooks/useResizeColumns.js @@ -1,7 +1,8 @@ import React from 'react' -import { actions, reducerHandlers } from '../hooks/useTable' import { + actions, + reducerHandlers, defaultColumn, getFirstDefined, mergeProps, diff --git a/src/plugin-hooks/useRowSelect.js b/src/plugin-hooks/useRowSelect.js index 90999db..4e721ad 100644 --- a/src/plugin-hooks/useRowSelect.js +++ b/src/plugin-hooks/useRowSelect.js @@ -1,12 +1,13 @@ import React from 'react' import { + actions, + reducerHandlers, mergeProps, applyPropHooks, ensurePluginOrder, safeUseLayoutEffect, } from '../utils' -import { actions, reducerHandlers } from '../hooks/useTable' const pluginName = 'useRowSelect' diff --git a/src/plugin-hooks/useRowState.js b/src/plugin-hooks/useRowState.js index 8109343..080b7bb 100644 --- a/src/plugin-hooks/useRowState.js +++ b/src/plugin-hooks/useRowState.js @@ -1,9 +1,11 @@ import React from 'react' -// - -import { actions, reducerHandlers } from '../hooks/useTable' -import { functionalUpdate, safeUseLayoutEffect } from '../utils' +import { + actions, + reducerHandlers, + functionalUpdate, + safeUseLayoutEffect, +} from '../utils' const pluginName = 'useRowState' diff --git a/src/plugin-hooks/useSortBy.js b/src/plugin-hooks/useSortBy.js index cf35e8e..c419c10 100755 --- a/src/plugin-hooks/useSortBy.js +++ b/src/plugin-hooks/useSortBy.js @@ -1,9 +1,11 @@ import React from 'react' -import { ensurePluginOrder, defaultColumn, safeUseLayoutEffect } from '../utils' -import { actions, reducerHandlers } from '../hooks/useTable' -import * as sortTypes from '../sortTypes' import { + actions, + reducerHandlers, + ensurePluginOrder, + defaultColumn, + safeUseLayoutEffect, mergeProps, applyPropHooks, getFirstDefined, @@ -11,6 +13,8 @@ import { isFunction, } from '../utils' +import * as sortTypes from '../sortTypes' + const pluginName = 'useSortBy' // Actions diff --git a/src/utils.js b/src/utils.js index 7ac3b72..3f55e9e 100755 --- a/src/utils.js +++ b/src/utils.js @@ -1,5 +1,11 @@ import React from 'react' +export const actions = { + init: 'init', +} + +export const reducerHandlers = {} + export const defaultColumn = { Cell: ({ cell: { value = '' } }) => String(value), width: 150, @@ -175,39 +181,6 @@ export function makeHeaderGroups(flatColumns, defaultColumn) { return headerGroups.reverse() } -export function determineHeaderVisibility(instance) { - const { headers } = instance - - const handleColumn = (column, parentVisible) => { - column.isVisible = parentVisible - ? typeof column.show === 'function' - ? column.show(instance) - : !!column.show - : false - - let totalVisibleHeaderCount = 0 - - if (column.headers && column.headers.length) { - column.headers.forEach( - subColumn => - (totalVisibleHeaderCount += handleColumn(subColumn, column.isVisible)) - ) - } else { - totalVisibleHeaderCount = column.isVisible ? 1 : 0 - } - - column.totalVisibleHeaderCount = totalVisibleHeaderCount - - return totalVisibleHeaderCount - } - - let totalVisibleHeaderCount = 0 - - headers.forEach( - subHeader => (totalVisibleHeaderCount += handleColumn(subHeader, true)) - ) -} - export function getBy(obj, path, def) { if (!path) { return obj