Skip to content

Commit cf1976c

Browse files
committed
fix(ResizablePanel): add new api and stories
1 parent 44ad5b9 commit cf1976c

File tree

2 files changed

+94
-8
lines changed

2 files changed

+94
-8
lines changed

src/components/layout/ResizablePanel.stories.tsx

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { Meta, StoryFn } from '@storybook/react';
2+
import { useState } from 'react';
23

34
import { Panel } from './Panel';
45
import { ResizablePanel, ResizablePanelProps } from './ResizablePanel';
@@ -41,6 +42,21 @@ const TemplateTop: StoryFn<ResizablePanelProps> = (args) => {
4142
);
4243
};
4344

45+
const TemplateControllable: StoryFn<ResizablePanelProps> = (args) => {
46+
const [size, setSize] = useState(200);
47+
48+
return (
49+
<ResizablePanel
50+
size={size}
51+
flow="column"
52+
height="min 30x"
53+
fill="#light"
54+
onSizeChange={(size) => setSize(Math.min(500, Math.max(100, size)))}
55+
{...args}
56+
></ResizablePanel>
57+
);
58+
};
59+
4460
export const ResizeRight = TemplateRight.bind({});
4561
ResizeRight.args = {
4662
direction: 'right',
@@ -60,3 +76,13 @@ export const ResizeTop = TemplateTop.bind({});
6076
ResizeTop.args = {
6177
direction: 'top',
6278
};
79+
80+
export const Controllable = TemplateControllable.bind({});
81+
Controllable.args = {
82+
direction: 'right',
83+
};
84+
85+
export const Disabled = TemplateRight.bind({});
86+
Disabled.args = {
87+
isDisabled: true,
88+
};

src/components/layout/ResizablePanel.tsx

Lines changed: 68 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
1-
import { ForwardedRef, forwardRef, useContext, useState } from 'react';
1+
import {
2+
ForwardedRef,
3+
forwardRef,
4+
useContext,
5+
useEffect,
6+
useState,
7+
} from 'react';
28
import { useHover, useMove } from 'react-aria';
39

410
import { useWarn } from '../../_internal/index';
@@ -12,8 +18,11 @@ type Direction = 'top' | 'right' | 'bottom' | 'left';
1218
export interface ResizablePanelProps extends CubePanelProps {
1319
handlerStyles?: Styles;
1420
direction: Direction;
21+
size?: number;
22+
onSizeChange?: (size: number) => void;
1523
minSize?: string | number;
1624
maxSize?: string | number;
25+
isDisabled?: boolean;
1726
}
1827

1928
const HandlerElement = tasty({
@@ -49,7 +58,10 @@ const HandlerElement = tasty({
4958
},
5059
position: 'absolute',
5160
zIndex: 1,
52-
cursor: 'col-resize',
61+
cursor: {
62+
'': 'col-resize',
63+
disabled: 'not-allowed',
64+
},
5365
fill: {
5466
'': '#clear',
5567
drag: '#purple-02',
@@ -74,7 +86,7 @@ const HandlerElement = tasty({
7486
},
7587
fill: {
7688
'': '#border-opaque',
77-
'hovered | drag': '#purple-03',
89+
'(hovered | drag) & !disabled': '#purple-03',
7890
},
7991
transition: 'theme',
8092
},
@@ -92,6 +104,7 @@ const HandlerElement = tasty({
92104
fill: {
93105
'': '#dark-03',
94106
'hovered | drag': '#dark-02',
107+
disabled: '#dark-04',
95108
},
96109
inset: {
97110
'': '3px 50% auto auto',
@@ -108,7 +121,7 @@ interface HandlerProps extends BasePropsWithoutChildren {
108121
}
109122

110123
const Handler = (props: HandlerProps) => {
111-
const { direction = 'right' } = props;
124+
const { direction = 'right', isDisabled } = props;
112125
const { hoverProps, isHovered } = useHover({});
113126
const isHorizontal = direction === 'left' || direction === 'right';
114127

@@ -120,6 +133,7 @@ const Handler = (props: HandlerProps) => {
120133
mods: {
121134
hovered: isHovered,
122135
horizontal: isHorizontal,
136+
disabled: isDisabled,
123137
},
124138
'data-direction': direction,
125139
},
@@ -162,16 +176,43 @@ function ResizablePanel(
162176
],
163177
});
164178

165-
const { direction = 'right', minSize = '15%', maxSize = '35%' } = props;
179+
const isControllable = typeof props.size === 'number';
180+
const {
181+
isDisabled,
182+
direction = 'right',
183+
size: providedSize,
184+
onSizeChange,
185+
minSize = 200,
186+
maxSize = isControllable ? undefined : 400,
187+
} = props;
188+
166189
const [isDragging, setIsDragging] = useState(false);
167190
const isHorizontal = direction === 'left' || direction === 'right';
168191

169192
ref = useCombinedRefs(ref);
170193

171-
let [size, setSize] = useState<number>(200);
194+
function clamp(size: number) {
195+
if (typeof maxSize === 'number') {
196+
size = Math.min(maxSize, size);
197+
}
198+
199+
if (typeof minSize === 'number' || !minSize) {
200+
size = Math.max((minSize as number) || 0, size);
201+
}
202+
203+
return Math.max(size, 0);
204+
}
205+
206+
let [size, setSize] = useState<number>(
207+
providedSize != null ? clamp(providedSize) : 200,
208+
);
172209

173210
let { moveProps } = useMove({
174211
onMoveStart(e) {
212+
if (isDisabled) {
213+
return;
214+
}
215+
175216
setIsDragging(true);
176217

177218
const offsetProp = isHorizontal ? 'offsetWidth' : 'offsetHeight';
@@ -182,6 +223,10 @@ function ResizablePanel(
182223
},
183224
onMove(e) {
184225
setSize((size) => {
226+
if (isDisabled) {
227+
return;
228+
}
229+
185230
if (e.pointerType === 'keyboard') {
186231
return size;
187232
}
@@ -190,23 +235,38 @@ function ResizablePanel(
190235
? e.deltaX * (direction === 'right' ? 1 : -1)
191236
: e.deltaY * (direction === 'bottom' ? 1 : -1);
192237

193-
return size;
238+
return clamp(size);
194239
});
195240
},
196241
onMoveEnd(e) {
197242
setIsDragging(false);
198243
},
199244
});
200245

246+
useEffect(() => {
247+
onSizeChange?.(size);
248+
}, [size]);
249+
250+
useEffect(() => {
251+
if (providedSize) {
252+
setSize(providedSize);
253+
}
254+
}, [providedSize]);
255+
201256
return (
202257
<PanelElement
203258
ref={ref}
204259
data-direction={direction}
205260
extra={
206261
<Handler
262+
isDisabled={isDisabled}
207263
direction={direction}
208264
{...moveProps}
209-
mods={{ drag: isDragging, horizontal: isHorizontal }}
265+
mods={{
266+
drag: isDragging,
267+
horizontal: isHorizontal,
268+
disabled: isDisabled,
269+
}}
210270
/>
211271
}
212272
{...mergeProps(props, {

0 commit comments

Comments
 (0)