1
- import { ForwardedRef , forwardRef , useEffect , useState } from 'react' ;
1
+ import { ForwardedRef , forwardRef , useEffect , useMemo , useState } from 'react' ;
2
2
import { useHover , useMove } from 'react-aria' ;
3
3
4
4
import { BasePropsWithoutChildren , Styles , tasty } from '../../tasty/index' ;
@@ -19,35 +19,45 @@ export interface CubeResizablePanelProps extends CubePanelProps {
19
19
}
20
20
21
21
const HandlerElement = tasty ( {
22
+ qa : 'ResizeHandler' ,
22
23
styles : {
23
24
// The real size is slightly bigger than the visual one.
24
25
width : {
25
- '' : 'auto ' ,
26
+ '' : 'initial ' ,
26
27
horizontal : '9px' ,
28
+ 'disabled & horizontal' : '1bw' ,
27
29
} ,
28
30
height : {
29
31
'' : '9px' ,
30
- horizontal : 'auto' ,
32
+ horizontal : 'initial' ,
33
+ 'disabled & !horizontal' : '1bw' ,
31
34
} ,
32
35
top : {
33
- '' : 'initial' ,
34
- horizontal : 0 ,
35
- '[data-direction="top"]' : - 2 ,
36
+ '' : 0 ,
37
+ '[data-direction="top"]' : 'initial' ,
36
38
} ,
37
39
bottom : {
38
- '' : 'initial' ,
39
- horizontal : 0 ,
40
- '[data-direction="bottom"]' : - 2 ,
40
+ '' : 0 ,
41
+ '[data-direction="bottom"]' : 'initial' ,
41
42
} ,
42
43
right : {
43
44
'' : 0 ,
44
- horizontal : 'initial' ,
45
- '[data-direction="right"]' : - 2 ,
45
+ '[data-direction="right"]' : 'initial' ,
46
46
} ,
47
47
left : {
48
48
'' : 0 ,
49
- horizontal : 'initial' ,
50
- '[data-direction="left"]' : - 2 ,
49
+ '[data-direction="left"]' : 'initial' ,
50
+ } ,
51
+ // Transform requires a separate visual size property to respect size boundaries
52
+ transform : {
53
+ '[data-direction="top"]' :
54
+ 'translate(0, (@size-compensation - @visual-size))' ,
55
+ '[data-direction="right"]' :
56
+ 'translate((@visual-size - @size-compensation), 0)' ,
57
+ '[data-direction="bottom"]' :
58
+ 'translate(0, (@visual-size - @size-compensation))' ,
59
+ '[data-direction="left"]' :
60
+ 'translate((@size-compensation - @visual-size), 0)' ,
51
61
} ,
52
62
position : 'absolute' ,
53
63
zIndex : 1 ,
@@ -62,20 +72,27 @@ const HandlerElement = tasty({
62
72
padding : 0 ,
63
73
boxSizing : 'border-box' ,
64
74
transition : 'theme' ,
75
+ '--size-compensation' : {
76
+ '' : '7px' ,
77
+ disabled : '1bw' ,
78
+ } ,
65
79
66
80
Track : {
67
81
width : {
68
82
'' : 'initial' ,
69
83
horizontal : '5px' ,
84
+ 'disabled & horizontal' : '1px' ,
70
85
} ,
71
86
height : {
72
87
'' : '5px' ,
73
88
horizontal : 'initial' ,
89
+ 'disabled & !horizontal' : '1px' ,
74
90
} ,
75
91
position : 'absolute' ,
76
92
inset : {
77
93
'' : '2px 0' ,
78
94
horizontal : '0 2px' ,
95
+ disabled : '0 0' ,
79
96
} ,
80
97
fill : {
81
98
'' : '#border-opaque' ,
@@ -85,6 +102,10 @@ const HandlerElement = tasty({
85
102
} ,
86
103
87
104
Drag : {
105
+ hide : {
106
+ '' : false ,
107
+ disabled : true ,
108
+ } ,
88
109
width : {
89
110
'' : '3x' ,
90
111
horizontal : '3px' ,
@@ -152,6 +173,11 @@ const PanelElement = tasty(Panel, {
152
173
} ,
153
174
placeSelf : 'stretch' ,
154
175
touchAction : 'none' ,
176
+
177
+ '--indent-compensation' : {
178
+ '' : '5px' ,
179
+ disabled : '1bw' ,
180
+ } ,
155
181
} ,
156
182
} ) ;
157
183
@@ -166,7 +192,7 @@ function ResizablePanel(
166
192
size : providedSize ,
167
193
onSizeChange,
168
194
minSize = 200 ,
169
- maxSize = isControllable ? undefined : 400 ,
195
+ maxSize = isControllable ? undefined : 'min(50%, 400px)' ,
170
196
} = props ;
171
197
172
198
const [ isDragging , setIsDragging ] = useState ( false ) ;
@@ -189,6 +215,20 @@ function ResizablePanel(
189
215
let [ size , setSize ] = useState < number > (
190
216
providedSize != null ? clamp ( providedSize ) : 200 ,
191
217
) ;
218
+ let [ visualSize , setVisualSize ] = useState < number | null > ( null ) ;
219
+
220
+ useEffect ( ( ) => {
221
+ if ( ref . current ) {
222
+ const offsetProp = isHorizontal ? 'offsetWidth' : 'offsetHeight' ;
223
+ const containerSize = ref . current [ offsetProp ] ;
224
+
225
+ if ( Math . abs ( containerSize - size ) < 1 && ! isDisabled ) {
226
+ setVisualSize ( size ) ;
227
+ } else {
228
+ setVisualSize ( containerSize ) ;
229
+ }
230
+ }
231
+ } , [ size , isDisabled ] ) ;
192
232
193
233
let { moveProps } = useMove ( {
194
234
onMoveStart ( e ) {
@@ -200,8 +240,8 @@ function ResizablePanel(
200
240
201
241
const offsetProp = isHorizontal ? 'offsetWidth' : 'offsetHeight' ;
202
242
203
- if ( ref . current && Math . abs ( ref . current [ offsetProp ] - size ) > 1 ) {
204
- setSize ( ref . current [ offsetProp ] ) ;
243
+ if ( ref . current && Math . abs ( ref . current [ offsetProp ] - size ) >= 1 ) {
244
+ setSize ( Math . round ( ref . current [ offsetProp ] ) ) ;
205
245
}
206
246
} ,
207
247
onMove ( e ) {
@@ -229,7 +269,7 @@ function ResizablePanel(
229
269
230
270
useEffect ( ( ) => {
231
271
if ( providedSize == null || Math . abs ( providedSize - size ) > 0.5 ) {
232
- onSizeChange ?.( size ) ;
272
+ onSizeChange ?.( Math . round ( size ) ) ;
233
273
}
234
274
} , [ size ] ) ;
235
275
@@ -239,30 +279,39 @@ function ResizablePanel(
239
279
}
240
280
} , [ providedSize ] ) ;
241
281
282
+ const mods = useMemo ( ( ) => {
283
+ return {
284
+ drag : isDragging ,
285
+ horizontal : isHorizontal ,
286
+ disabled : isDisabled ,
287
+ } ;
288
+ } , [ isDragging , isHorizontal , isDisabled ] ) ;
289
+
242
290
return (
243
291
< PanelElement
244
292
ref = { ref }
245
293
data-direction = { direction }
294
+ mods = { mods }
246
295
extra = {
247
296
< Handler
248
- isDisabled = { isDisabled }
297
+ isDisabled = { isDisabled || visualSize == null }
249
298
direction = { direction }
250
299
{ ...moveProps }
251
- mods = { {
252
- drag : isDragging ,
253
- horizontal : isHorizontal ,
254
- disabled : isDisabled ,
255
- } }
300
+ mods = { mods }
256
301
/>
257
302
}
258
303
{ ...mergeProps ( props , {
259
304
style : {
305
+ // We set a current size further via width/min-width/max-width styles to respect size boundaries
260
306
'--size' : `${ size } px` ,
307
+ // We use a separate visual size to paint the handler for smoother experience
308
+ '--visual-size' : `${ visualSize } px` ,
261
309
'--min-size' : typeof minSize === 'number' ? `${ minSize } px` : minSize ,
262
310
'--max-size' : typeof maxSize === 'number' ? `${ maxSize } px` : maxSize ,
263
311
} ,
264
312
innerStyles : {
265
- margin : `5px ${ direction } ` ,
313
+ // The panel inner space compensation for the handler
314
+ margin : `@indent-compensation ${ direction } ` ,
266
315
} ,
267
316
} ) }
268
317
/>
0 commit comments