Skip to content

Commit

Permalink
chore(web): panel resize functionality improvements (#572)
Browse files Browse the repository at this point in the history
Co-authored-by: nina992 <nouralali992@gmail.com>
Co-authored-by: KaWaite <34051327+KaWaite@users.noreply.github.com>
  • Loading branch information
3 people committed Jul 19, 2023
1 parent 0b8919e commit 99f5ad8
Show file tree
Hide file tree
Showing 5 changed files with 101 additions and 54 deletions.
58 changes: 41 additions & 17 deletions web/src/beta/components/Resizable/hooks.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { useCallback, useState, useEffect, useMemo } from "react";

type Direction = "vertical" | "horizontal";
type Gutter = "start" | "end";
export type Direction = "vertical" | "horizontal";
export type Gutter = "start" | "end";

const getPositionFromEvent = (e: React.MouseEvent | React.TouchEvent) => {
const { nativeEvent } = e;
Expand All @@ -18,7 +18,6 @@ const getPositionFromEvent = (e: React.MouseEvent | React.TouchEvent) => {
if (nativeEvent instanceof TouchEvent) {
const touch = nativeEvent.touches[0];
const { clientX: x, clientY: y } = touch;

return { x, y };
}

Expand All @@ -28,32 +27,31 @@ const getPositionFromEvent = (e: React.MouseEvent | React.TouchEvent) => {
const getDelta = (direction: Direction, deltaX: number, deltaY: number) =>
direction === "vertical" ? deltaX : deltaY;

const getSize = (size: number, delta: number, minSize?: number, maxSize?: number) => {
const getSize = (size: number, delta: number, minSize?: number) => {
if (minSize !== undefined && size + delta < minSize) return minSize;
if (maxSize !== undefined && size + delta > maxSize) return maxSize;
return size + delta;
};

export default (
direction: Direction,
gutter: Gutter,
initialSize: number,
minSize?: number,
maxSize?: number,
) => {
export default (direction: Direction, gutter: Gutter, initialSize: number, minSize: number) => {
const [startingSize, setStartingSize] = useState(initialSize);

const [isResizing, setIsResizing] = useState(false);
const [size, setSize] = useState(initialSize);
const [position, setPosition] = useState({ x: 0, y: 0 });
const [minimized, setMinimized] = useState(false);

const [difference, setDifference] = useState(0);

const onResizeStart = useCallback(
(e: React.MouseEvent<HTMLDivElement> | React.TouchEvent<HTMLDivElement>) => {
const position = getPositionFromEvent(e);
if (!position) return;

setStartingSize(size);
setIsResizing(true);
setPosition(position);
},
[],
[size],
);

const onResize = useCallback(
Expand All @@ -65,18 +63,39 @@ export default (
const deltaY = gutter === "start" ? position.y - y : y - position.y;
const delta = getDelta(direction, deltaX, deltaY);

setSize(getSize(size, delta, minSize, maxSize));
const newDiff = difference + delta;

setDifference(newDiff);

setSize(getSize(size, delta, minSize));

if (!minimized && startingSize + newDiff <= minSize / 2) {
setMinimized(true);
}
setPosition({ x, y });
},
[isResizing, position, direction, gutter, size, minSize, maxSize],
[
isResizing,
gutter,
position.x,
position.y,
direction,
size,
startingSize,
minSize,
minimized,
difference,
],
);

const onResizeEnd = useCallback(() => {
if (!isResizing) return;

setIsResizing(false);
setPosition({ x: 0, y: 0 });
}, [isResizing]);
setStartingSize(size);
setDifference(0);
}, [isResizing, size]);

const bindEventListeners = useCallback(() => {
if (typeof window === "undefined") return;
Expand Down Expand Up @@ -113,5 +132,10 @@ export default (
[onResizeStart],
);

return { size, gutterProps };
const handleResetSize = useCallback(() => {
setMinimized(false);
setSize(initialSize);
}, [initialSize]);

return { size: size, gutterProps, minimized, handleResetSize };
};
10 changes: 4 additions & 6 deletions web/src/beta/components/Resizable/index.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,8 @@ export const Vertical: StoryObj<typeof Resizable> = {
args: {
direction: "vertical",
gutter: "end",
size: 400,
minSize: 300,
maxSize: 500,
initialSize: 400,
minSize: 100,
},
render: args => {
return (
Expand All @@ -40,9 +39,8 @@ export const Horizontal: StoryObj<typeof Resizable> = {
args: {
direction: "horizontal",
gutter: "end",
size: 200,
minSize: 100,
maxSize: 300,
initialSize: 350,
minSize: 200,
},
render: args => {
return (
Expand Down
73 changes: 50 additions & 23 deletions web/src/beta/components/Resizable/index.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,25 @@
import { ReactNode } from "react";

import Icon from "@reearth/classic/components/atoms/Icon";
import { styled } from "@reearth/services/theme";

import useHooks from "./hooks";
import useHooks, { type Direction, type Gutter } from "./hooks";

type Props = {
children?: ReactNode;
direction: "vertical" | "horizontal";
gutter: "start" | "end";
size: number;
minSize?: number;
maxSize?: number;
direction: Direction;
gutter: Gutter;
initialSize: number;
minSize: number;
};

const Resizable: React.FC<Props> = ({
direction,
gutter,
size: initialSize,
minSize,
maxSize,
children,
}) => {
const { size, gutterProps } = useHooks(direction, gutter, initialSize, minSize, maxSize);
const Resizable: React.FC<Props> = ({ direction, gutter, minSize, initialSize, children }) => {
const { size, gutterProps, minimized, handleResetSize } = useHooks(
direction,
gutter,
initialSize,
minSize,
);

const showTopGutter = direction === "horizontal" && gutter === "start";
const showRightGutter = direction === "vertical" && gutter === "end";
Expand All @@ -34,23 +32,39 @@ const Resizable: React.FC<Props> = ({
const LeftGutter = showLeftGutter ? <VerticalGutter {...gutterProps} /> : null;

return (
<StyledResizable direction={direction} size={size}>
{TopGutter}
{LeftGutter}
<Wrapper>{children}</Wrapper>
{RightGutter}
{BottomGutter}
</StyledResizable>
<>
{minimized ? (
<MinimizedWrapper direction={direction} onClick={handleResetSize}>
<Icon icon={gutter == "end" ? "arrowRight" : "arrowLeft"} />
</MinimizedWrapper>
) : (
<StyledResizable direction={direction} size={size} minSize={minSize}>
{TopGutter}
{LeftGutter}
<Wrapper>{children}</Wrapper>
{RightGutter}
{BottomGutter}
</StyledResizable>
)}
</>
);
};

const StyledResizable = styled.div<Pick<Props, "direction" | "size">>`
const StyledResizable = styled.div<{
direction: "vertical" | "horizontal";
size: number;
minSize?: number;
}>`
display: flex;
align-items: stretch;
flex-direction: ${({ direction }) => (direction === "vertical" ? "row" : "column")};
width: ${({ direction, size }) => (direction === "horizontal" ? null : `${size}px`)};
height: ${({ direction, size }) => (direction === "vertical" ? null : `${size}px`)};
flex-shrink: 0;
min-width: ${({ direction, minSize }) =>
direction === "vertical" && minSize ? `${minSize}px` : null};
min-height: ${({ direction, minSize }) =>
direction === "horizontal" && minSize ? `${minSize}px` : null};
`;

const Wrapper = styled.div`
Expand All @@ -74,4 +88,17 @@ const VerticalGutter = styled(Gutter)`
cursor: col-resize;
`;

const MinimizedWrapper = styled.div<Pick<Props, "direction">>`
display: flex;
align-items: center;
width: ${({ direction }) => (direction === "horizontal" ? null : `24px`)};
height: ${({ direction }) => (direction === "vertical" ? null : `24px`)};
background: ${({ theme }) => theme.general.bg.weak};
cursor: pointer;
transition: background 0.3s;
:hover {
background: ${({ theme }) => theme.general.bg.veryWeak};
}
`;
export default Resizable;
10 changes: 4 additions & 6 deletions web/src/beta/features/Editor/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,8 @@ const Editor: React.FC<Props> = ({ sceneId, projectId, workspaceId, tab }) => {
<Resizable
direction="vertical"
gutter="end"
size={metrics.propertyMenuMinWidth}
minSize={metrics.propertyMenuMinWidth}
maxSize={metrics.propertyMenuMaxWidth}>
initialSize={metrics.propertyMenuWidth}
minSize={metrics.propertyMenuMinWidth}>
{leftPanel}
</Resizable>
)}
Expand All @@ -51,9 +50,8 @@ const Editor: React.FC<Props> = ({ sceneId, projectId, workspaceId, tab }) => {
<Resizable
direction="vertical"
gutter="start"
size={metrics.propertyMenuMinWidth}
minSize={metrics.propertyMenuMinWidth}
maxSize={metrics.propertyMenuMaxWidth}>
initialSize={metrics.propertyMenuWidth}
minSize={metrics.propertyMenuMinWidth}>
{rightPanel}
</Resizable>
)}
Expand Down
4 changes: 2 additions & 2 deletions web/src/services/theme/reearthTheme/common/metrics.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
const metrics = {
propertyMenuMinWidth: 272,
propertyMenuMaxWidth: 336,
propertyMenuWidth: 308,
propertyMenuMinWidth: 200,
};

export const metricsSizes = {
Expand Down

0 comments on commit 99f5ad8

Please sign in to comment.