Skip to content

Commit

Permalink
allow resizing of left sidebar (#244)
Browse files Browse the repository at this point in the history
* wip

* integrate original sidebar content

* ResizableSidebar component

* trigger toggleCollapse

* remove debugging code

* minor refactor. disable text select on mousemove

* replace icons with fontawesome icons. fix alignment issues

* fix session view width when tabs overflow

* prevent index and icon from shifting when resizing

* snap effect

* minor refactor

* apply collapsed mode to sidebar contents

* change default width to 240px

* backend implementation

* fix wrong subcmd

* save collapsed state

* retore sidebar state on reload/launch

* use collapse data form db on first load. use previously saved width on expand.

* persist width as well collapse state

* various fixes and improvements

* bind methods

* refactor

* more refactor

* fix minor bug

* fix merge issues

* various fixes

* refactor

* fixes

* fix issues

* fix all issues

* resolve undefind tempWidth

* fix toggleCollapsed

* use Promise in stopResizing method

* use tempCollapsed to for real time toggling between logos

* minor method name change

* refactor

* remove debugging code

* fix conflict

* fix setting collapsed state via CLI

* minor refactor

* remove debugging code

* create setTempWidthAndTempCollapsed method

* handle invalid width set via cli

* refactor: setbycli not actually needed

* remove unused code
  • Loading branch information
adred authored Jan 31, 2024
1 parent 40757fa commit 37ab1bc
Show file tree
Hide file tree
Showing 11 changed files with 471 additions and 129 deletions.
16 changes: 12 additions & 4 deletions src/app/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ type OV<V> = mobx.IObservableValue<V>;
@mobxReact.observer
class App extends React.Component<{}, {}> {
dcWait: OV<boolean> = mobx.observable.box(false, { name: "dcWait" });
mainContentRef: React.RefObject<HTMLDivElement> = React.createRef();

constructor(props: any) {
super(props);
Expand Down Expand Up @@ -75,15 +76,22 @@ class App extends React.Component<{}, {}> {
let hasClientStop = GlobalModel.getHasClientStop();
let dcWait = this.dcWait.get();
let platform = GlobalModel.getPlatform();
let clientData = GlobalModel.clientData.get();

// Previously, this is done in sidebar.tsx but it causes flicker when clientData is null cos screen-view shifts around.
// Doing it here fixes the flicker cos app is not rendered until clientData is populated.
if (clientData == null) {
return null;
}

if (disconnected || hasClientStop) {
if (!dcWait) {
setTimeout(() => this.updateDcWait(true), 1500);
}
return (
<div id="main" className={"platform-" + platform} onContextMenu={this.handleContextMenu}>
<div className="main-content">
<MainSideBar />
<div ref={this.mainContentRef} className="main-content">
<MainSideBar parentRef={this.mainContentRef} clientData={clientData} />
<div className="session-view" />
</div>
<If condition={dcWait}>
Expand All @@ -102,8 +110,8 @@ class App extends React.Component<{}, {}> {
}
return (
<div id="main" className={"platform-" + platform} onContextMenu={this.handleContextMenu}>
<div className="main-content">
<MainSideBar />
<div ref={this.mainContentRef} className="main-content">
<MainSideBar parentRef={this.mainContentRef} clientData={clientData} />
<ErrorBoundary>
<PluginsView />
<WorkspaceView />
Expand Down
169 changes: 167 additions & 2 deletions src/app/common/common.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,12 @@ import ReactMarkdown from "react-markdown";
import remarkGfm from "remark-gfm";
import cn from "classnames";
import { If } from "tsx-control-statements/components";
import { RemoteType, StatusIndicatorLevel } from "../../types/types";
import { RemoteType } from "../../types/types";
import ReactDOM from "react-dom";
import { GlobalModel } from "../../model/model";
import { GlobalModel, GlobalCommandRunner } from "../../model/model";
import * as appconst from "../appconst";
import { checkKeyPressed, adaptFromReactOrNativeKeyEvent } from "../../util/keyutil";
import { MagicLayout } from "../magiclayout";

import { ReactComponent as CheckIcon } from "../assets/icons/line/check.svg";
import { ReactComponent as CopyIcon } from "../assets/icons/history/copy.svg";
Expand Down Expand Up @@ -1265,6 +1266,169 @@ In order to use Wave's advanced features like unified history and persistent ses
});
}

interface ResizableSidebarProps {
parentRef: React.RefObject<HTMLElement>;
position: "left" | "right";
enableSnap?: boolean;
className?: string;
children?: (toggleCollapsed: () => void) => React.ReactNode;
toggleCollapse?: () => void;
}

@mobxReact.observer
class ResizableSidebar extends React.Component<ResizableSidebarProps> {
resizeStartWidth: number = 0;
startX: number = 0;
prevDelta: number = 0;
prevDragDirection: string = null;
disposeReaction: any;

@boundMethod
startResizing(event: React.MouseEvent<HTMLDivElement>) {
event.preventDefault();

let { parentRef, position } = this.props;
let parentRect = parentRef.current?.getBoundingClientRect();

if (!parentRect) return;

if (position === "right") {
this.startX = parentRect.right - event.clientX;
} else {
this.startX = event.clientX - parentRect.left;
}

this.resizeStartWidth = GlobalModel.mainSidebarModel.getWidth();
document.addEventListener("mousemove", this.onMouseMove);
document.addEventListener("mouseup", this.stopResizing);

document.body.style.cursor = "col-resize";
mobx.action(() => {
GlobalModel.mainSidebarModel.isDragging.set(true);
})();
}

@boundMethod
onMouseMove(event: MouseEvent) {
event.preventDefault();

let { parentRef, enableSnap, position } = this.props;
let parentRect = parentRef.current?.getBoundingClientRect();
let mainSidebarModel = GlobalModel.mainSidebarModel;

if (!mainSidebarModel.isDragging.get() || !parentRect) return;

let delta, newWidth;

if (position === "right") {
delta = parentRect.right - event.clientX - this.startX;
} else {
delta = event.clientX - parentRect.left - this.startX;
}

newWidth = this.resizeStartWidth + delta;

if (enableSnap) {
let minWidth = MagicLayout.MainSidebarMinWidth;
let snapPoint = minWidth + MagicLayout.MainSidebarSnapThreshold;
let dragResistance = MagicLayout.MainSidebarDragResistance;
let dragDirection;

if (delta - this.prevDelta > 0) {
dragDirection = "+";
} else if (delta - this.prevDelta == 0) {
if (this.prevDragDirection == "+") {
dragDirection = "+";
} else {
dragDirection = "-";
}
} else {
dragDirection = "-";
}

this.prevDelta = delta;
this.prevDragDirection = dragDirection;

if (newWidth - dragResistance > minWidth && newWidth < snapPoint && dragDirection == "+") {
newWidth = snapPoint;
mainSidebarModel.setTempWidthAndTempCollapsed(newWidth, false);
} else if (newWidth + dragResistance < snapPoint && dragDirection == "-") {
newWidth = minWidth;
mainSidebarModel.setTempWidthAndTempCollapsed(newWidth, true);
} else if (newWidth > snapPoint) {
mainSidebarModel.setTempWidthAndTempCollapsed(newWidth, false);
}
} else {
if (newWidth <= MagicLayout.MainSidebarMinWidth) {
mainSidebarModel.setTempWidthAndTempCollapsed(newWidth, true);
} else {
mainSidebarModel.setTempWidthAndTempCollapsed(newWidth, false);
}
}
}

@boundMethod
stopResizing() {
let mainSidebarModel = GlobalModel.mainSidebarModel;

GlobalCommandRunner.clientSetSidebar(
mainSidebarModel.tempWidth.get(),
mainSidebarModel.tempCollapsed.get()
).finally(() => {
mobx.action(() => {
mainSidebarModel.isDragging.set(false);
})();
});

document.removeEventListener("mousemove", this.onMouseMove);
document.removeEventListener("mouseup", this.stopResizing);
document.body.style.cursor = "";
}

@boundMethod
toggleCollapsed() {
let mainSidebarModel = GlobalModel.mainSidebarModel;

let tempCollapsed = mainSidebarModel.getCollapsed();
let width = MagicLayout.MainSidebarDefaultWidth;
let newWidth;
if (tempCollapsed) {
newWidth = width;
} else {
newWidth = MagicLayout.MainSidebarMinWidth;
}

mainSidebarModel.setTempWidthAndTempCollapsed(newWidth, !tempCollapsed);
GlobalCommandRunner.clientSetSidebar(newWidth, !tempCollapsed);
}

render() {
let { className, children } = this.props;
let mainSidebarModel = GlobalModel.mainSidebarModel;
let width = mainSidebarModel.getWidth();
let isCollapsed = mainSidebarModel.getCollapsed();

return (
<div className={cn("sidebar", className, { collapsed: isCollapsed })} style={{ width }}>
<div className="sidebar-content">{children(this.toggleCollapsed)}</div>
<div
className="sidebar-handle"
style={{
position: "absolute",
top: 0,
[this.props.position === "left" ? "right" : "left"]: 0,
bottom: 0,
width: "5px",
cursor: "col-resize",
}}
onMouseDown={this.startResizing}
onDoubleClick={this.toggleCollapsed}
></div>
</div>
);
}
}

export {
CmdStrCode,
Toggle,
Expand All @@ -1286,5 +1450,6 @@ export {
LinkButton,
Status,
Modal,
ResizableSidebar,
ShowWaveShellInstallPrompt,
};
6 changes: 6 additions & 0 deletions src/app/magiclayout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ let MagicLayout = {
ScreenSidebarWidthPadding: 5,
ScreenSidebarMinWidth: 200,
ScreenSidebarHeaderHeight: 28,

MainSidebarMinWidth: 75,
MainSidebarMaxWidth: 300,
MainSidebarSnapThreshold: 90,
MainSidebarDragResistance: 50,
MainSidebarDefaultWidth: 240,
};

let m = MagicLayout;
Expand Down
16 changes: 9 additions & 7 deletions src/app/sidebar/sidebar.less
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,13 @@

.main-sidebar {
padding: 0;
min-width: 20rem;
max-width: 20rem;
display: flex;
flex-direction: column;
position: relative;
font-size: 12.5px;
line-height: 20px;
backdrop-filter: blur(4px);
z-index: 20;

.title-bar-drag {
-webkit-app-region: drag;
Expand All @@ -24,7 +23,6 @@
&.collapsed {
width: 6em;
min-width: 6em;

.arrow-container,
.collapse-button {
transform: rotate(180deg);
Expand All @@ -34,7 +32,7 @@
margin-top: 26px;

.top,
.workspaces-item,
.workspaces,
.middle,
.bottom,
.separator {
Expand All @@ -50,7 +48,7 @@
justify-content: center;
align-items: center;

.logo-container img {
.logo-container {
width: 45px;
}

Expand Down Expand Up @@ -86,10 +84,14 @@
display: flex;
flex-direction: row;

.logo-container {
flex-shrink: 0;
width: 100px;
}

.spacer {
flex-grow: 1;
}

img {
width: 100px;
}
Expand Down Expand Up @@ -150,7 +152,6 @@
margin-left: 6px;
border-radius: 4px;
opacity: 1;
transition: opacity 0.1s ease-in-out, visibility 0.1s step-end;
width: inherit;
max-width: inherit;
min-width: inherit;
Expand All @@ -177,6 +178,7 @@
float: right;
margin-right: 6px;
letter-spacing: 6px;
margin-left: auto;
}
&:hover {
:not(.disabled) .hotkey {
Expand Down
Loading

0 comments on commit 37ab1bc

Please sign in to comment.