@@ -254,13 +254,16 @@ export const Table: React.FC<TableProps> = ({
254254 handleUpdateFilter : handleUpdateFilterFromHook ,
255255 handleGroupByChange,
256256 handleSortChange,
257+ handleColumnWidthChange,
257258 handleEnsureAllFieldsRegistered,
258259 handleFieldToggle,
259260 handleFieldOrderChange,
260261 updateViewConfigBackend,
262+ localColumnWidths,
261263 } = useTableViewConfig ( {
262264 baseMeta,
263265 effectiveViewId,
266+ tableId,
264267 columns,
265268 updateViewMutation : actions ?. updateView ,
266269 searchableColumns,
@@ -376,25 +379,33 @@ export const Table: React.FC<TableProps> = ({
376379 const [ openColumnDropdownIndex , setOpenColumnDropdownIndex ] = useState < number | null > ( null ) ;
377380 const dragPinnedStateRef = useRef < boolean | null > ( null ) ;
378381
379- // Static column widths (no resize). Prefer view meta widths, else column.width, else 235.
382+ // Column resize state
383+ const [ resizingColumnIndex , setResizingColumnIndex ] = useState < number | null > ( null ) ;
384+ const [ liveColumnWidths , setLiveColumnWidths ] = useState < number [ ] | null > ( null ) ;
385+ const resizeStartRef = useRef < { x : number ; width : number } | null > ( null ) ;
386+ const columnWidthsRef = useRef < number [ ] > ( [ ] ) ;
387+
388+ // Column widths: live drag preview > localStorage > view meta > column.width > 235.
380389 const columnWidths = useMemo ( ( ) => {
381- return ( visibleColumns || [ ] ) . map ( ( c ) => {
390+ return ( visibleColumns || [ ] ) . map ( ( c , idx ) => {
382391 const widthKey = String ( c . id || c . key ) ;
392+ // 1. Live drag preview (highest priority during resize)
393+ if ( liveColumnWidths && liveColumnWidths [ idx ] != null ) return liveColumnWidths [ idx ] ;
394+ // 2. localStorage (user's saved overrides)
395+ const fromLocal = ( localColumnWidths ?? { } ) [ widthKey ] ?? ( localColumnWidths ?? { } ) [ c . key ] ;
396+ if ( typeof fromLocal === 'number' ) return fromLocal ;
397+ // 3. view meta (backend)
383398 const fromView = viewConfigState . columnWidths ?. [ widthKey ] ?? viewConfigState . columnWidths ?. [ c . key ] ;
384-
385- let width = 235 ;
386-
387- if ( typeof c . width === 'number' ) {
388- width = c . width ;
389- }
390-
391- if ( typeof fromView === 'number' ) {
392- width = fromView ;
393- }
394-
395- return width ;
399+ if ( typeof fromView === 'number' ) return fromView ;
400+ // 4. column.width (API)
401+ if ( typeof c . width === 'number' ) return c . width ;
402+ // 5. default
403+ return 235 ;
396404 } ) ;
397- } , [ visibleColumns , viewConfigState . columnWidths ] ) ;
405+ } , [ visibleColumns , viewConfigState . columnWidths , localColumnWidths , liveColumnWidths ] ) ;
406+
407+ // Keep columnWidthsRef in sync with the computed columnWidths
408+ useEffect ( ( ) => { columnWidthsRef . current = columnWidths ; } , [ columnWidths ] ) ;
398409
399410 // Keep only first visible column pinned by default (Noco-like behavior)
400411 const pinnedColumnIds = useMemo ( ( ) => {
@@ -724,6 +735,48 @@ export const Table: React.FC<TableProps> = ({
724735 }
725736 } , [ canReorderColumns , handleColumnDragEndFromHook , visibleColumns , localFieldConfig , effectiveViewId , baseMeta , actions ?. updateView , handleFieldOrderChange ] ) ;
726737
738+ // Column resize: start drag
739+ const handleResizeMouseDown = useCallback ( ( e : React . MouseEvent , columnIndex : number ) => {
740+ e . preventDefault ( ) ;
741+ e . stopPropagation ( ) ;
742+ const currentWidths = columnWidthsRef . current ;
743+ setResizingColumnIndex ( columnIndex ) ;
744+ resizeStartRef . current = {
745+ x : e . clientX ,
746+ width : currentWidths [ columnIndex ] ?? 235 ,
747+ } ;
748+
749+ const handleMouseMove = ( moveEvent : MouseEvent ) => {
750+ if ( ! resizeStartRef . current ) return ;
751+ const delta = moveEvent . clientX - resizeStartRef . current . x ;
752+ const newWidth = Math . max ( 80 , resizeStartRef . current . width + delta ) ;
753+ setLiveColumnWidths ( prev => {
754+ const base = prev ?? currentWidths ;
755+ const next = [ ...base ] ;
756+ next [ columnIndex ] = newWidth ;
757+ return next ;
758+ } ) ;
759+ } ;
760+
761+ const handleMouseUp = ( upEvent : MouseEvent ) => {
762+ if ( ! resizeStartRef . current ) return ;
763+ const delta = upEvent . clientX - resizeStartRef . current . x ;
764+ const newWidth = Math . max ( 80 , resizeStartRef . current . width + delta ) ;
765+ const column = visibleColumns [ columnIndex ] ;
766+ if ( column ) {
767+ handleColumnWidthChange ( String ( column . id || column . key ) , newWidth ) ;
768+ }
769+ resizeStartRef . current = null ;
770+ setResizingColumnIndex ( null ) ;
771+ setLiveColumnWidths ( null ) ;
772+ document . removeEventListener ( 'mousemove' , handleMouseMove ) ;
773+ document . removeEventListener ( 'mouseup' , handleMouseUp ) ;
774+ } ;
775+
776+ document . addEventListener ( 'mousemove' , handleMouseMove ) ;
777+ document . addEventListener ( 'mouseup' , handleMouseUp ) ;
778+ } , [ visibleColumns , handleColumnWidthChange ] ) ;
779+
727780 // Wrapper for handleEditColumn to use hook's version
728781 const handleEditColumn = useCallback ( ( col : ColumnConfig , index : number , event ?: { target : HTMLElement } ) => {
729782 handleEditColumnFromHook ( col , index , event ) ;
@@ -857,6 +910,14 @@ export const Table: React.FC<TableProps> = ({
857910 onOpenChange = { handleDropdownOpenChange }
858911 />
859912 ) }
913+ { /* Column resize handle */ }
914+ < div
915+ className = { `absolute right-0 top-0 h-full w-1 cursor-col-resize z-10 transition-colors
916+ ${ resizingColumnIndex === index ? 'bg-primary' : 'hover:bg-primary/60 bg-transparent' }
917+ ` }
918+ style = { { top : 0 } }
919+ onMouseDown = { ( e ) => handleResizeMouseDown ( e , index ) }
920+ />
860921 </ div >
861922 </ div >
862923 ) ;
@@ -878,7 +939,9 @@ export const Table: React.FC<TableProps> = ({
878939 openColumnDropdownIndex ,
879940 canShowColumnDropdown ,
880941 getColumnHeaderClassName ,
881- setOpenColumnDropdownIndex
942+ setOpenColumnDropdownIndex ,
943+ handleResizeMouseDown ,
944+ resizingColumnIndex ,
882945 ] ) ;
883946
884947 const getEstimatedItemCount = useCallback ( ( ) => {
0 commit comments