-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[data grid] Autofit Column Width #1241
Comments
One solution could be to implement an We likely want to implement a double click -> auto-size UX on the column headers, we could share the same method. |
That would be a tricky one as the virtualisation doesn't render every cells so it will be tricky to calculate the width for the unrendered cells. It could also increase dynamically when we render content that does not fit the size of a cell. |
@dtassone I imagine that only using the visible rows for a given column would be enough? |
Probably 🤔 |
This comment was marked as outdated.
This comment was marked as outdated.
I've implemented with a wrapper component, something like this: import React, { Component } from 'react';
import _ from 'lodash';
import { DataGrid } from '@mui/x-data-grid';
import CircularProgress from '@material-ui/core/CircularProgress';
import Grid from '@material-ui/core/Grid';
export default class StretchyDataGrid extends Component {
constructor(props) {
super(props);
this.ref = React.createRef();
const { columns } = props;
this.state = { mappedColumns:_.clone(columns) };
}
autoSizeColumns = () => {
const domRows = [ ...this.ref.current?.querySelectorAll('.MuiDataGrid-row') ];
const domReady = (this.props.rows?.length === 0) || domRows.length;
if(!domReady) {
setTimeout(this.autoSizeColumns);
return;
}
this.setState(previousState => {
const mappedColumns = _.clone(previousState.mappedColumns);
mappedColumns
.forEach((col, idx) => {
const maxContentWidth = domRows
.reduce((previousMax, dR) => Math.max(previousMax, dR.childNodes[idx].scrollWidth), 0);
if(maxContentWidth < this.ref.current.clientWidth / mappedColumns.length) {
col.width = maxContentWidth;
delete col.flex;
} else {
delete col.width;
col.flex = 1;
}
});
return { mappedColumns, resized:true };
});
}
render() {
const { mappedColumns, resized } = this.state;
const { columns, ...props } = this.props;
return (
<>
{!resized &&
<Grid container alignItems="center" justifyContent="center" style={{height:'100%', position:'absolute', opacity:0.8, backgroundColor:'white', textAlign:'center', zIndex:1}}>
<Grid item xs={12}>
<CircularProgress/>
</Grid>
</Grid>
}
<DataGrid
ref={this.ref}
onResize={this.autoSizeColumns}
columns={mappedColumns}
{...props /* eslint-disable-line react/jsx-props-no-spreading */}
/>
</>
);
}
} This is extracted from a more complicated implementation, so apologies if there's anything missing/broken. |
This comment was marked as resolved.
This comment was marked as resolved.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
my solution for now taken out of my project. Let me know if any of u improve on it because I was in a rush. make sure u have a columns state to use this, as well as gridRef if u want the fitScreen option to work too // returns width of the biggest row inside a column
function maxOfCol(colIndex: number) {
let invisibleContainer = document.createElement("div")
invisibleContainer.style.visibility = "hidden"
invisibleContainer.style.zIndex = "-9999999999"
invisibleContainer.style.position = "absolute"
invisibleContainer.style.fontSize = "14px"
invisibleContainer.style.top = "0"
invisibleContainer.style.left = "0"
document.body.append(invisibleContainer)
let widths: any[] = []
document.querySelectorAll<HTMLElement>(`[aria-colindex="${colIndex + 1}"]`).forEach(cell => {
let invisibleCell = document.createElement("div")
invisibleCell.innerHTML = cell.innerHTML
invisibleCell.style.width = "max-content"
invisibleCell.style.maxWidth = "none"
invisibleCell.style.minWidth = "none"
invisibleContainer.append(invisibleCell)
widths.push(Math.ceil(invisibleCell.clientWidth))
})
let max = Math.max(...widths)
if (max !== 0 && max < 50) { max = 50 }
invisibleContainer.remove()
return max
}
type ResizeType = "condense" | "maxContent" | "fitScreen"
function resizeColumns(columns: GridColDef[], resizeType: ResizeType): GridColDef[] {
let cols = [...columns]
cols.forEach((col: GridColDef, index: number) => {
if (resizeType === "fitScreen") {
let gridWidth = gridRef.current.clientWidth
let idColWidth = 90
let scrollbar = 20
let padding = 24 * 2
const columnWidth = Math.floor((gridWidth - padding - scrollbar - idColWidth) / (cols.length - 1))
col.width = columnWidth
if (col.field.toLowerCase() === "id") { col.width = idColWidth }
} else if (resizeType === "maxContent") {
let maxColWidth = maxOfCol(index)
col.width = maxColWidth + 22
} else {
col.width = 0
}
})
return cols
}
const CustomToolbar: FC = () => {
const [resizeMenuAnchorEl, setResizeMenuAnchorEl] = useState<null | HTMLElement>(null)
const isResizeMenuOpen = Boolean(resizeMenuAnchorEl)
const openResizeMenu = (event: React.MouseEvent<HTMLButtonElement>) => { setResizeMenuAnchorEl(event.currentTarget) }
const closeResizeMenu = () => { setResizeMenuAnchorEl(null) }
return (
<GridToolbarContainer sx={{ justifyContent: "space-between" }}>
<div style={{ display: "flex", flexWrap: "wrap" }}>
<GridToolbarColumnsButton sx={{ ml: "10px", fontSize: "13px" }} />
<GridToolbarFilterButton sx={{ ml: "10px", fontSize: "13px" }} />
<GridToolbarDensitySelector sx={{ ml: "10px", fontSize: "13px" }} />
<GridToolbarExport sx={{ ml: "10px", fontSize: "13px" }} />
</div>
<div>
<Tooltip title="Resize columns">
<Button onClick={openResizeMenu}><ExpandIcon sx={{ transform: "rotate(90deg)" }} /></Button>
</Tooltip>
<Menu anchorEl={resizeMenuAnchorEl} open={isResizeMenuOpen} onClose={closeResizeMenu}>
<MenuItem onClick={() => { closeResizeMenu(); setColumns(resizeColumns(columns, "fitScreen")) }}>Fit Screen</MenuItem>
<MenuItem onClick={() => { closeResizeMenu(); setColumns(resizeColumns(columns, "maxContent")) }}>Max Content</MenuItem>
<MenuItem onClick={() => { closeResizeMenu(); setColumns(resizeColumns(columns, "condense")) }}>Condense</MenuItem>
</Menu>
</div>
</GridToolbarContainer>
)
}
return (
<>
<div style={{ height: '100%', width: '100%', padding: "24px" }} ref={gridRef} >
<DataGridPro
// ...
components={{ Toolbar: CustomToolbar }}
// ...
/>
</div> |
|
May I ask if there is any update or plan for this feature? |
It is very strange that at least some basic solution to this problem does not exist out of the box (for example, devs could add matching column size to content along with a horizontal table scroll). I'm waiting for a solution. |
At least for now to make it easier just have this option available only if virtualization is disabled |
temporary solution (simplified) if u have checkbox column disabled, the colindex should be +1 rather than +2 // returns width of the biggest row inside a column
function maxOfCol(colIndex) {
let invisibleContainer = document.createElement("div")
invisibleContainer.style = "visibility: hidden; z-index: -9999999999; position: absolute; font-size: 14px; top: 0; left: 0;"
document.body.append(invisibleContainer)
let widths = []
document.querySelectorAll(`[aria-colindex="${colIndex + 2}"]`).forEach(cell => {
let invisibleCell = document.createElement("div")
invisibleCell.innerHTML = cell.innerHTML
invisibleCell.style = "width: max-content; max-width: none; min-width: none;"
invisibleContainer.append(invisibleCell)
widths.push(Math.ceil(invisibleCell.clientWidth + 1))
})
let max = Math.max(...widths)
if (max !== 0 && max < 30) { max = 30 }
invisibleContainer.remove()
return max
}
// use this function somewhere as a button. table content must be rendered first
function resizeColumns() {
let cols = [...columns] // columns is my columns state.
cols.forEach((col, index) => {
let maxColWidth = maxOfCol(index)
col.width = maxColWidth + 20
})
setColumns(cols)
} |
Hi, |
This would be a great addittion, instead of just setting a huge overflow area (big blank space which looks cheesy) |
This comment was marked as resolved.
This comment was marked as resolved.
👍 needed on our side as well. A bit sad to have most of the text truncated all the time |
We also currently have a very custom implementation in place: We listen to the loading state via |
could provide an example? sound interesting |
We need this. it's a bit frustrating to program it yourself |
Yup it is, it seems pretty fundamental too. |
Shockingly realized there was no such option and you have to adjust values manually, praying there won't be a content for a cell that breaks everything. |
Following! This is a critical feature for Mobile where the column widths should be as small as possible while still fitting the data. On web I can just set the fixed width to be a bit bigger than the anticipated size of the field, but on mobile this results in all of my columns being a bit larger than necessary. |
A very common feature of spreadsheets and grids like this is to size a column to fit content when the user double-clicks the column header divider instead of dragging it. I was surprised that DataGrid doesn't support this! Would be great to have general facilities for sizing columns to fit content. |
We really need this feature. Many thanks. |
Really need this feature!! 🙏 |
This is really needed. |
Adding on here... really need this. This has been open for 2+ years, definitely re-evaluating just building our own tables if a feature like this has been on back-burner for so long despite how much the community wants official support. |
We really need this as well, is this actually going to happen in the near future or should we start migrating to other alternatives such as AG-Grid? |
+++ it`s really needed feature |
Highly anticipated feature! |
Hello! @joserodolfofreitas, please take a look |
We really need this feature! |
Hi, we are Premium subscribers and we really need this feature. Many thanks. Mauro |
This comment was marked as outdated.
This comment was marked as outdated.
Is this in the works? |
@dizzyjaguar We plan to work on it this quarter (Q3) |
Summary 💡
It would be helpful to add fields to the columns definition to handle dynamic content where the actual size is not known beforehand. For example, to have a flag like fitContent and a minWidth/maxWidth field to make columns responsive to the actual value at runtime. Right now, I need to either specify absolute widths or proportional flex values.
Feedback from #5875:
Examples 🌈
Motivation 🔦
I built an application where users can select data from a database and the structure depends on the actual query that the user defines. For example, the user could run a query with two returning columns where the first is a string and the second a number (e.g. Product name and price). The number column does not need much space and the string column should get all the remaining space.
Use cases
This is an established pattern that has been used for years in various data grids, like MS Excel, Google Sheets, and many others.
Challenges
Benchmark
autoFitColumns
The text was updated successfully, but these errors were encountered: