|
|
@@ -7,7 +7,7 @@
|
|
7
|
7
|
* @module components/Layout/ResizableLayout
|
|
8
|
8
|
*/
|
|
9
|
9
|
|
|
10
|
|
-import React, { useState, useRef, useCallback } from 'react';
|
|
|
10
|
+import React, { useState, useRef, useCallback, useEffect } from 'react';
|
|
11
|
11
|
import { getItem, setItem } from '../../utils/storage';
|
|
12
|
12
|
|
|
13
|
13
|
/**
|
|
|
@@ -73,6 +73,8 @@ const ResizableLayout: React.FC<ResizableLayoutProps> = ({
|
|
73
|
73
|
}) => {
|
|
74
|
74
|
const containerRef = useRef<HTMLDivElement>(null);
|
|
75
|
75
|
const isDraggingRef = useRef(false);
|
|
|
76
|
+ const pendingWidthRef = useRef<number | null>(null);
|
|
|
77
|
+ const animationFrameRef = useRef<number | null>(null);
|
|
76
|
78
|
|
|
77
|
79
|
// State-tracked dragging flag for render (avoids ref read during render)
|
|
78
|
80
|
const [isDragging, setIsDragging] = useState(false);
|
|
|
@@ -88,12 +90,17 @@ const ResizableLayout: React.FC<ResizableLayoutProps> = ({
|
|
88
|
90
|
const applyWidth = useCallback(
|
|
89
|
91
|
(newWidth: number) => {
|
|
90
|
92
|
setLeftWidth(newWidth);
|
|
91
|
|
- setItem(STORAGE_KEY, newWidth);
|
|
92
|
93
|
onWidthChange?.(newWidth);
|
|
93
|
94
|
},
|
|
94
|
95
|
[onWidthChange]
|
|
95
|
96
|
);
|
|
96
|
97
|
|
|
|
98
|
+ useEffect(() => () => {
|
|
|
99
|
+ if (animationFrameRef.current !== null) {
|
|
|
100
|
+ cancelAnimationFrame(animationFrameRef.current);
|
|
|
101
|
+ }
|
|
|
102
|
+ }, []);
|
|
|
103
|
+
|
|
97
|
104
|
// ── Drag handlers ───────────────────────────────────────────────────────
|
|
98
|
105
|
|
|
99
|
106
|
const handleMouseDown = useCallback(
|
|
|
@@ -123,12 +130,29 @@ const ResizableLayout: React.FC<ResizableLayoutProps> = ({
|
|
123
|
130
|
maxWidthPct
|
|
124
|
131
|
);
|
|
125
|
132
|
|
|
126
|
|
- applyWidth(newLeftPct);
|
|
|
133
|
+ pendingWidthRef.current = newLeftPct;
|
|
|
134
|
+ if (animationFrameRef.current === null) {
|
|
|
135
|
+ animationFrameRef.current = requestAnimationFrame(() => {
|
|
|
136
|
+ animationFrameRef.current = null;
|
|
|
137
|
+ if (pendingWidthRef.current !== null) {
|
|
|
138
|
+ applyWidth(pendingWidthRef.current);
|
|
|
139
|
+ }
|
|
|
140
|
+ });
|
|
|
141
|
+ }
|
|
127
|
142
|
};
|
|
128
|
143
|
|
|
129
|
144
|
const onMouseUp = () => {
|
|
130
|
145
|
isDraggingRef.current = false;
|
|
131
|
146
|
setIsDragging(false);
|
|
|
147
|
+ if (animationFrameRef.current !== null) {
|
|
|
148
|
+ cancelAnimationFrame(animationFrameRef.current);
|
|
|
149
|
+ animationFrameRef.current = null;
|
|
|
150
|
+ }
|
|
|
151
|
+ if (pendingWidthRef.current !== null) {
|
|
|
152
|
+ applyWidth(pendingWidthRef.current);
|
|
|
153
|
+ setItem(STORAGE_KEY, pendingWidthRef.current);
|
|
|
154
|
+ pendingWidthRef.current = null;
|
|
|
155
|
+ }
|
|
132
|
156
|
document.removeEventListener('mousemove', onMouseMove);
|
|
133
|
157
|
document.removeEventListener('mouseup', onMouseUp);
|
|
134
|
158
|
};
|