1- import { useLayoutEffect , useRef } from 'react' ;
1+ import { useLayoutEffect , useState } from 'react' ;
22import { flushSync } from 'react-dom' ;
33
4- import type { CalculatedColumn , StateSetter } from '../types' ;
4+ import type { CalculatedColumn , ResizedWidth , StateSetter } from '../types' ;
55import type { DataGridProps } from '../DataGrid' ;
66
77export function useColumnWidths < R , SR > (
@@ -16,17 +16,27 @@ export function useColumnWidths<R, SR>(
1616 setMeasuredColumnWidths : StateSetter < ReadonlyMap < string , number > > ,
1717 onColumnResize : DataGridProps < R , SR > [ 'onColumnResize' ]
1818) {
19- const prevGridWidthRef = useRef ( gridWidth ) ;
19+ const [ columnToAutoResize , setColumnToAutoResize ] = useState < {
20+ readonly key : string ;
21+ readonly width : ResizedWidth ;
22+ } | null > ( null ) ;
23+ const [ prevGridWidth , setPreviousGridWidth ] = useState ( gridWidth ) ;
2024 const columnsCanFlex : boolean = columns . length === viewportColumns . length ;
2125 // Allow columns to flex again when...
2226 const ignorePreviouslyMeasuredColumns : boolean =
2327 // there is enough space for columns to flex and the grid was resized
24- columnsCanFlex && gridWidth !== prevGridWidthRef . current ;
28+ columnsCanFlex && gridWidth !== prevGridWidth ;
2529 const newTemplateColumns = [ ...templateColumns ] ;
2630 const columnsToMeasure : string [ ] = [ ] ;
2731
2832 for ( const { key, idx, width } of viewportColumns ) {
29- if (
33+ if ( key === columnToAutoResize ?. key ) {
34+ newTemplateColumns [ idx ] =
35+ columnToAutoResize . width === 'max-content'
36+ ? columnToAutoResize . width
37+ : `${ columnToAutoResize . width } px` ;
38+ columnsToMeasure . push ( key ) ;
39+ } else if (
3040 typeof width === 'string' &&
3141 ( ignorePreviouslyMeasuredColumns || ! measuredColumnWidths . has ( key ) ) &&
3242 ! resizedColumnWidths . has ( key )
@@ -38,12 +48,10 @@ export function useColumnWidths<R, SR>(
3848
3949 const gridTemplateColumns = newTemplateColumns . join ( ' ' ) ;
4050
41- useLayoutEffect ( ( ) => {
42- prevGridWidthRef . current = gridWidth ;
43- updateMeasuredWidths ( columnsToMeasure ) ;
44- } ) ;
51+ useLayoutEffect ( updateMeasuredWidths ) ;
4552
46- function updateMeasuredWidths ( columnsToMeasure : readonly string [ ] ) {
53+ function updateMeasuredWidths ( ) {
54+ setPreviousGridWidth ( gridWidth ) ;
4755 if ( columnsToMeasure . length === 0 ) return ;
4856
4957 setMeasuredColumnWidths ( ( measuredColumnWidths ) => {
@@ -62,40 +70,54 @@ export function useColumnWidths<R, SR>(
6270
6371 return hasChanges ? newMeasuredColumnWidths : measuredColumnWidths ;
6472 } ) ;
65- }
6673
67- function handleColumnResize ( column : CalculatedColumn < R , SR > , nextWidth : number | 'max-content' ) {
68- const { key : resizingKey } = column ;
69- const newTemplateColumns = [ ... templateColumns ] ;
70- const columnsToMeasure : string [ ] = [ ] ;
71-
72- for ( const { key , idx , width } of viewportColumns ) {
73- if ( resizingKey === key ) {
74- const width = typeof nextWidth === 'number' ? ` ${ nextWidth } px` : nextWidth ;
75- newTemplateColumns [ idx ] = width ;
76- } else if ( columnsCanFlex && typeof width === 'string' && ! resizedColumnWidths . has ( key ) ) {
77- newTemplateColumns [ idx ] = width ;
78- columnsToMeasure . push ( key ) ;
79- }
74+ if ( columnToAutoResize !== null ) {
75+ const resizingKey = columnToAutoResize . key ;
76+ setResizedColumnWidths ( ( resizedColumnWidths ) => {
77+ const oldWidth = resizedColumnWidths . get ( resizingKey ) ;
78+ const newWidth = measureColumnWidth ( gridRef , resizingKey ) ;
79+ if ( newWidth !== undefined && oldWidth !== newWidth ) {
80+ const newResizedColumnWidths = new Map ( resizedColumnWidths ) ;
81+ newResizedColumnWidths . set ( resizingKey , newWidth ) ;
82+ return newResizedColumnWidths ;
83+ }
84+ return resizedColumnWidths ;
85+ } ) ;
86+ setColumnToAutoResize ( null ) ;
8087 }
88+ }
8189
82- gridRef . current ! . style . gridTemplateColumns = newTemplateColumns . join ( ' ' ) ;
83- const measuredWidth =
84- typeof nextWidth === 'number' ? nextWidth : measureColumnWidth ( gridRef , resizingKey ) ! ;
90+ function handleColumnResize ( column : CalculatedColumn < R , SR > , nextWidth : ResizedWidth ) {
91+ const { key : resizingKey } = column ;
8592
86- // TODO: remove
87- // need flushSync to keep frozen column offsets in sync
88- // we may be able to use `startTransition` or even `requestIdleCallback` instead
8993 flushSync ( ( ) => {
90- setResizedColumnWidths ( ( resizedColumnWidths ) => {
91- const newResizedColumnWidths = new Map ( resizedColumnWidths ) ;
92- newResizedColumnWidths . set ( resizingKey , measuredWidth ) ;
93- return newResizedColumnWidths ;
94+ if ( columnsCanFlex ) {
95+ // remeasure all the columns that can flex and are not resized by the user
96+ setMeasuredColumnWidths ( ( measuredColumnWidths ) => {
97+ const newMeasuredColumnWidths = new Map ( measuredColumnWidths ) ;
98+ for ( const { key, width } of viewportColumns ) {
99+ if ( resizingKey !== key && typeof width === 'string' && ! resizedColumnWidths . has ( key ) ) {
100+ newMeasuredColumnWidths . delete ( key ) ;
101+ }
102+ }
103+ return newMeasuredColumnWidths ;
104+ } ) ;
105+ }
106+
107+ setColumnToAutoResize ( {
108+ key : resizingKey ,
109+ width : nextWidth
94110 } ) ;
95- updateMeasuredWidths ( columnsToMeasure ) ;
96111 } ) ;
97112
98- onColumnResize ?.( column , measuredWidth ) ;
113+ if ( onColumnResize ) {
114+ const previousWidth = resizedColumnWidths . get ( resizingKey ) ;
115+ const newWidth =
116+ typeof nextWidth === 'number' ? nextWidth : measureColumnWidth ( gridRef , resizingKey ) ;
117+ if ( newWidth !== undefined && newWidth !== previousWidth ) {
118+ onColumnResize ( column , newWidth ) ;
119+ }
120+ }
99121 }
100122
101123 return {
0 commit comments