1
- import { useLayoutEffect , useRef } from 'react' ;
1
+ import { useLayoutEffect , useState } from 'react' ;
2
2
import { flushSync } from 'react-dom' ;
3
3
4
- import type { CalculatedColumn , StateSetter } from '../types' ;
4
+ import type { CalculatedColumn , ResizedWidth , StateSetter } from '../types' ;
5
5
import type { DataGridProps } from '../DataGrid' ;
6
6
7
7
export function useColumnWidths < R , SR > (
@@ -16,17 +16,27 @@ export function useColumnWidths<R, SR>(
16
16
setMeasuredColumnWidths : StateSetter < ReadonlyMap < string , number > > ,
17
17
onColumnResize : DataGridProps < R , SR > [ 'onColumnResize' ]
18
18
) {
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 ) ;
20
24
const columnsCanFlex : boolean = columns . length === viewportColumns . length ;
21
25
// Allow columns to flex again when...
22
26
const ignorePreviouslyMeasuredColumns : boolean =
23
27
// there is enough space for columns to flex and the grid was resized
24
- columnsCanFlex && gridWidth !== prevGridWidthRef . current ;
28
+ columnsCanFlex && gridWidth !== prevGridWidth ;
25
29
const newTemplateColumns = [ ...templateColumns ] ;
26
30
const columnsToMeasure : string [ ] = [ ] ;
27
31
28
32
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 (
30
40
typeof width === 'string' &&
31
41
( ignorePreviouslyMeasuredColumns || ! measuredColumnWidths . has ( key ) ) &&
32
42
! resizedColumnWidths . has ( key )
@@ -38,12 +48,10 @@ export function useColumnWidths<R, SR>(
38
48
39
49
const gridTemplateColumns = newTemplateColumns . join ( ' ' ) ;
40
50
41
- useLayoutEffect ( ( ) => {
42
- prevGridWidthRef . current = gridWidth ;
43
- updateMeasuredWidths ( columnsToMeasure ) ;
44
- } ) ;
51
+ useLayoutEffect ( updateMeasuredWidths ) ;
45
52
46
- function updateMeasuredWidths ( columnsToMeasure : readonly string [ ] ) {
53
+ function updateMeasuredWidths ( ) {
54
+ setPreviousGridWidth ( gridWidth ) ;
47
55
if ( columnsToMeasure . length === 0 ) return ;
48
56
49
57
setMeasuredColumnWidths ( ( measuredColumnWidths ) => {
@@ -62,40 +70,54 @@ export function useColumnWidths<R, SR>(
62
70
63
71
return hasChanges ? newMeasuredColumnWidths : measuredColumnWidths ;
64
72
} ) ;
65
- }
66
73
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 ) ;
80
87
}
88
+ }
81
89
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 ;
85
92
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
89
93
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
94
110
} ) ;
95
- updateMeasuredWidths ( columnsToMeasure ) ;
96
111
} ) ;
97
112
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
+ }
99
121
}
100
122
101
123
return {
0 commit comments