Skip to content

Commit

Permalink
fix(ResizablePanel): round the size style (#541)
Browse files Browse the repository at this point in the history
  • Loading branch information
tenphi authored Dec 11, 2024
1 parent 09599a3 commit 40c6072
Show file tree
Hide file tree
Showing 4 changed files with 99 additions and 32 deletions.
5 changes: 5 additions & 0 deletions .changeset/proud-kangaroos-switch.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@cube-dev/ui-kit': patch
---

Smoother transition for ResizablePanel.'
5 changes: 5 additions & 0 deletions .changeset/soft-months-beam.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@cube-dev/ui-kit': patch
---

Round the output size style in ResizablePanel.
24 changes: 16 additions & 8 deletions src/components/layout/ResizablePanel.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,30 +13,30 @@ export default {
const TemplateRight: StoryFn<CubeResizablePanelProps> = (args) => (
<Panel isFlex isStretched height="min 30x" fill="#white">
<ResizablePanel {...args} />
<Panel fill="#light"></Panel>
<Panel fill="#purple-04.10"></Panel>
</Panel>
);

const TemplateLeft: StoryFn<CubeResizablePanelProps> = (args) => {
return (
<Panel isFlex isStretched height="min 30x" fill="#white">
<Panel fill="#light"></Panel>
<Panel fill="#purple-04.10"></Panel>
<ResizablePanel {...args} />
</Panel>
);
};

const TemplateBottom: StoryFn<CubeResizablePanelProps> = (args) => (
<Panel isFlex isStretched flow="column" height="min 30x" fill="#white">
<Panel isFlex isStretched flow="column" fill="#white">
<ResizablePanel {...args} />
<Panel fill="#light"></Panel>
<Panel fill="#purple-04.10"></Panel>
</Panel>
);

const TemplateTop: StoryFn<CubeResizablePanelProps> = (args) => {
return (
<Panel isFlex isStretched flow="column" height="min 30x" fill="#white">
<Panel fill="#light"></Panel>
<Panel isFlex isStretched flow="column" fill="#white">
<Panel fill="#purple-04.10"></Panel>
<ResizablePanel {...args} />
</Panel>
);
Expand All @@ -60,7 +60,14 @@ const TemplateControllable: StoryFn<CubeResizablePanelProps> = (args) => {
const GridTemplate: StoryFn<CubeResizablePanelProps> = (args) => (
<Panel isStretched height="min 30x" fill="#white" gridColumns="auto 1fr">
<ResizablePanel size={300} {...args} />
<Panel fill="#light"></Panel>
<Panel fill="#purple-04.10"></Panel>
</Panel>
);

const TemplateDisabled: StoryFn<CubeResizablePanelProps> = (args) => (
<Panel isFlex isStretched height="min 30x" fill="#white">
<ResizablePanel {...args} />
<Panel fill="#purple-04.10"></Panel>
</Panel>
);

Expand All @@ -87,9 +94,10 @@ ResizeTop.args = {
export const Controllable = TemplateControllable.bind({});
Controllable.args = {
direction: 'right',
maxSize: 500,
};

export const Disabled = TemplateRight.bind({});
export const Disabled = TemplateDisabled.bind({});
Disabled.args = {
isDisabled: true,
};
Expand Down
97 changes: 73 additions & 24 deletions src/components/layout/ResizablePanel.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ForwardedRef, forwardRef, useEffect, useState } from 'react';
import { ForwardedRef, forwardRef, useEffect, useMemo, useState } from 'react';
import { useHover, useMove } from 'react-aria';

import { BasePropsWithoutChildren, Styles, tasty } from '../../tasty/index';
Expand All @@ -19,35 +19,45 @@ export interface CubeResizablePanelProps extends CubePanelProps {
}

const HandlerElement = tasty({
qa: 'ResizeHandler',
styles: {
// The real size is slightly bigger than the visual one.
width: {
'': 'auto',
'': 'initial',
horizontal: '9px',
'disabled & horizontal': '1bw',
},
height: {
'': '9px',
horizontal: 'auto',
horizontal: 'initial',
'disabled & !horizontal': '1bw',
},
top: {
'': 'initial',
horizontal: 0,
'[data-direction="top"]': -2,
'': 0,
'[data-direction="top"]': 'initial',
},
bottom: {
'': 'initial',
horizontal: 0,
'[data-direction="bottom"]': -2,
'': 0,
'[data-direction="bottom"]': 'initial',
},
right: {
'': 0,
horizontal: 'initial',
'[data-direction="right"]': -2,
'[data-direction="right"]': 'initial',
},
left: {
'': 0,
horizontal: 'initial',
'[data-direction="left"]': -2,
'[data-direction="left"]': 'initial',
},
// Transform requires a separate visual size property to respect size boundaries
transform: {
'[data-direction="top"]':
'translate(0, (@size-compensation - @visual-size))',
'[data-direction="right"]':
'translate((@visual-size - @size-compensation), 0)',
'[data-direction="bottom"]':
'translate(0, (@visual-size - @size-compensation))',
'[data-direction="left"]':
'translate((@size-compensation - @visual-size), 0)',
},
position: 'absolute',
zIndex: 1,
Expand All @@ -62,20 +72,27 @@ const HandlerElement = tasty({
padding: 0,
boxSizing: 'border-box',
transition: 'theme',
'--size-compensation': {
'': '7px',
disabled: '1bw',
},

Track: {
width: {
'': 'initial',
horizontal: '5px',
'disabled & horizontal': '1px',
},
height: {
'': '5px',
horizontal: 'initial',
'disabled & !horizontal': '1px',
},
position: 'absolute',
inset: {
'': '2px 0',
horizontal: '0 2px',
disabled: '0 0',
},
fill: {
'': '#border-opaque',
Expand All @@ -85,6 +102,10 @@ const HandlerElement = tasty({
},

Drag: {
hide: {
'': false,
disabled: true,
},
width: {
'': '3x',
horizontal: '3px',
Expand Down Expand Up @@ -152,6 +173,11 @@ const PanelElement = tasty(Panel, {
},
placeSelf: 'stretch',
touchAction: 'none',

'--indent-compensation': {
'': '5px',
disabled: '1bw',
},
},
});

Expand All @@ -166,7 +192,7 @@ function ResizablePanel(
size: providedSize,
onSizeChange,
minSize = 200,
maxSize = isControllable ? undefined : 400,
maxSize = isControllable ? undefined : 'min(50%, 400px)',
} = props;

const [isDragging, setIsDragging] = useState(false);
Expand All @@ -189,6 +215,20 @@ function ResizablePanel(
let [size, setSize] = useState<number>(
providedSize != null ? clamp(providedSize) : 200,
);
let [visualSize, setVisualSize] = useState<number | null>(null);

useEffect(() => {
if (ref.current) {
const offsetProp = isHorizontal ? 'offsetWidth' : 'offsetHeight';
const containerSize = ref.current[offsetProp];

if (Math.abs(containerSize - size) < 1 && !isDisabled) {
setVisualSize(size);
} else {
setVisualSize(containerSize);
}
}
}, [size, isDisabled]);

let { moveProps } = useMove({
onMoveStart(e) {
Expand All @@ -200,8 +240,8 @@ function ResizablePanel(

const offsetProp = isHorizontal ? 'offsetWidth' : 'offsetHeight';

if (ref.current && Math.abs(ref.current[offsetProp] - size) > 1) {
setSize(ref.current[offsetProp]);
if (ref.current && Math.abs(ref.current[offsetProp] - size) >= 1) {
setSize(Math.round(ref.current[offsetProp]));
}
},
onMove(e) {
Expand Down Expand Up @@ -229,7 +269,7 @@ function ResizablePanel(

useEffect(() => {
if (providedSize == null || Math.abs(providedSize - size) > 0.5) {
onSizeChange?.(size);
onSizeChange?.(Math.round(size));
}
}, [size]);

Expand All @@ -239,30 +279,39 @@ function ResizablePanel(
}
}, [providedSize]);

const mods = useMemo(() => {
return {
drag: isDragging,
horizontal: isHorizontal,
disabled: isDisabled,
};
}, [isDragging, isHorizontal, isDisabled]);

return (
<PanelElement
ref={ref}
data-direction={direction}
mods={mods}
extra={
<Handler
isDisabled={isDisabled}
isDisabled={isDisabled || visualSize == null}
direction={direction}
{...moveProps}
mods={{
drag: isDragging,
horizontal: isHorizontal,
disabled: isDisabled,
}}
mods={mods}
/>
}
{...mergeProps(props, {
style: {
// We set a current size further via width/min-width/max-width styles to respect size boundaries
'--size': `${size}px`,
// We use a separate visual size to paint the handler for smoother experience
'--visual-size': `${visualSize}px`,
'--min-size': typeof minSize === 'number' ? `${minSize}px` : minSize,
'--max-size': typeof maxSize === 'number' ? `${maxSize}px` : maxSize,
},
innerStyles: {
margin: `5px ${direction}`,
// The panel inner space compensation for the handler
margin: `@indent-compensation ${direction}`,
},
})}
/>
Expand Down

0 comments on commit 40c6072

Please sign in to comment.