Skip to content

Instantly share code, notes, and snippets.

@adamscybot
Created August 18, 2020 11:36
Show Gist options
  • Save adamscybot/65df3ce2b155c20c8b867786ef612e72 to your computer and use it in GitHub Desktop.
Save adamscybot/65df3ce2b155c20c8b867786ef612e72 to your computer and use it in GitHub Desktop.
Reac table v6 percentage column width plugin hook
/**
* This plugin hook for React table provides the ability for percentage
* widths to be provided in the column definitions, whilst maintaining the
* ability to resize those columns.
*/
export const usePercentageColumns = hooks => {
hooks.useInstance.push(useInstance)
hooks.getRowProps.push(getRowStyles)
hooks.getHeaderProps.push(getHeaderProps)
hooks.getCellProps.push(getCellProps)
}
usePercentageColumns.pluginName = 'usePercentageColumns'
const getRowStyles = (props, { instance }) => [
props,
{
style: {
display: 'flex',
},
},
]
const generateStyleForHeader = column => {
return {
width: 'auto',
// Allows sizing column smaller than the content.
minWidth: 0,
// If its a percentage, grow and fill with that percentage as the flex basis.
// If its an absolute value treat it as a fact that the column must be that size.
flex: /^[0-9]+%$/.test(column.totalWidth)
? `1 1 ${column.totalWidth}`
: `0 0 ${column.totalWidth}px`,
}
}
const getHeaderProps = (props, { column }) => [
props,
{
// We get a reference to each header cell so we can
// measure it later to aid in resizing.
headerCellRef: el => {
if (el !== null) column.el = el
},
style: generateStyleForHeader(column),
},
]
const getCellProps = (props, { cell }) => {
return [
props,
{
style: generateStyleForHeader(cell.column),
},
]
}
const useInstance = instance => {
// This resets the totalWidth from NaN caused by react-table assumption that provided widths
// are absolute values and not percentages. By putting it back to the percentage, it makes its
// way down to the styles of the cell.
instance.headers.forEach(header => {
if (/^[0-9]+%$/.test(header.width)) {
header.totalWidth = header.width
}
})
// The purpose of this block of code is to compose a new mouse over function
// with the existing one from the resize column plugin hook. In this handler,
// we change the width from the percentage value to an absolute value, just before
// we call the resize column plugin hook handler. This is because that handler is
// designed only to work with absolute values.
instance.flatHeaders.forEach(header => {
if (header.getResizerProps) {
const oldGetResizeProps = header.getResizerProps
header.getResizerProps = userProps => {
const {
onMouseDown: oldOnMouseDown,
...propsToSpread
} = oldGetResizeProps(userProps)
return {
...propsToSpread,
onMouseDown: e => {
const headersToResize = getLeafHeaders(header)
headersToResize.forEach((header, index) => {
header.totalWidth = header.el.getBoundingClientRect().width
})
return oldOnMouseDown(e, header)
},
}
}
}
})
}
function getLeafHeaders(header) {
const leafHeaders = []
const recurseHeader = header => {
if (header.columns && header.columns.length) {
header.columns.map(recurseHeader)
}
leafHeaders.push(header)
}
recurseHeader(header)
return leafHeaders
}
@JdeJ
Copy link

JdeJ commented Dec 23, 2020

Hi @adamscybot I need to use relative units for react-table columns dimensions. Your gist gets an error on header.totalWidth = header.el.getBoundingClientRect().width because this headerCellRef: el => { if (el !== null) column.el = el },

Can you explain me how to use your solution?

Thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment