mirror of
https://github.com/gosticks/react-table.git
synced 2025-10-16 11:55:36 +00:00
Since useColumns was relying on groupBy logic, this was code smell. I wanted useGroupBy to be able to add that logic all by itself and not have to have dependencies in the core of the table. To fix that, I've moved the core column and row logic to the useTable hook and added a new hook 'columnsBeforeHeaderGroups' to allow useGroupBy to do what i needs in a more pure way.
308 lines
7.4 KiB
JavaScript
Executable File
308 lines
7.4 KiB
JavaScript
Executable File
import React from 'react'
|
|
|
|
// Find the depth of the columns
|
|
export function findMaxDepth(columns, depth = 0) {
|
|
return columns.reduce((prev, curr) => {
|
|
if (curr.columns) {
|
|
return Math.max(prev, findMaxDepth(curr.columns, depth + 1))
|
|
}
|
|
return depth
|
|
}, 0)
|
|
}
|
|
|
|
export function decorateColumn(column, defaultColumn, parent, depth, index) {
|
|
// Apply the defaultColumn
|
|
column = { ...defaultColumn, ...column }
|
|
|
|
// First check for string accessor
|
|
let { id, accessor, Header } = column
|
|
|
|
if (typeof accessor === 'string') {
|
|
id = id || accessor
|
|
const accessorString = accessor
|
|
accessor = row => getBy(row, accessorString)
|
|
}
|
|
|
|
if (!id && typeof Header === 'string') {
|
|
id = Header
|
|
}
|
|
|
|
if (!id && column.columns) {
|
|
console.error(column)
|
|
throw new Error('A column ID (or unique "Header" value) is required!')
|
|
}
|
|
|
|
if (!id) {
|
|
console.error(column)
|
|
throw new Error('A column ID (or string accessor) is required!')
|
|
}
|
|
|
|
column = {
|
|
Header: ({ id }) => id,
|
|
Cell: ({ value }) => value,
|
|
show: true,
|
|
...column,
|
|
id,
|
|
accessor,
|
|
parent,
|
|
depth,
|
|
index,
|
|
}
|
|
|
|
return column
|
|
}
|
|
|
|
// Build the visible columns, headers and flat column list
|
|
export function decorateColumnTree(columns, defaultColumn, parent, depth = 0) {
|
|
return columns.map((column, columnIndex) => {
|
|
column = decorateColumn(column, defaultColumn, parent, depth, columnIndex)
|
|
if (column.columns) {
|
|
column.columns = decorateColumnTree(
|
|
column.columns,
|
|
defaultColumn,
|
|
column,
|
|
depth + 1
|
|
)
|
|
}
|
|
return column
|
|
})
|
|
}
|
|
|
|
// Build the header groups from the bottom up
|
|
export function makeHeaderGroups(columns, maxDepth, defaultColumn) {
|
|
const headerGroups = []
|
|
|
|
const removeChildColumns = column => {
|
|
delete column.columns
|
|
if (column.parent) {
|
|
removeChildColumns(column.parent)
|
|
}
|
|
}
|
|
columns.forEach(removeChildColumns)
|
|
|
|
const buildGroup = (columns, depth = 0) => {
|
|
const headerGroup = {
|
|
headers: [],
|
|
}
|
|
|
|
const parentColumns = []
|
|
|
|
const hasParents = columns.some(col => col.parent)
|
|
|
|
columns.forEach(column => {
|
|
const isFirst = !parentColumns.length
|
|
let latestParentColumn = [...parentColumns].reverse()[0]
|
|
|
|
// If the column has a parent, add it if necessary
|
|
if (column.parent) {
|
|
if (isFirst || latestParentColumn.originalID !== column.parent.id) {
|
|
parentColumns.push({
|
|
...column.parent,
|
|
originalID: column.parent.id,
|
|
id: [column.parent.id, parentColumns.length].join('_'),
|
|
})
|
|
}
|
|
} else if (hasParents) {
|
|
// If other columns have parents, add a place holder if necessary
|
|
const placeholderColumn = decorateColumn(
|
|
{
|
|
originalID: [column.id, 'placeholder', maxDepth - depth].join('_'),
|
|
id: [
|
|
column.id,
|
|
'placeholder',
|
|
maxDepth - depth,
|
|
parentColumns.length,
|
|
].join('_'),
|
|
},
|
|
defaultColumn
|
|
)
|
|
if (
|
|
isFirst ||
|
|
latestParentColumn.originalID !== placeholderColumn.originalID
|
|
) {
|
|
parentColumns.push(placeholderColumn)
|
|
}
|
|
}
|
|
|
|
// Establish the new columns[] relationship on the parent
|
|
if (column.parent || hasParents) {
|
|
latestParentColumn = [...parentColumns].reverse()[0]
|
|
latestParentColumn.columns = latestParentColumn.columns || []
|
|
if (!latestParentColumn.columns.includes(column)) {
|
|
latestParentColumn.columns.push(column)
|
|
}
|
|
}
|
|
|
|
headerGroup.headers.push(column)
|
|
})
|
|
|
|
headerGroups.push(headerGroup)
|
|
|
|
if (parentColumns.length) {
|
|
buildGroup(parentColumns)
|
|
}
|
|
}
|
|
|
|
buildGroup(columns)
|
|
|
|
return headerGroups.reverse()
|
|
}
|
|
|
|
export function getBy(obj, path, def) {
|
|
if (!path) {
|
|
return obj
|
|
}
|
|
const pathObj = makePathArray(path)
|
|
let val
|
|
try {
|
|
val = pathObj.reduce((cursor, pathPart) => cursor[pathPart], obj)
|
|
} catch (e) {
|
|
// continue regardless of error
|
|
}
|
|
return typeof val !== 'undefined' ? val : def
|
|
}
|
|
|
|
export function defaultOrderByFn(arr, funcs, dirs) {
|
|
return [...arr].sort((rowA, rowB) => {
|
|
for (let i = 0; i < funcs.length; i += 1) {
|
|
const sortFn = funcs[i]
|
|
const desc = dirs[i] === false || dirs[i] === 'desc'
|
|
const sortInt = sortFn(rowA, rowB)
|
|
if (sortInt !== 0) {
|
|
return desc ? -sortInt : sortInt
|
|
}
|
|
}
|
|
return dirs[0] ? rowA.index - rowB.index : rowB.index - rowA.index
|
|
})
|
|
}
|
|
|
|
export function getFirstDefined(...args) {
|
|
for (let i = 0; i < args.length; i += 1) {
|
|
if (typeof args[i] !== 'undefined') {
|
|
return args[i]
|
|
}
|
|
}
|
|
}
|
|
|
|
export function defaultGroupByFn(rows, grouper) {
|
|
return rows.reduce((prev, row, i) => {
|
|
const resKey =
|
|
typeof grouper === 'function'
|
|
? grouper(row.values, i)
|
|
: row.values[grouper]
|
|
prev[resKey] = Array.isArray(prev[resKey]) ? prev[resKey] : []
|
|
prev[resKey].push(row)
|
|
return prev
|
|
}, {})
|
|
}
|
|
|
|
export function setBy(obj = {}, path, value) {
|
|
const recurse = (obj, depth = 0) => {
|
|
const key = path[depth]
|
|
const target = typeof obj[key] !== 'object' ? {} : obj[key]
|
|
const subValue =
|
|
depth === path.length - 1 ? value : recurse(target, depth + 1)
|
|
return {
|
|
...obj,
|
|
[key]: subValue,
|
|
}
|
|
}
|
|
|
|
return recurse(obj)
|
|
}
|
|
|
|
export function getElementDimensions(element) {
|
|
const rect = element.getBoundingClientRect()
|
|
const style = window.getComputedStyle(element)
|
|
const margins = {
|
|
left: parseInt(style.marginLeft),
|
|
right: parseInt(style.marginRight),
|
|
}
|
|
const padding = {
|
|
left: parseInt(style.paddingLeft),
|
|
right: parseInt(style.paddingRight),
|
|
}
|
|
return {
|
|
left: Math.ceil(rect.left),
|
|
width: Math.ceil(rect.width),
|
|
outerWidth: Math.ceil(
|
|
rect.width + margins.left + margins.right + padding.left + padding.right
|
|
),
|
|
marginLeft: margins.left,
|
|
marginRight: margins.right,
|
|
paddingLeft: padding.left,
|
|
paddingRight: padding.right,
|
|
scrollWidth: element.scrollWidth,
|
|
}
|
|
}
|
|
|
|
export function flexRender(Comp, props) {
|
|
if (typeof Comp === 'function' || typeof Comp === 'object') {
|
|
return <Comp {...props} />
|
|
}
|
|
return Comp
|
|
}
|
|
|
|
export const mergeProps = (...groups) => {
|
|
let props = {}
|
|
groups.forEach(({ style = {}, className, ...rest } = {}) => {
|
|
props = {
|
|
...props,
|
|
...rest,
|
|
style: {
|
|
...(props.style || {}),
|
|
...style,
|
|
},
|
|
className: [props.className, className].filter(Boolean).join(' '),
|
|
}
|
|
})
|
|
return props
|
|
}
|
|
|
|
export const applyHooks = (hooks, initial, ...args) =>
|
|
hooks.reduce((prev, next) => next(prev, ...args), initial)
|
|
|
|
export const applyPropHooks = (hooks, ...args) =>
|
|
hooks.reduce((prev, next) => mergeProps(prev, next(...args)), {})
|
|
|
|
export const warnUnknownProps = props => {
|
|
if (Object.keys(props).length) {
|
|
throw new Error(
|
|
`Unknown options passed to useReactTable:
|
|
|
|
${JSON.stringify(props, null, 2)}`
|
|
)
|
|
}
|
|
}
|
|
|
|
export function sum(arr) {
|
|
return arr.reduce((prev, curr) => prev + curr, 0)
|
|
}
|
|
|
|
export function isFunction(a) {
|
|
if (typeof a === 'function') {
|
|
return a
|
|
}
|
|
}
|
|
|
|
//
|
|
|
|
function makePathArray(obj) {
|
|
return flattenDeep(obj)
|
|
.join('.')
|
|
.replace(/\[/g, '.')
|
|
.replace(/\]/g, '')
|
|
.split('.')
|
|
}
|
|
|
|
function flattenDeep(arr, newArr = []) {
|
|
if (!Array.isArray(arr)) {
|
|
newArr.push(arr)
|
|
} else {
|
|
for (let i = 0; i < arr.length; i += 1) {
|
|
flattenDeep(arr[i], newArr)
|
|
}
|
|
}
|
|
return newArr
|
|
}
|