Skip to content

Commit

Permalink
feat: Add sash resizer
Browse files Browse the repository at this point in the history
  • Loading branch information
NriotHrreion committed Jan 1, 2024
1 parent 347eba9 commit 691077d
Show file tree
Hide file tree
Showing 5 changed files with 130 additions and 2 deletions.
1 change: 1 addition & 0 deletions public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<div id="dialogs"></div>
<div className="sash-container" id="sashes"></div>
<script src="https://unpkg.com/[email protected]/dist/hidpi-canvas.min.js"></script>
</body>
</html>
97 changes: 97 additions & 0 deletions src/components/Sash.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/* eslint-disable @typescript-eslint/no-unused-expressions */
/* eslint-disable react-hooks/exhaustive-deps */
import React, { useState, useEffect, useRef, useId } from "react";

import Utils from "@/utils/Utils";

interface SashProps {
direction: "vertical" | "horizontal"
defaultValue: number
minValue?: number
maxValue?: number
side: "left" | "right" | "top" | "bottom"
disabled?: boolean
onChange?: (value: number) => void
}

const Sash: React.FC<SashProps> = (props) => {
const [isHovered, setIsHovered] = useState<boolean>(false);
const [isMoving, setIsMoving] = useState<boolean>(false);
const id = useRef(useId());
const timer = useRef<NodeJS.Timeout | null>();

const handleMouseOver = () => {
if(props.disabled || timer.current) return;

timer.current = setTimeout(() => {
setIsHovered(true);
}, 350);
};

const handleMouseOut = () => {
if(props.disabled || isMoving) return;

setIsHovered(false);
clearTimeout(timer.current ?? 0);
timer.current = null;
};

const handleMouseDown = () => {
if(props.disabled) return;

setIsHovered(true);
setIsMoving(true);
};

useEffect(() => {
window.addEventListener("mousemove", async (e: MouseEvent) => {
if(props.disabled || !(await Utils.getCurrentState(setIsMoving))) return;

var newValue = props.direction === "vertical" ? e.clientX : e.clientY;
if(
props.minValue &&
props.minValue >= newValue
) newValue = props.minValue;
if(
props.maxValue &&
props.maxValue <= newValue
) newValue = props.maxValue;

const elem = Utils.getElem("sash--"+ id.current);
elem.style[props.side] = newValue +"px";
// Do a repaint so that the update of page will
// be able to keep up with the update of CSS.
// Thanks to ChatGPT....
elem.style.display = "none";
elem.offsetHeight; // trigger a repaint
elem.style.display = "";
if(props.onChange) props.onChange(newValue);
});
window.addEventListener("mouseup", (e) => {
if(props.disabled || (e.target as HTMLElement).id !== "sash--"+ id.current) setIsHovered(false);
setIsMoving(false);
});
}, []);

useEffect(() => {
if(isHovered) {
document.body.style.cursor = props.direction === "vertical" ? "ew-resize" : "ns-resize";
} else {
document.body.style.cursor = "";
}
}, [isHovered]);

useEffect(() => {
Utils.getElem("sash--"+ id.current).style[props.side] = props.defaultValue +"px";
}, [props.disabled]);

return <div
className={"sash "+ props.direction + (isHovered ? " hover" : "")}
id={"sash--"+ id.current}
style={{ [props.side]: props.defaultValue - 2 }}
onMouseOver={() => handleMouseOver()}
onMouseOut={() => handleMouseOut()}
onMouseDown={() => handleMouseDown()}/>;
}

export default Sash;
13 changes: 12 additions & 1 deletion src/components/sidebar/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import KeepAlive from "react-activation";
import ModeButton from "@/components/sidebar/ModeButton";
import History from "@/components/sidebar/History";
import FunctionList from "@/components/sidebar/FunctionList";
import Sash from "@/components/Sash";

import { Mode, Theme } from "@/types";
import { version } from "@/global";
Expand All @@ -25,6 +26,7 @@ import ProgrammingIcon from "@/icons/programming_mode.svg";
const Sidebar: React.FC = () => {
const { mode } = useContext(MainContext);
const [sidebarOpen, setSidebarOpen] = useState<boolean>(!Utils.isMobile());
const [width, setWidth] = useState<number>(382);
const themeValue = Storage.get().getItem("theme", useThemeDetector());

const handleToggle = (isActive: boolean) => {
Expand Down Expand Up @@ -63,7 +65,7 @@ const Sidebar: React.FC = () => {
}, [sidebarOpen]);

return (
<aside className={"sidebar-container"+ (sidebarOpen ? " open" : "")}>
<aside className={"sidebar-container"+ (sidebarOpen ? " open" : "")} style={{ width }}>
<div className="control-panel-container">
{/* Mobile only */}
{Utils.isMobile() && (
Expand Down Expand Up @@ -110,6 +112,15 @@ const Sidebar: React.FC = () => {
</div>

{layoutSwitch(mode)}

<Sash
direction="vertical"
defaultValue={width}
minValue={315}
maxValue={620}
side="left"
disabled={mode === Mode.GRAPHING}
onChange={(value) => setWidth(value)}/>
</aside>
);
}
Expand Down
16 changes: 16 additions & 0 deletions src/style/layout.less
Original file line number Diff line number Diff line change
Expand Up @@ -87,3 +87,19 @@ kbd {
box-shadow: 1px 1px 10px 2px rgba(0, 0, 0, .1);
z-index: 100;
}

.sash {
position: absolute;
transition: all 100ms ease;
z-index: 1;
@size: 4px;
&.vertical {
width: @size;
}
&.horizontal {
height: @size;
}
&.hover {
background-color: var(--ca-blue1);
}
}
5 changes: 4 additions & 1 deletion src/style/sidebar.less
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
.sidebar-container {
flex: 1;
display: flex;
flex-direction: row;
min-width: 315px;
.control-panel-container {
display: flex;
flex-direction: column;
Expand Down Expand Up @@ -271,4 +271,7 @@
}
}
}
.sash {
height: calc(100% - 26px);
}
}

1 comment on commit 691077d

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.