11import { Box , Table , type MantineSize } from '@mantine/core' ;
22import { useMergedRef } from '@mantine/hooks' ;
33import clsx from 'clsx' ;
4- import { useCallback , useEffect , useMemo , useRef , useState } from 'react' ;
4+ import { useCallback , useMemo , useState } from 'react' ;
55import { DataTableColumnsProvider } from './DataTableDragToggleProvider' ;
66import { DataTableEmptyRow } from './DataTableEmptyRow' ;
77import { DataTableEmptyState } from './DataTableEmptyState' ;
@@ -134,19 +134,9 @@ export function DataTable<T>({
134134 return groups ?. flatMap ( ( group ) => group . columns ) ?? columns ! ;
135135 } , [ columns , groups ] ) ;
136136
137- const hasResizableColumns = useMemo ( ( ) => {
138- return effectiveColumns . some ( ( col ) => col . resizable ) ;
139- } , [ effectiveColumns ] ) ;
140-
141137 // When columns are resizable, start with auto layout to let the browser
142138 // compute natural widths, then capture them and switch to fixed layout.
143139 const [ fixedLayoutEnabled , setFixedLayoutEnabled ] = useState ( false ) ;
144- const prevHasResizableRef = useRef < boolean | null > ( null ) ;
145-
146- const dragToggle = useDataTableColumns ( {
147- key : storeColumnsKey ,
148- columns : effectiveColumns ,
149- } ) ;
150140
151141 const { refs, onScroll : handleScrollPositionChange } = useDataTableInjectCssVariables ( {
152142 scrollCallbacks : {
@@ -159,124 +149,19 @@ export function DataTable<T>({
159149 withRowBorders : otherProps . withRowBorders ,
160150 } ) ;
161151
152+ const dragToggle = useDataTableColumns ( {
153+ key : storeColumnsKey ,
154+ columns : effectiveColumns ,
155+ headerRef : refs . header as any ,
156+ scrollViewportRef : refs . scrollViewport as any ,
157+ onFixedLayoutChange : setFixedLayoutEnabled ,
158+ } ) ;
159+
162160 const mergedTableRef = useMergedRef ( refs . table , tableRef ) ;
163161 const mergedViewportRef = useMergedRef ( refs . scrollViewport , scrollViewportRef ) ;
164162
165163 const rowExpansionInfo = useRowExpansion < T > ( { rowExpansion, records, idAccessor } ) ;
166164
167- // Initialize content-based widths when resizable columns are present.
168- useEffect ( ( ) => {
169- // If resizable just became disabled, revert to auto layout
170- if ( ! hasResizableColumns ) {
171- prevHasResizableRef . current = false ;
172- setFixedLayoutEnabled ( false ) ;
173- return ;
174- }
175-
176- // Only run when switching from non-resizable -> resizable
177- if ( prevHasResizableRef . current === true ) return ;
178- prevHasResizableRef . current = true ;
179-
180- let raf = requestAnimationFrame ( ( ) => {
181- const thead = refs . header . current ;
182- if ( ! thead ) {
183- setFixedLayoutEnabled ( true ) ;
184- return ;
185- }
186-
187- const headerCells = Array . from ( thead . querySelectorAll < HTMLTableCellElement > ( 'th[data-accessor]' ) ) ;
188-
189- if ( headerCells . length === 0 ) {
190- setFixedLayoutEnabled ( true ) ;
191- return ;
192- }
193-
194- let measured = headerCells
195- . map ( ( cell ) => {
196- const accessor = cell . getAttribute ( 'data-accessor' ) ;
197- if ( ! accessor || accessor === '__selection__' ) return null ;
198- const width = Math . round ( cell . getBoundingClientRect ( ) . width ) ;
199- return { accessor, width } as const ;
200- } )
201- . filter ( Boolean ) as Array < { accessor : string ; width : number } > ;
202-
203- const viewport = refs . scrollViewport . current ;
204- const viewportWidth = viewport ?. clientWidth ?? 0 ;
205- if ( viewportWidth && measured . length ) {
206- const total = measured . reduce ( ( acc , u ) => acc + u . width , 0 ) ;
207- const overflow = total - viewportWidth ;
208- if ( overflow > 0 ) {
209- const last = measured [ measured . length - 1 ] ;
210- last . width = Math . max ( 50 , last . width - overflow ) ;
211- }
212- }
213-
214- const updates = measured . map ( ( m ) => ( { accessor : m . accessor , width : `${ m . width } px` } ) ) ;
215-
216- setTimeout ( ( ) => {
217- if ( updates . length ) dragToggle . setMultipleColumnWidths ( updates ) ;
218- setFixedLayoutEnabled ( true ) ;
219- } , 0 ) ;
220- } ) ;
221-
222- return ( ) => cancelAnimationFrame ( raf ) ;
223- } , [ hasResizableColumns ] ) ;
224-
225- // If user resets widths to 'initial', recompute widths and re-enable fixed layout.
226- const allResizableWidthsInitial = useMemo ( ( ) => {
227- if ( ! hasResizableColumns ) return false ;
228- return effectiveColumns
229- . filter ( ( c ) => c . resizable && ! c . hidden && c . accessor !== '__selection__' )
230- . every ( ( c ) => c . width === undefined || c . width === '' || c . width === 'initial' ) ;
231- } , [ effectiveColumns , hasResizableColumns ] ) ;
232-
233- useEffect ( ( ) => {
234- if ( ! hasResizableColumns ) return ;
235- if ( ! allResizableWidthsInitial ) return ;
236-
237- // Temporarily disable fixed layout so natural widths can be measured
238- setFixedLayoutEnabled ( false ) ;
239-
240- let raf = requestAnimationFrame ( ( ) => {
241- const thead = refs . header . current ;
242- if ( ! thead ) {
243- setFixedLayoutEnabled ( true ) ;
244- return ;
245- }
246-
247- const headerCells = Array . from ( thead . querySelectorAll < HTMLTableCellElement > ( 'th[data-accessor]' ) ) ;
248-
249- let measured = headerCells
250- . map ( ( cell ) => {
251- const accessor = cell . getAttribute ( 'data-accessor' ) ;
252- if ( ! accessor || accessor === '__selection__' ) return null ;
253- const width = Math . round ( cell . getBoundingClientRect ( ) . width ) ;
254- return { accessor, width } as const ;
255- } )
256- . filter ( Boolean ) as Array < { accessor : string ; width : number } > ;
257-
258- const viewport = refs . scrollViewport . current ;
259- const viewportWidth = viewport ?. clientWidth ?? 0 ;
260- if ( viewportWidth && measured . length ) {
261- const total = measured . reduce ( ( acc , u ) => acc + u . width , 0 ) ;
262- const overflow = total - viewportWidth ;
263- if ( overflow > 0 ) {
264- const last = measured [ measured . length - 1 ] ;
265- last . width = Math . max ( 50 , last . width - overflow ) ;
266- }
267- }
268-
269- const updates = measured . map ( ( m ) => ( { accessor : m . accessor , width : `${ m . width } px` } ) ) ;
270-
271- setTimeout ( ( ) => {
272- if ( updates . length ) dragToggle . setMultipleColumnWidths ( updates ) ;
273- setFixedLayoutEnabled ( true ) ;
274- } , 0 ) ;
275- } ) ;
276-
277- return ( ) => cancelAnimationFrame ( raf ) ;
278- } , [ hasResizableColumns , allResizableWidthsInitial , refs . header , dragToggle ] ) ;
279-
280165 const handlePageChange = useCallback (
281166 ( page : number ) => {
282167 refs . scrollViewport . current ?. scrollTo ( { top : 0 , left : 0 } ) ;
@@ -385,7 +270,7 @@ export function DataTable<T>({
385270 'mantine-datatable-pin-last-column' : pinLastColumn ,
386271 'mantine-datatable-selection-column-visible' : selectionColumnVisible ,
387272 'mantine-datatable-pin-first-column' : pinFirstColumn ,
388- 'mantine-datatable-resizable-columns' : fixedLayoutEnabled ,
273+ 'mantine-datatable-resizable-columns' : dragToggle . hasResizableColumns && fixedLayoutEnabled ,
389274 } ,
390275 classNames ?. table
391276 ) }
0 commit comments