feat: add draggable left sidebar resizing

This commit is contained in:
barkstone2 2026-02-08 21:59:45 +09:00 committed by barkstone2
parent 4f1cec17e9
commit 7a6fae8f6c
3 changed files with 57 additions and 11 deletions

View file

@ -1,10 +1,11 @@
import {CSSProperties} from "react";
import {CSSProperties, MouseEvent} from "react";
interface WorkspaceLeafResizeHandleProps {
style?: CSSProperties;
onMouseDown?: (event: MouseEvent<HTMLHRElement>) => void;
}
export function WorkspaceLeafResizeHandle({style}: WorkspaceLeafResizeHandleProps) {
export function WorkspaceLeafResizeHandle({style, onMouseDown}: WorkspaceLeafResizeHandleProps) {
return (
<hr className="workspace-leaf-resize-handle" style={style}/>
<hr className="workspace-leaf-resize-handle" style={style} onMouseDown={onMouseDown}/>
)
}
}

View file

@ -1,4 +1,4 @@
import {ReactNode, useEffect, useState} from "react";
import {MouseEvent as ReactMouseEvent, ReactNode, useCallback, useEffect, useState} from "react";
import {WorkspaceSplitContainer} from "./WorkspaceSplitContainer";
import TreeContainer from "../components/TreeContainer";
import {WorkspaceTabs} from "../components/tab-header/WorkspaceTabs";
@ -33,6 +33,9 @@ export function resolveTocHeaders(
const leftSideDockOpenedClassName = 'is-left-sidedock-open';
const rightSideDockOpenedClassName = 'is-right-sidedock-open';
const defaultLeftSideDockWidth = 300;
const minLeftSideDockWidth = 220;
const maxLeftSideDockWidth = 560;
export function WorkspaceContainer({children}: {children: ReactNode}) {
const {'*': filePath } = useParams();
@ -54,6 +57,7 @@ export function WorkspaceContainer({children}: {children: ReactNode}) {
const [isLeftSideDockOpened, setIsLeftSideDockOpened] = useState(!isMobile);
const [isRightSideDockOpened, setIsRightSideDockOpened] = useState(!isMobile);
const [leftSideDockWidth, setLeftSideDockWidth] = useState(defaultLeftSideDockWidth);
useEffect(() => {
setIsLeftSideDockOpened(!isMobile)
@ -69,11 +73,39 @@ export function WorkspaceContainer({children}: {children: ReactNode}) {
workspaceClassName += ' ' + rightSideDockOpenedClassName;
}
const handleLeftSidebarResizeMouseDown = useCallback((event: ReactMouseEvent<HTMLHRElement>) => {
if (isMobile || !isLeftSideDockOpened) {
return;
}
event.preventDefault();
const startX = event.clientX;
const startWidth = leftSideDockWidth;
const handleMouseMove = (moveEvent: MouseEvent) => {
const nextWidth = Math.min(
maxLeftSideDockWidth,
Math.max(minLeftSideDockWidth, startWidth + (moveEvent.clientX - startX)),
);
setLeftSideDockWidth(nextWidth);
};
const handleMouseUp = () => {
window.removeEventListener('mousemove', handleMouseMove);
window.removeEventListener('mouseup', handleMouseUp);
};
window.addEventListener('mousemove', handleMouseMove);
window.addEventListener('mouseup', handleMouseUp);
}, [isLeftSideDockOpened, isMobile, leftSideDockWidth]);
return (
<div className={workspaceClassName}>
<WorkspaceSplitContainer modOrientation={'mod-horizontal'}
isSideDock={true}
isSideDockCollapse={!isLeftSideDockOpened}
width={leftSideDockWidth}
onResizeMouseDown={handleLeftSidebarResizeMouseDown}
modSplitDirection={'mod-left-split'}>
<WorkspaceTabs spaceDirection={'mod-top-left-space'}>
<WorkspaceTabHeaderContainer

View file

@ -1,24 +1,37 @@
import {WorkspaceLeafResizeHandle} from "../components/WorkspaceLeafResizeHandle";
import {CSSProperties} from "react";
import {CSSProperties, MouseEvent, ReactNode} from "react";
interface WorkspaceSplitContainerProps {
modOrientation: "mod-vertical" | "mod-horizontal";
isSideDock: boolean;
modSplitDirection?: "mod-left-split" | "mod-right-split";
isSideDockCollapse?: boolean;
children?: React.ReactNode;
width?: number;
onResizeMouseDown?: (event: MouseEvent<HTMLHRElement>) => void;
children?: ReactNode;
}
export function WorkspaceSplitContainer({modOrientation, isSideDock, modSplitDirection, isSideDockCollapse, children}: WorkspaceSplitContainerProps) {
export function WorkspaceSplitContainer({
modOrientation,
isSideDock,
modSplitDirection,
isSideDockCollapse,
width,
onResizeMouseDown,
children,
}: WorkspaceSplitContainerProps) {
const collapsedClass = isSideDockCollapse && "is-sidedock-collapsed";
let styles: CSSProperties = isSideDock ? {width: '300px'} : {}
let styles: CSSProperties = isSideDock ? {width: `${width ?? 300}px`} : {}
styles = isSideDockCollapse ? {width: 0, display: 'none'} : styles;
return (
<div
className={`workspace-split ${modOrientation} ${isSideDock ? 'mod-sidedock' : 'mod-root'} ${modSplitDirection} ${collapsedClass}`}
style={styles}>
<WorkspaceLeafResizeHandle style={(isSideDock && !isSideDockCollapse) ? {opacity: 1} : {opacity: 0}}/>
<WorkspaceLeafResizeHandle
style={(isSideDock && !isSideDockCollapse) ? {opacity: 1} : {opacity: 0}}
onMouseDown={onResizeMouseDown}
/>
{children}
</div>
)
}
}