mirror of
https://github.com/wavetermdev/backup.git
synced 2026-08-05 13:57:07 -07:00
Rght sidebar (#461)
* init * model * pass model * increase snap threshold * move saving of state to model. backend implementation * fix wrong clientdata prop * right header space for right sidebar toggles * button component refactor * set default classname to primary * remove debugging code * hide trigger for now * disable right sidebar in production (only show on dev for now)
This commit is contained in:
@@ -44,6 +44,10 @@
|
|||||||
--floating-logo-width: 40px;
|
--floating-logo-width: 40px;
|
||||||
--floating-logo-height: var(--screentabs-height);
|
--floating-logo-height: var(--screentabs-height);
|
||||||
|
|
||||||
|
/* right sidebar triggers */
|
||||||
|
--floating-right-sidebar-triggers-width-darwin: 50px;
|
||||||
|
--floating-right-sidebar-triggers-width: 40px;
|
||||||
|
|
||||||
/* global colors */
|
/* global colors */
|
||||||
--app-bg-color: black;
|
--app-bg-color: black;
|
||||||
--app-accent-color: rgb(88, 193, 66);
|
--app-accent-color: rgb(88, 193, 66);
|
||||||
|
|||||||
@@ -302,6 +302,25 @@ a.a-block {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.right-sidebar-triggers {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 25;
|
||||||
|
right: 0px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
pointer-events: none;
|
||||||
|
height: 38px;
|
||||||
|
padding: 0 5px;
|
||||||
|
|
||||||
|
.right-sidebar-trigger {
|
||||||
|
cursor: pointer;
|
||||||
|
user-select: none;
|
||||||
|
-webkit-app-region: no-drag;
|
||||||
|
pointer-events: all;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.copied-indicator {
|
.copied-indicator {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
|
|||||||
+35
-11
@@ -16,10 +16,12 @@ import { BookmarksView } from "./bookmarks/bookmarks";
|
|||||||
import { HistoryView } from "./history/history";
|
import { HistoryView } from "./history/history";
|
||||||
import { ConnectionsView } from "./connections/connections";
|
import { ConnectionsView } from "./connections/connections";
|
||||||
import { ClientSettingsView } from "./clientsettings/clientsettings";
|
import { ClientSettingsView } from "./clientsettings/clientsettings";
|
||||||
import { MainSideBar } from "./sidebar/sidebar";
|
import { MainSideBar } from "./sidebar/main";
|
||||||
import { DisconnectedModal, ClientStopModal } from "./common/modals";
|
import { RightSideBar } from "./sidebar/right";
|
||||||
import { ModalsProvider } from "./common/modals/provider";
|
import { DisconnectedModal, ClientStopModal } from "@/modals";
|
||||||
import { ErrorBoundary } from "./common/error/errorboundary";
|
import { ModalsProvider } from "@/modals/provider";
|
||||||
|
import { Button } from "@/elements";
|
||||||
|
import { ErrorBoundary } from "@/common/error/errorboundary";
|
||||||
import cn from "classnames";
|
import cn from "classnames";
|
||||||
import "./app.less";
|
import "./app.less";
|
||||||
|
|
||||||
@@ -65,9 +67,17 @@ class App extends React.Component<{}, {}> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@boundMethod
|
@boundMethod
|
||||||
openSidebar() {
|
openMainSidebar() {
|
||||||
const width = GlobalModel.mainSidebarModel.getWidth(true);
|
const mainSidebarModel = GlobalModel.mainSidebarModel;
|
||||||
GlobalCommandRunner.clientSetSidebar(width, false);
|
const width = mainSidebarModel.getWidth(true);
|
||||||
|
mainSidebarModel.saveState(width, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@boundMethod
|
||||||
|
openRightSidebar() {
|
||||||
|
const rightSidebarModel = GlobalModel.rightSidebarModel;
|
||||||
|
const width = rightSidebarModel.getWidth(true);
|
||||||
|
rightSidebarModel.saveState(width, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
@@ -110,23 +120,36 @@ class App extends React.Component<{}, {}> {
|
|||||||
}
|
}
|
||||||
// used to force a full reload of the application
|
// used to force a full reload of the application
|
||||||
const renderVersion = GlobalModel.renderVersion.get();
|
const renderVersion = GlobalModel.renderVersion.get();
|
||||||
const sidebarCollapsed = GlobalModel.mainSidebarModel.getCollapsed();
|
const mainSidebarCollapsed = GlobalModel.mainSidebarModel.getCollapsed();
|
||||||
|
const rightSidebarCollapsed = GlobalModel.rightSidebarModel.getCollapsed();
|
||||||
|
const activeMainView = GlobalModel.activeMainView.get();
|
||||||
const lightDarkClass = GlobalModel.isThemeDark() ? "is-dark" : "is-light";
|
const lightDarkClass = GlobalModel.isThemeDark() ? "is-dark" : "is-light";
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={"version-" + renderVersion}
|
key={"version-" + renderVersion}
|
||||||
id="main"
|
id="main"
|
||||||
className={cn("platform-" + platform, { "sidebar-collapsed": sidebarCollapsed }, lightDarkClass)}
|
className={cn(
|
||||||
|
"platform-" + platform,
|
||||||
|
{ "mainsidebar-collapsed": mainSidebarCollapsed, "rightsidebar-collapsed": rightSidebarCollapsed },
|
||||||
|
lightDarkClass
|
||||||
|
)}
|
||||||
onContextMenu={this.handleContextMenu}
|
onContextMenu={this.handleContextMenu}
|
||||||
>
|
>
|
||||||
<If condition={sidebarCollapsed}>
|
<If condition={mainSidebarCollapsed}>
|
||||||
<div key="logo-button" className="logo-button-container">
|
<div key="logo-button" className="logo-button-container">
|
||||||
<div className="logo-button-spacer" />
|
<div className="logo-button-spacer" />
|
||||||
<div className="logo-button" onClick={this.openSidebar}>
|
<div className="logo-button" onClick={this.openMainSidebar}>
|
||||||
<img src="public/logos/wave-logo.png" alt="logo" />
|
<img src="public/logos/wave-logo.png" alt="logo" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</If>
|
</If>
|
||||||
|
<If condition={GlobalModel.isDev && rightSidebarCollapsed && activeMainView == "session"}>
|
||||||
|
<div className="right-sidebar-triggers">
|
||||||
|
<Button className="secondary ghost right-sidebar-trigger" onClick={this.openRightSidebar}>
|
||||||
|
<i className="fa-sharp fa-regular fa-lightbulb"></i>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</If>
|
||||||
<div ref={this.mainContentRef} className="main-content">
|
<div ref={this.mainContentRef} className="main-content">
|
||||||
<MainSideBar parentRef={this.mainContentRef} clientData={clientData} />
|
<MainSideBar parentRef={this.mainContentRef} clientData={clientData} />
|
||||||
<ErrorBoundary>
|
<ErrorBoundary>
|
||||||
@@ -137,6 +160,7 @@ class App extends React.Component<{}, {}> {
|
|||||||
<ConnectionsView model={remotesModel} />
|
<ConnectionsView model={remotesModel} />
|
||||||
<ClientSettingsView model={remotesModel} />
|
<ClientSettingsView model={remotesModel} />
|
||||||
</ErrorBoundary>
|
</ErrorBoundary>
|
||||||
|
<RightSideBar parentRef={this.mainContentRef} clientData={clientData} />
|
||||||
</div>
|
</div>
|
||||||
<ModalsProvider />
|
<ModalsProvider />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
font: inherit;
|
font: inherit;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
outline: inherit;
|
outline: inherit;
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
padding: 6px 16px;
|
padding: 6px 16px;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -15,7 +14,8 @@
|
|||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
|
|
||||||
&.primary {
|
&.primary {
|
||||||
background: none;
|
color: var(--form-element-text-color);
|
||||||
|
background: var(--form-element-primary-color);
|
||||||
|
|
||||||
i {
|
i {
|
||||||
fill: var(--form-element-primary-color);
|
fill: var(--form-element-primary-color);
|
||||||
@@ -31,11 +31,12 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&.outlined {
|
&.outlined {
|
||||||
|
background: none;
|
||||||
border: 1px solid var(--form-element-primary-color);
|
border: 1px solid var(--form-element-primary-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
&.ghost {
|
&.ghost {
|
||||||
// Styles for .ghost are already defined above
|
background: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
@@ -46,6 +47,9 @@
|
|||||||
&.secondary {
|
&.secondary {
|
||||||
color: var(--form-element-text-color);
|
color: var(--form-element-text-color);
|
||||||
background: none;
|
background: none;
|
||||||
|
color: var(--form-element-text-color);
|
||||||
|
background: var(--form-element-secondary-color);
|
||||||
|
box-shadow: none;
|
||||||
|
|
||||||
&.solid {
|
&.solid {
|
||||||
color: var(--form-element-text-color);
|
color: var(--form-element-text-color);
|
||||||
@@ -54,10 +58,12 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&.outlined {
|
&.outlined {
|
||||||
|
background: none;
|
||||||
border: 1px solid var(--form-element-secondary-color);
|
border: 1px solid var(--form-element-secondary-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
&.ghost {
|
&.ghost {
|
||||||
|
background: none;
|
||||||
padding: 6px 10px;
|
padding: 6px 10px;
|
||||||
|
|
||||||
i {
|
i {
|
||||||
|
|||||||
@@ -1,24 +1,15 @@
|
|||||||
// Copyright 2023, Command Line Inc.
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { boundMethod } from "autobind-decorator";
|
import { boundMethod } from "autobind-decorator";
|
||||||
import cn from "classnames";
|
import cn from "classnames";
|
||||||
|
|
||||||
import "./button.less";
|
import "./button.less";
|
||||||
|
|
||||||
type ButtonVariantType = "outlined" | "solid" | "ghost";
|
|
||||||
type ButtonThemeType = "primary" | "secondary";
|
|
||||||
|
|
||||||
interface ButtonProps {
|
interface ButtonProps {
|
||||||
theme?: ButtonThemeType;
|
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
onClick?: () => void;
|
onClick?: () => void;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
variant?: ButtonVariantType;
|
|
||||||
leftIcon?: React.ReactNode;
|
leftIcon?: React.ReactNode;
|
||||||
rightIcon?: React.ReactNode;
|
rightIcon?: React.ReactNode;
|
||||||
color?: string;
|
|
||||||
style?: React.CSSProperties;
|
style?: React.CSSProperties;
|
||||||
autoFocus?: boolean;
|
autoFocus?: boolean;
|
||||||
className?: string;
|
className?: string;
|
||||||
@@ -27,10 +18,8 @@ interface ButtonProps {
|
|||||||
|
|
||||||
class Button extends React.Component<ButtonProps> {
|
class Button extends React.Component<ButtonProps> {
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
theme: "primary",
|
|
||||||
variant: "solid",
|
|
||||||
color: "",
|
|
||||||
style: {},
|
style: {},
|
||||||
|
className: "primary",
|
||||||
};
|
};
|
||||||
|
|
||||||
@boundMethod
|
@boundMethod
|
||||||
@@ -41,31 +30,11 @@ class Button extends React.Component<ButtonProps> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const { leftIcon, rightIcon, children, disabled, style, autoFocus, termInline, className } = this.props;
|
||||||
leftIcon,
|
|
||||||
rightIcon,
|
|
||||||
theme,
|
|
||||||
children,
|
|
||||||
disabled,
|
|
||||||
variant,
|
|
||||||
color,
|
|
||||||
style,
|
|
||||||
autoFocus,
|
|
||||||
termInline,
|
|
||||||
className,
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
className={cn(
|
className={cn("wave-button", { disabled }, { "term-inline": termInline }, className)}
|
||||||
"wave-button",
|
|
||||||
theme,
|
|
||||||
variant,
|
|
||||||
color,
|
|
||||||
{ disabled: disabled },
|
|
||||||
{ "term-inline": termInline },
|
|
||||||
className
|
|
||||||
)}
|
|
||||||
onClick={this.handleClick}
|
onClick={this.handleClick}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
style={style}
|
style={style}
|
||||||
|
|||||||
@@ -1,21 +0,0 @@
|
|||||||
// Copyright 2023, Command Line Inc.
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
import * as React from "react";
|
|
||||||
import { Button } from "./button";
|
|
||||||
class IconButton extends Button {
|
|
||||||
render() {
|
|
||||||
const { children, theme, variant = "solid", ...rest } = this.props;
|
|
||||||
const className = `wave-button icon-button ${theme} ${variant}`;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<button {...rest} className={className}>
|
|
||||||
{children}
|
|
||||||
</button>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default IconButton;
|
|
||||||
|
|
||||||
export { IconButton };
|
|
||||||
@@ -3,7 +3,6 @@ export { Checkbox } from "./checkbox";
|
|||||||
export { CmdStrCode } from "./cmdstrcode";
|
export { CmdStrCode } from "./cmdstrcode";
|
||||||
export { renderCmdText } from "./cmdtext";
|
export { renderCmdText } from "./cmdtext";
|
||||||
export { Dropdown } from "./dropdown";
|
export { Dropdown } from "./dropdown";
|
||||||
export { IconButton } from "./iconbutton";
|
|
||||||
export { InlineSettingsTextEdit } from "./inlinesettingstextedit";
|
export { InlineSettingsTextEdit } from "./inlinesettingstextedit";
|
||||||
export { InputDecoration } from "./inputdecoration";
|
export { InputDecoration } from "./inputdecoration";
|
||||||
export { LinkButton } from "./linkbutton";
|
export { LinkButton } from "./linkbutton";
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import * as mobx from "mobx";
|
|||||||
import { If } from "tsx-control-statements/components";
|
import { If } from "tsx-control-statements/components";
|
||||||
import ReactDOM from "react-dom";
|
import ReactDOM from "react-dom";
|
||||||
import { Button } from "./button";
|
import { Button } from "./button";
|
||||||
import { IconButton } from "./iconbutton";
|
|
||||||
|
|
||||||
import "./modal.less";
|
import "./modal.less";
|
||||||
|
|
||||||
@@ -19,9 +18,9 @@ const ModalHeader: React.FC<ModalHeaderProps> = ({ onClose, title }) => (
|
|||||||
<div className="wave-modal-header">
|
<div className="wave-modal-header">
|
||||||
{<div className="wave-modal-title">{title}</div>}
|
{<div className="wave-modal-title">{title}</div>}
|
||||||
<If condition={onClose}>
|
<If condition={onClose}>
|
||||||
<IconButton theme="secondary" variant="ghost" onClick={onClose}>
|
<Button className="secondary ghost" onClick={onClose}>
|
||||||
<i className="fa-sharp fa-solid fa-xmark"></i>
|
<i className="fa-sharp fa-solid fa-xmark"></i>
|
||||||
</IconButton>
|
</Button>
|
||||||
</If>
|
</If>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@@ -36,11 +35,15 @@ interface ModalFooterProps {
|
|||||||
const ModalFooter: React.FC<ModalFooterProps> = ({ onCancel, onOk, cancelLabel = "Cancel", okLabel = "Ok" }) => (
|
const ModalFooter: React.FC<ModalFooterProps> = ({ onCancel, onOk, cancelLabel = "Cancel", okLabel = "Ok" }) => (
|
||||||
<div className="wave-modal-footer">
|
<div className="wave-modal-footer">
|
||||||
{onCancel && (
|
{onCancel && (
|
||||||
<Button theme="secondary" onClick={onCancel}>
|
<Button className="secondary" onClick={onCancel}>
|
||||||
{cancelLabel}
|
{cancelLabel}
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
{onOk && <Button onClick={onOk}>{okLabel}</Button>}
|
{onOk && (
|
||||||
|
<Button className="primary" onClick={onOk}>
|
||||||
|
{okLabel}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import * as mobxReact from "mobx-react";
|
|||||||
import * as mobx from "mobx";
|
import * as mobx from "mobx";
|
||||||
import { boundMethod } from "autobind-decorator";
|
import { boundMethod } from "autobind-decorator";
|
||||||
import cn from "classnames";
|
import cn from "classnames";
|
||||||
import { GlobalModel, GlobalCommandRunner } from "@/models";
|
import { GlobalCommandRunner, SidebarModel } from "@/models";
|
||||||
import { MagicLayout } from "@/app/magiclayout";
|
import { MagicLayout } from "@/app/magiclayout";
|
||||||
|
|
||||||
import "./resizablesidebar.less";
|
import "./resizablesidebar.less";
|
||||||
@@ -14,6 +14,7 @@ import "./resizablesidebar.less";
|
|||||||
interface ResizableSidebarProps {
|
interface ResizableSidebarProps {
|
||||||
parentRef: React.RefObject<HTMLElement>;
|
parentRef: React.RefObject<HTMLElement>;
|
||||||
position: "left" | "right";
|
position: "left" | "right";
|
||||||
|
model: SidebarModel;
|
||||||
enableSnap?: boolean;
|
enableSnap?: boolean;
|
||||||
className?: string;
|
className?: string;
|
||||||
children?: (toggleCollapsed: () => void) => React.ReactNode;
|
children?: (toggleCollapsed: () => void) => React.ReactNode;
|
||||||
@@ -32,7 +33,7 @@ class ResizableSidebar extends React.Component<ResizableSidebarProps> {
|
|||||||
startResizing(event: React.MouseEvent<HTMLDivElement>) {
|
startResizing(event: React.MouseEvent<HTMLDivElement>) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
const { parentRef, position } = this.props;
|
const { parentRef, position, model } = this.props;
|
||||||
const parentRect = parentRef.current?.getBoundingClientRect();
|
const parentRect = parentRef.current?.getBoundingClientRect();
|
||||||
|
|
||||||
if (!parentRect) return;
|
if (!parentRect) return;
|
||||||
@@ -43,17 +44,16 @@ class ResizableSidebar extends React.Component<ResizableSidebarProps> {
|
|||||||
this.startX = event.clientX - parentRect.left;
|
this.startX = event.clientX - parentRect.left;
|
||||||
}
|
}
|
||||||
|
|
||||||
const mainSidebarModel = GlobalModel.mainSidebarModel;
|
const collapsed = model.getCollapsed();
|
||||||
const collapsed = mainSidebarModel.getCollapsed();
|
|
||||||
|
|
||||||
this.resizeStartWidth = mainSidebarModel.getWidth();
|
this.resizeStartWidth = model.getWidth();
|
||||||
document.addEventListener("mousemove", this.onMouseMove);
|
document.addEventListener("mousemove", this.onMouseMove);
|
||||||
document.addEventListener("mouseup", this.stopResizing);
|
document.addEventListener("mouseup", this.stopResizing);
|
||||||
|
|
||||||
document.body.style.cursor = "col-resize";
|
document.body.style.cursor = "col-resize";
|
||||||
mobx.action(() => {
|
mobx.action(() => {
|
||||||
mainSidebarModel.setTempWidthAndTempCollapsed(this.resizeStartWidth, collapsed);
|
model.setTempWidthAndTempCollapsed(this.resizeStartWidth, collapsed);
|
||||||
mainSidebarModel.isDragging.set(true);
|
model.isDragging.set(true);
|
||||||
})();
|
})();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,11 +61,10 @@ class ResizableSidebar extends React.Component<ResizableSidebarProps> {
|
|||||||
onMouseMove(event: MouseEvent) {
|
onMouseMove(event: MouseEvent) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
const { parentRef, enableSnap, position } = this.props;
|
const { parentRef, enableSnap, position, model } = this.props;
|
||||||
const parentRect = parentRef.current?.getBoundingClientRect();
|
const parentRect = parentRef.current?.getBoundingClientRect();
|
||||||
const mainSidebarModel = GlobalModel.mainSidebarModel;
|
|
||||||
|
|
||||||
if (!mainSidebarModel.isDragging.get() || !parentRect) return;
|
if (!model.isDragging.get() || !parentRect) return;
|
||||||
|
|
||||||
let delta: number, newWidth: number;
|
let delta: number, newWidth: number;
|
||||||
|
|
||||||
@@ -100,34 +99,27 @@ class ResizableSidebar extends React.Component<ResizableSidebarProps> {
|
|||||||
|
|
||||||
if (newWidth - dragResistance > minWidth && newWidth < snapPoint && dragDirection == "+") {
|
if (newWidth - dragResistance > minWidth && newWidth < snapPoint && dragDirection == "+") {
|
||||||
newWidth = snapPoint;
|
newWidth = snapPoint;
|
||||||
mainSidebarModel.setTempWidthAndTempCollapsed(newWidth, false);
|
model.setTempWidthAndTempCollapsed(newWidth, false);
|
||||||
} else if (newWidth + dragResistance < snapPoint && dragDirection == "-") {
|
} else if (newWidth + dragResistance < snapPoint && dragDirection == "-") {
|
||||||
newWidth = minWidth;
|
newWidth = minWidth;
|
||||||
mainSidebarModel.setTempWidthAndTempCollapsed(newWidth, true);
|
model.setTempWidthAndTempCollapsed(newWidth, true);
|
||||||
} else if (newWidth > snapPoint) {
|
} else if (newWidth > snapPoint) {
|
||||||
mainSidebarModel.setTempWidthAndTempCollapsed(newWidth, false);
|
model.setTempWidthAndTempCollapsed(newWidth, false);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (newWidth <= MagicLayout.MainSidebarMinWidth) {
|
if (newWidth <= MagicLayout.MainSidebarMinWidth) {
|
||||||
mainSidebarModel.setTempWidthAndTempCollapsed(newWidth, true);
|
model.setTempWidthAndTempCollapsed(newWidth, true);
|
||||||
} else {
|
} else {
|
||||||
mainSidebarModel.setTempWidthAndTempCollapsed(newWidth, false);
|
model.setTempWidthAndTempCollapsed(newWidth, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@boundMethod
|
@boundMethod
|
||||||
stopResizing() {
|
stopResizing() {
|
||||||
let mainSidebarModel = GlobalModel.mainSidebarModel;
|
const { model } = this.props;
|
||||||
|
|
||||||
GlobalCommandRunner.clientSetSidebar(
|
model.saveState(model.tempWidth.get(), model.tempCollapsed.get());
|
||||||
mainSidebarModel.tempWidth.get(),
|
|
||||||
mainSidebarModel.tempCollapsed.get()
|
|
||||||
).finally(() => {
|
|
||||||
mobx.action(() => {
|
|
||||||
mainSidebarModel.isDragging.set(false);
|
|
||||||
})();
|
|
||||||
});
|
|
||||||
|
|
||||||
document.removeEventListener("mousemove", this.onMouseMove);
|
document.removeEventListener("mousemove", this.onMouseMove);
|
||||||
document.removeEventListener("mouseup", this.stopResizing);
|
document.removeEventListener("mouseup", this.stopResizing);
|
||||||
@@ -136,19 +128,18 @@ class ResizableSidebar extends React.Component<ResizableSidebarProps> {
|
|||||||
|
|
||||||
@boundMethod
|
@boundMethod
|
||||||
toggleCollapsed() {
|
toggleCollapsed() {
|
||||||
const mainSidebarModel = GlobalModel.mainSidebarModel;
|
const { model } = this.props;
|
||||||
|
|
||||||
const tempCollapsed = mainSidebarModel.getCollapsed();
|
const tempCollapsed = model.getCollapsed();
|
||||||
const width = mainSidebarModel.getWidth(true);
|
const width = model.getWidth(true);
|
||||||
mainSidebarModel.setTempWidthAndTempCollapsed(width, !tempCollapsed);
|
model.setTempWidthAndTempCollapsed(width, !tempCollapsed);
|
||||||
GlobalCommandRunner.clientSetSidebar(width, !tempCollapsed);
|
model.saveState(width, !tempCollapsed);
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { className, children } = this.props;
|
const { className, children, model } = this.props;
|
||||||
const mainSidebarModel = GlobalModel.mainSidebarModel;
|
const width = model.getWidth();
|
||||||
const width = mainSidebarModel.getWidth();
|
const isCollapsed = model.getCollapsed();
|
||||||
const isCollapsed = mainSidebarModel.getCollapsed();
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={cn("sidebar", className, { collapsed: isCollapsed })} style={{ width, minWidth: width }}>
|
<div className={cn("sidebar", className, { collapsed: isCollapsed })} style={{ width, minWidth: width }}>
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ class AlertModal extends React.Component<{}, {}> {
|
|||||||
</div>
|
</div>
|
||||||
<div className="wave-modal-footer">
|
<div className="wave-modal-footer">
|
||||||
<If condition={isConfirm}>
|
<If condition={isConfirm}>
|
||||||
<Button theme="secondary" onClick={this.closeModal}>
|
<Button className="secondary" onClick={this.closeModal}>
|
||||||
Cancel
|
Cancel
|
||||||
</Button>
|
</Button>
|
||||||
<Button autoFocus={true} onClick={this.handleOK}>
|
<Button autoFocus={true} onClick={this.handleOK}>
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ class ClientStopModal extends React.Component<{}, {}> {
|
|||||||
</If>
|
</If>
|
||||||
<div>
|
<div>
|
||||||
<Button
|
<Button
|
||||||
theme="secondary"
|
className="secondary"
|
||||||
onClick={this.refreshClient}
|
onClick={this.refreshClient}
|
||||||
leftIcon={<i className="fa-sharp fa-solid fa-rotate"></i>}
|
leftIcon={<i className="fa-sharp fa-solid fa-rotate"></i>}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ class DisconnectedModal extends React.Component<{}, {}> {
|
|||||||
</div>
|
</div>
|
||||||
<div className="wave-modal-footer">
|
<div className="wave-modal-footer">
|
||||||
<Button
|
<Button
|
||||||
theme="secondary"
|
className="secondary"
|
||||||
onClick={this.tryReconnect}
|
onClick={this.tryReconnect}
|
||||||
leftIcon={
|
leftIcon={
|
||||||
<span className="icon">
|
<span className="icon">
|
||||||
@@ -86,7 +86,7 @@ class DisconnectedModal extends React.Component<{}, {}> {
|
|||||||
Try Reconnect
|
Try Reconnect
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
theme="secondary"
|
className="secondary"
|
||||||
onClick={this.restartServer}
|
onClick={this.restartServer}
|
||||||
leftIcon={<i className="fa-sharp fa-solid fa-triangle-exclamation"></i>}
|
leftIcon={<i className="fa-sharp fa-solid fa-triangle-exclamation"></i>}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -162,42 +162,42 @@ class ViewRemoteConnDetailModal extends React.Component<{}, {}> {
|
|||||||
renderHeaderBtns(remote: RemoteType): React.ReactNode {
|
renderHeaderBtns(remote: RemoteType): React.ReactNode {
|
||||||
let buttons: React.ReactNode[] = [];
|
let buttons: React.ReactNode[] = [];
|
||||||
const disconnectButton = (
|
const disconnectButton = (
|
||||||
<Button theme="secondary" onClick={() => this.disconnectRemote(remote.remoteid)}>
|
<Button className="secondary" onClick={() => this.disconnectRemote(remote.remoteid)}>
|
||||||
Disconnect Now
|
Disconnect Now
|
||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
const connectButton = (
|
const connectButton = (
|
||||||
<Button theme="secondary" onClick={() => this.connectRemote(remote.remoteid)}>
|
<Button className="secondary" onClick={() => this.connectRemote(remote.remoteid)}>
|
||||||
Connect Now
|
Connect Now
|
||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
const tryReconnectButton = (
|
const tryReconnectButton = (
|
||||||
<Button theme="secondary" onClick={() => this.connectRemote(remote.remoteid)}>
|
<Button className="secondary" onClick={() => this.connectRemote(remote.remoteid)}>
|
||||||
Try Reconnect
|
Try Reconnect
|
||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
let updateAuthButton = (
|
let updateAuthButton = (
|
||||||
<Button theme="secondary" onClick={() => this.openEditModal()}>
|
<Button className="secondary" onClick={() => this.openEditModal()}>
|
||||||
Edit
|
Edit
|
||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
let cancelInstallButton = (
|
let cancelInstallButton = (
|
||||||
<Button theme="secondary" onClick={() => this.cancelInstall(remote.remoteid)}>
|
<Button className="secondary" onClick={() => this.cancelInstall(remote.remoteid)}>
|
||||||
Cancel Install
|
Cancel Install
|
||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
let installNowButton = (
|
let installNowButton = (
|
||||||
<Button theme="secondary" onClick={() => this.installRemote(remote.remoteid)}>
|
<Button className="secondary" onClick={() => this.installRemote(remote.remoteid)}>
|
||||||
Install Now
|
Install Now
|
||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
let archiveButton = (
|
let archiveButton = (
|
||||||
<Button theme="secondary" onClick={() => this.clickArchive()}>
|
<Button className="secondary" onClick={() => this.clickArchive()}>
|
||||||
Delete
|
Delete
|
||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
const reinstallButton = (
|
const reinstallButton = (
|
||||||
<Button theme="secondary" onClick={this.clickReinstall}>
|
<Button className="secondary" onClick={this.clickReinstall}>
|
||||||
Reinstall
|
Reinstall
|
||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
@@ -207,7 +207,7 @@ class ViewRemoteConnDetailModal extends React.Component<{}, {}> {
|
|||||||
}
|
}
|
||||||
if (remote.sshconfigsrc == "sshconfig-import") {
|
if (remote.sshconfigsrc == "sshconfig-import") {
|
||||||
archiveButton = (
|
archiveButton = (
|
||||||
<Button theme="secondary" onClick={() => this.clickArchive()}>
|
<Button className="secondary" onClick={() => this.clickArchive()}>
|
||||||
Delete
|
Delete
|
||||||
<Tooltip
|
<Tooltip
|
||||||
message={
|
message={
|
||||||
@@ -383,7 +383,7 @@ class ViewRemoteConnDetailModal extends React.Component<{}, {}> {
|
|||||||
</div>
|
</div>
|
||||||
<div className="wave-modal-footer">
|
<div className="wave-modal-footer">
|
||||||
<Button
|
<Button
|
||||||
theme="secondary"
|
className="secondary"
|
||||||
disabled={selectedRemoteStatus == "connecting"}
|
disabled={selectedRemoteStatus == "connecting"}
|
||||||
onClick={this.handleClose}
|
onClick={this.handleClose}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -180,14 +180,14 @@ class ConnectionsView extends React.Component<{ model: RemotesModel }, { hovered
|
|||||||
</table>
|
</table>
|
||||||
<footer>
|
<footer>
|
||||||
<Button
|
<Button
|
||||||
theme="secondary"
|
className="secondary"
|
||||||
leftIcon={<i className="fa-sharp fa-solid fa-plus"></i>}
|
leftIcon={<i className="fa-sharp fa-solid fa-plus"></i>}
|
||||||
onClick={this.handleAddConnection}
|
onClick={this.handleAddConnection}
|
||||||
>
|
>
|
||||||
New Connection
|
New Connection
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
theme="secondary"
|
className="secondary"
|
||||||
leftIcon={<i className="fa-sharp fa-solid fa-fw fa-file-import"></i>}
|
leftIcon={<i className="fa-sharp fa-solid fa-fw fa-file-import"></i>}
|
||||||
onClick={this.handleImportSshConfig}
|
onClick={this.handleImportSshConfig}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -16,11 +16,17 @@ let MagicLayout = {
|
|||||||
ScreenSidebarMinWidth: 200,
|
ScreenSidebarMinWidth: 200,
|
||||||
ScreenSidebarHeaderHeight: 26,
|
ScreenSidebarHeaderHeight: 26,
|
||||||
|
|
||||||
MainSidebarMinWidth: 75,
|
MainSidebarMinWidth: 0,
|
||||||
MainSidebarMaxWidth: 300,
|
MainSidebarMaxWidth: 300,
|
||||||
MainSidebarSnapThreshold: 90,
|
MainSidebarSnapThreshold: 165,
|
||||||
MainSidebarDragResistance: 50,
|
MainSidebarDragResistance: 50,
|
||||||
MainSidebarDefaultWidth: 240,
|
MainSidebarDefaultWidth: 240,
|
||||||
|
|
||||||
|
RightSidebarMinWidth: 0,
|
||||||
|
RightSidebarMaxWidth: 300,
|
||||||
|
RightSidebarSnapThreshold: 90,
|
||||||
|
RightSidebarDragResistance: 50,
|
||||||
|
RightSidebarDefaultWidth: 240,
|
||||||
};
|
};
|
||||||
|
|
||||||
let m = MagicLayout;
|
let m = MagicLayout;
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import { isBlank, openLink } from "@/util/util";
|
|||||||
import { ResizableSidebar } from "@/common/elements";
|
import { ResizableSidebar } from "@/common/elements";
|
||||||
import * as appconst from "@/app/appconst";
|
import * as appconst from "@/app/appconst";
|
||||||
|
|
||||||
import "./sidebar.less";
|
import "./main.less";
|
||||||
import { ActionsIcon, CenteredIcon, FrontIcon, StatusIndicator } from "@/common/icons/icons";
|
import { ActionsIcon, CenteredIcon, FrontIcon, StatusIndicator } from "@/common/icons/icons";
|
||||||
|
|
||||||
dayjs.extend(localizedFormat);
|
dayjs.extend(localizedFormat);
|
||||||
@@ -251,6 +251,7 @@ class MainSideBar extends React.Component<MainSideBarProps, {}> {
|
|||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<ResizableSidebar
|
<ResizableSidebar
|
||||||
|
model={GlobalModel.mainSidebarModel}
|
||||||
className="main-sidebar"
|
className="main-sidebar"
|
||||||
position="left"
|
position="left"
|
||||||
enableSnap={true}
|
enableSnap={true}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
@import "@/common/icons/icons.less";
|
||||||
|
|
||||||
|
.right-sidebar {
|
||||||
|
padding: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
position: relative;
|
||||||
|
line-height: 20px;
|
||||||
|
backdrop-filter: blur(4px);
|
||||||
|
z-index: 20;
|
||||||
|
font-size: var(--sidebar-font-size);
|
||||||
|
font-family: var(--base-font-family);
|
||||||
|
font-weight: var(--sidebar-font-weight);
|
||||||
|
border-left: 1px solid var(--app-border-color);
|
||||||
|
|
||||||
|
.header {
|
||||||
|
height: 39px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-end;
|
||||||
|
padding: 0 5px;
|
||||||
|
border-bottom: 1px solid var(--app-border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.collapsed {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
// Copyright 2023, Command Line Inc.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
import * as React from "react";
|
||||||
|
import * as mobxReact from "mobx-react";
|
||||||
|
import dayjs from "dayjs";
|
||||||
|
|
||||||
|
import localizedFormat from "dayjs/plugin/localizedFormat";
|
||||||
|
import { GlobalModel } from "@/models";
|
||||||
|
import { ResizableSidebar, Button } from "@/elements";
|
||||||
|
|
||||||
|
import "./right.less";
|
||||||
|
|
||||||
|
dayjs.extend(localizedFormat);
|
||||||
|
|
||||||
|
interface RightSideBarProps {
|
||||||
|
parentRef: React.RefObject<HTMLElement>;
|
||||||
|
clientData: ClientDataType;
|
||||||
|
}
|
||||||
|
|
||||||
|
@mobxReact.observer
|
||||||
|
class RightSideBar extends React.Component<RightSideBarProps, {}> {
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<ResizableSidebar
|
||||||
|
model={GlobalModel.rightSidebarModel}
|
||||||
|
className="right-sidebar"
|
||||||
|
position="right"
|
||||||
|
enableSnap={true}
|
||||||
|
parentRef={this.props.parentRef}
|
||||||
|
>
|
||||||
|
{(toggleCollapse) => (
|
||||||
|
<React.Fragment>
|
||||||
|
<div className="header">
|
||||||
|
<Button className="secondary ghost" onClick={toggleCollapse}>
|
||||||
|
<i className="fa-sharp fa-regular fa-xmark"></i>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</React.Fragment>
|
||||||
|
)}
|
||||||
|
</ResizableSidebar>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export { RightSideBar };
|
||||||
@@ -325,7 +325,7 @@ class ScreenSidebar extends React.Component<{ screen: Screen; width: string }, {
|
|||||||
<br />
|
<br />
|
||||||
</div>
|
</div>
|
||||||
<div onClick={this.sidebarClose} className="close-button-container">
|
<div onClick={this.sidebarClose} className="close-button-container">
|
||||||
<Button theme="secondary" onClick={this.sidebarClose}>
|
<Button className="secondary" onClick={this.sidebarClose}>
|
||||||
Close Sidebar
|
Close Sidebar
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user