feat: make panes resizable (#925)

This commit is contained in:
Alex Bäuerle 2024-04-09 17:42:16 -07:00 committed by GitHub
parent 78858c18b5
commit 707ab7b3f8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 120 additions and 17 deletions

View File

@ -3,14 +3,15 @@ import "./App.css";
import CogTooth from "./assets/cog-tooth";
import ChatInterface from "./components/ChatInterface";
import Errors from "./components/Errors";
import LoadMessageModal from "./components/LoadMessageModal";
import { Container, Orientation } from "./components/Resizable";
import SettingModal from "./components/SettingModal";
import Terminal from "./components/Terminal";
import Workspace from "./components/Workspace";
import { fetchMsgTotal } from "./services/session";
import LoadMessageModal from "./components/LoadMessageModal";
import { ResConfigurations, ResFetchMsgTotal } from "./types/ResponseType";
import { fetchConfigurations, saveSettings } from "./services/settingsService";
import Socket from "./services/socket";
import { ResConfigurations, ResFetchMsgTotal } from "./types/ResponseType";
import { getCachedConfig } from "./utils/storage";
interface Props {
@ -78,23 +79,33 @@ function App(): JSX.Element {
};
return (
<div className="flex h-screen bg-neutral-900 text-white">
<LeftNav setSettingOpen={setSettingOpen} />
<div className="flex flex-col grow gap-3 py-3 pr-3">
<div className="flex gap-3 grow min-h-0">
<div className="w-[500px] shrink-0 rounded-xl overflow-hidden border border-neutral-600">
<ChatInterface />
</div>
<div className="flex flex-col flex-1 overflow-hidden rounded-xl bg-neutral-800 border border-neutral-600">
<Workspace />
</div>
</div>
<div className="h-72 shrink-0 bg-neutral-800 rounded-xl border border-neutral-600 flex flex-col">
<Terminal key="terminal" />
</div>
<div className="h-screen w-screen flex flex-col">
<div className="flex grow bg-neutral-900 text-white min-h-0">
<LeftNav setSettingOpen={setSettingOpen} />
<Container
orientation={Orientation.VERTICAL}
className="grow p-3 py-3 pr-3 min-w-0"
initialSize={window.innerHeight - 300}
firstChild={
<Container
orientation={Orientation.HORIZONTAL}
className="grow h-full min-h-0 min-w-0"
initialSize={500}
firstChild={<ChatInterface />}
firstClassName="min-w-[500px] rounded-xl overflow-hidden border border-neutral-600"
secondChild={<Workspace />}
secondClassName="flex flex-col overflow-hidden rounded-xl bg-neutral-800 border border-neutral-600 grow min-w-[500px] min-w-[500px]"
/>
}
firstClassName="min-h-72"
secondChild={<Terminal key="terminal" />}
secondClassName="min-h-72 bg-neutral-800 rounded-xl border border-neutral-600 flex flex-col"
/>
</div>
{/* This div is for the footer that will be added later
<div className="h-8 w-full border-t border-border px-2" />
*/}
<SettingModal isOpen={settingOpen} onClose={handleCloseModal} />
<LoadMessageModal
isOpen={loadMsgWarning}
onClose={() => setLoadMsgWarning(false)}

View File

@ -0,0 +1,92 @@
import React, { useEffect, useRef, useState } from "react";
import { twMerge } from "tailwind-merge";
export enum Orientation {
HORIZONTAL = "horizontal",
VERTICAL = "vertical",
}
type ContainerProps = {
firstChild: React.ReactNode;
firstClassName: string | undefined;
secondChild: React.ReactNode;
secondClassName: string | undefined;
className: string | undefined;
orientation: Orientation;
initialSize: number;
};
export function Container({
firstChild,
firstClassName,
secondChild,
secondClassName,
className,
orientation,
initialSize,
}: ContainerProps): JSX.Element {
const [firstSize, setFirstSize] = useState<number | undefined>(initialSize);
const [dividerPosition, setDividerPosition] = useState<undefined | number>(
undefined,
);
const firstRef = useRef<HTMLDivElement>(null);
useEffect(() => {
if (firstRef.current !== null) {
if (orientation === Orientation.HORIZONTAL) {
firstRef.current.style.width = `${firstSize}px`;
} else {
firstRef.current.style.height = `${firstSize}px`;
}
}
}, [firstSize, orientation]);
const onMouseMove = (e: MouseEvent) => {
e.stopPropagation();
e.preventDefault();
if (firstSize && dividerPosition) {
if (orientation === Orientation.HORIZONTAL) {
const newLeftWidth = firstSize + e.clientX - dividerPosition;
setDividerPosition(e.clientX);
setFirstSize(newLeftWidth);
} else {
const newTopHeight = firstSize + e.clientY - dividerPosition;
setDividerPosition(e.clientY);
setFirstSize(newTopHeight);
}
}
};
const onMouseUp = () => {
document.removeEventListener("mousemove", onMouseMove);
document.removeEventListener("mouseup", onMouseUp);
};
const onMouseDown = (e: React.MouseEvent) => {
e.preventDefault();
e.stopPropagation();
setDividerPosition(
orientation === Orientation.HORIZONTAL ? e.clientX : e.clientY,
);
document.addEventListener("mousemove", onMouseMove);
document.addEventListener("mouseup", onMouseUp);
};
return (
<div
className={twMerge(
`flex ${orientation === Orientation.HORIZONTAL ? "" : "flex-col"}`,
className,
)}
>
<div ref={firstRef} className={firstClassName}>
{firstChild}
</div>
<div
className={`${orientation === Orientation.VERTICAL ? "cursor-ns-resize h-3" : "cursor-ew-resize w-3"} shrink-0`}
onMouseDown={onMouseDown}
/>
<div className={twMerge(secondClassName, "flex-1")}>{secondChild}</div>
</div>
);
}