diff --git a/src/app/common/common.less b/src/app/common/common.less index c1b8f208d..4bfc892a6 100644 --- a/src/app/common/common.less +++ b/src/app/common/common.less @@ -1151,42 +1151,3 @@ } } } - -.front-icon { - margin-right: 5px; - .svg-icon svg { - width: 14px; - height: 14px; - } - font-size: 16px; -} - -.positional-icon-inner { - & > div,i { - text-align: center; - align-items: center; - vertical-align: middle; - width: 20px; - margin: auto auto; - } -} - -.actions { - .icon { - font-size: 15px; - padding-top: 2.5px; - margin-bottom: -2.5px; - } -} - -.status-indicator { - &.error { - color: @term-red; - } - &.success { - color: @term-green; - } - &.output { - color: @term-white; - } -} diff --git a/src/app/common/common.tsx b/src/app/common/common.tsx index 35d5f4a54..0f382348d 100644 --- a/src/app/common/common.tsx +++ b/src/app/common/common.tsx @@ -1241,81 +1241,6 @@ class Modal extends React.Component { } } -interface PositionalIconProps { - children?: React.ReactNode; -} - -class FrontIcon extends React.Component { - render() { - return ( -
-
- {this.props.children} -
-
- ); - } -} - -class EndIcon extends React.Component { - render() { - return ( -
-
- {this.props.children} -
-
- ); - } - -} - -interface ActionsIconProps { - onClick: React.MouseEventHandler; -} - -class ActionsIcon extends React.Component { - render() { - return ( -
-
-
- ); - } -} - -interface StatusIndicatorProps { - level: StatusIndicatorLevel; - className?: string; -} - -class StatusIndicator extends React.Component { - render() { - const statusIndicatorLevel = this.props.level; - let statusIndicator = null; - if (statusIndicatorLevel != StatusIndicatorLevel.None) { - let statusIndicatorClass = null; - switch (statusIndicatorLevel) { - case StatusIndicatorLevel.Output: - statusIndicatorClass = "output"; - break; - case StatusIndicatorLevel.Success: - statusIndicatorClass = "success"; - break; - case StatusIndicatorLevel.Error: - statusIndicatorClass = "error"; - break; - } - statusIndicator = ( -
- ); - } - return statusIndicator; - } -} - function ShowWaveShellInstallPrompt(callbackFn: () => void) { let message: string = ` In order to use Wave's advanced features like unified history and persistent sessions, Wave installs a small, open-source helper program called WaveShell on your remote machine. WaveShell does not open any external ports and only communicates with your *local* Wave terminal instance over ssh. For more information please see [the docs](https://docs.waveterm.dev/reference/waveshell). @@ -1358,9 +1283,5 @@ export { LinkButton, Status, Modal, - FrontIcon, - EndIcon, - ActionsIcon, - StatusIndicator, ShowWaveShellInstallPrompt, }; diff --git a/src/app/common/icons/icons.less b/src/app/common/icons/icons.less new file mode 100644 index 000000000..2bee74395 --- /dev/null +++ b/src/app/common/icons/icons.less @@ -0,0 +1,58 @@ +@import "../../../app/common/themes/themes.less"; + +.front-icon { + margin-right: 5px; +} + +.positional-icon-visible { + display: inline-block; +} + +.positional-icon-hidden { + display: none; +} + +.positional-icon { + display: none; + width: 20px; + height: 20px; + .svg-icon svg { + width: 14px; + height: 14px; + } + font-size: 16px; +} + +.positional-icon-inner { + display: flex; + width: inherit; + height: 100%; + line-height: inherit; + align-items: center; + justify-content: center; + & > div,i,.svg-icon,span { + width: 20px; + display: flex; + text-align: center; + align-items: center; + justify-content: center; + } +} + +.actions { + .icon { + font-size: 14px; + } +} + +.status-indicator { + .error { + color: @term-red; + } + .success { + color: @term-green; + } + .output { + color: @term-white; + } +} diff --git a/src/app/common/icons/icons.tsx b/src/app/common/icons/icons.tsx new file mode 100644 index 000000000..744f16214 --- /dev/null +++ b/src/app/common/icons/icons.tsx @@ -0,0 +1,75 @@ +import React from "react"; +import { StatusIndicatorLevel } from "../../../types/types"; +import cn from "classnames"; + +interface PositionalIconProps { + children?: React.ReactNode; + className?: string; + onClick?: React.MouseEventHandler; +} + +export class FrontIcon extends React.Component { + render() { + return ( +
+
{this.props.children}
+
+ ); + } +} + +export class CenteredIcon extends React.Component { + render() { + return ( +
+
{this.props.children}
+
+ ); + } +} + +interface ActionsIconProps { + onClick: React.MouseEventHandler; +} + +export class ActionsIcon extends React.Component { + render() { + return ( + +
+
+ ); + } +} + +interface StatusIndicatorProps { + level: StatusIndicatorLevel; + className?: string; +} + +export class StatusIndicator extends React.Component { + render() { + const statusIndicatorLevel = this.props.level; + let statusIndicator = null; + if (statusIndicatorLevel != StatusIndicatorLevel.None) { + let statusIndicatorClass = null; + switch (statusIndicatorLevel) { + case StatusIndicatorLevel.Output: + statusIndicatorClass = "output"; + break; + case StatusIndicatorLevel.Success: + statusIndicatorClass = "success"; + break; + case StatusIndicatorLevel.Error: + statusIndicatorClass = "error"; + break; + } + statusIndicator = ( + +
+
+ ); + } + return statusIndicator; + } +} diff --git a/src/app/sidebar/sidebar.less b/src/app/sidebar/sidebar.less index 4ebd9135a..865602693 100644 --- a/src/app/sidebar/sidebar.less +++ b/src/app/sidebar/sidebar.less @@ -1,4 +1,5 @@ @import "../../app/common/themes/themes.less"; +@import "../../app/common/icons/icons.less"; .main-sidebar { padding: 0; @@ -23,6 +24,7 @@ &.collapsed { width: 6em; min-width: 6em; + .arrow-container, .collapse-button { transform: rotate(180deg); @@ -125,12 +127,9 @@ 0px 0px 0.5px 0px rgba(255, 255, 255, 0.5) inset, 0px 0.5px 0px 0px rgba(255, 255, 255, 0.2) inset; } .index { - margin: 0 1.5em 0 -0.5rem; font-size: 0.8em; - vertical-align: middle; - text-align: right; + text-align: center; width: 2em; - display: inline-block; } .hotkey { float: left !important; @@ -150,7 +149,7 @@ .item { padding: 5px; - margin: 0 6px; + margin-left: 6px; border-radius: 4px; opacity: 1; transition: opacity 0.1s ease-in-out, visibility 0.1s step-end; @@ -161,50 +160,43 @@ flex-direction: row; align-items: center; + .front-icon { + .positional-icon-visible; + } + + .end-icons { + height: 20px; + } + .item-contents { flex-grow: 1; } .icon { - margin: -2px 8px 0px 4px; width: 16px; height: 16px; - display: inline-block; - vertical-align: middle; - } - .actions.icon { - margin-left: 8px; } .hotkey { float: right; margin-right: 6px; - display: none; letter-spacing: 6px; } - .disabled .hotkey { - display: none; - } - .actions { - display: none; - } &:hover { - .hotkey { - display: block; + :not(.disabled) .hotkey { + .positional-icon-visible; } .actions { - display: block; - } - .status-indicator { - display: none; + .positional-icon-visible; } } + + &:not(:hover) .status-indicator { + .positional-icon-visible; + } .add_workspace { float: right; - width: 1.5rem; - height: 1.5rem; padding: 2px; margin-right: 6px; transition: transform 0.3s ease-in-out; - vertical-align: middle; svg { fill: @base-color; } diff --git a/src/app/sidebar/sidebar.tsx b/src/app/sidebar/sidebar.tsx index e1fab8f6d..ed0f79e54 100644 --- a/src/app/sidebar/sidebar.tsx +++ b/src/app/sidebar/sidebar.tsx @@ -15,6 +15,7 @@ import { ReactComponent as LeftChevronIcon } from "../assets/icons/chevron_left. import { ReactComponent as AppsIcon } from "../assets/icons/apps.svg"; import { ReactComponent as WorkspacesIcon } from "../assets/icons/workspaces.svg"; import { ReactComponent as AddIcon } from "../assets/icons/add.svg"; +import { ReactComponent as SettingsIcon } from "../assets/icons/settings.svg"; import localizedFormat from "dayjs/plugin/localizedFormat"; import { GlobalModel, GlobalCommandRunner, Session, VERSION } from "../../model/model"; @@ -22,7 +23,7 @@ import { isBlank, openLink } from "../../util/util"; import * as constants from "../appconst"; import "./sidebar.less"; -import { ActionsIcon, EndIcon, FrontIcon, StatusIndicator } from "../common/common"; +import { ActionsIcon, CenteredIcon, FrontIcon, StatusIndicator } from "../common/icons/icons"; dayjs.extend(localizedFormat); @@ -31,26 +32,34 @@ type OV = mobx.IObservableValue; class SideBarItem extends React.Component<{ frontIcon: React.ReactNode; contents: React.ReactNode | string; - endIcon?: React.ReactNode[]; + endIcons?: React.ReactNode[]; className?: string; - key?: React.Key; onClick?: React.MouseEventHandler; }> { render() { return (
{this.props.frontIcon}
{this.props.contents}
- {this.props.endIcon} +
{this.props.endIcons}
); } } +class HotKeyIcon extends React.Component<{ hotkey: string }> { + render() { + return ( + + ⌘{this.props.hotkey} + + ); + } +} + @mobxReact.observer class MainSideBar extends React.Component<{}, {}> { collapsed: mobx.IObservableValue = mobx.observable.box(false); @@ -180,15 +189,12 @@ class MainSideBar extends React.Component<{}, {}> { const sessionIndicator = Math.max(...sessionScreens.map((screen) => screen.statusIndicator.get())); return ( {index + 1}} contents={session.name.get()} - endIcon={[ - , - this.openSessionSettings(e, session)} - />, + endIcons={[ + , + this.openSessionSettings(e, session)} />, ]} onClick={() => this.handleSessionClick(session.sessionId)} /> @@ -228,12 +234,12 @@ class MainSideBar extends React.Component<{}, {}> { } contents="History" - endIcon={[⌘H]} + endIcons={[]} onClick={this.handleHistoryClick} /> {/* } contents="Favorites" endIcon={⌘B} onClick={this.handleBookmarksClick}/> */} } + frontIcon={} contents="Connections" onClick={this.handleConnectionsClick} /> @@ -242,8 +248,12 @@ class MainSideBar extends React.Component<{}, {}> { } contents="Workspaces" - endIcon={[ -
+ endIcons={[ +
, ]} @@ -263,11 +273,11 @@ class MainSideBar extends React.Component<{}, {}> { frontIcon={} contents="Apps" onClick={this.handlePluginsClick} - endIcon={[⌘A]} + endIcons={[]} /> } + frontIcon={} contents="Settings" onClick={this.handleSettingsClick} /> diff --git a/src/app/workspace/screen/tab.tsx b/src/app/workspace/screen/tab.tsx index b83bbbcde..26e6b9ade 100644 --- a/src/app/workspace/screen/tab.tsx +++ b/src/app/workspace/screen/tab.tsx @@ -7,7 +7,8 @@ import * as mobx from "mobx"; import { boundMethod } from "autobind-decorator"; import cn from "classnames"; import { GlobalModel, GlobalCommandRunner, Screen } from "../../../model/model"; -import { ActionsIcon, EndIcon, StatusIndicator, renderCmdText } from "../../common/common"; +import { ActionsIcon, StatusIndicator, CenteredIcon } from "../../common/icons/icons"; +import { renderCmdText } from "../../common/common"; import { ReactComponent as SquareIcon } from "../../assets/icons/tab/square.svg"; import * as constants from "../../appconst"; import { Reorder } from "framer-motion"; @@ -79,7 +80,11 @@ class ScreenTab extends React.Component< let tabIndex = null; if (index + 1 <= 9) { - tabIndex =
{renderCmdText(String(index + 1))}
; + tabIndex = ( + +
{renderCmdText(String(index + 1))}
+
+ ); } let archived = screen.archived.get() ? ( @@ -109,19 +114,17 @@ class ScreenTab extends React.Component< onContextMenu={(event) => this.openScreenSettings(event, screen)} onDragEnd={this.handleDragEnd} > -
- {this.renderTabIcon(screen)} -
+ {this.renderTabIcon(screen)}
{archived} {webShared} {screen.name.get()}
- - +
+ {tabIndex} this.openScreenSettings(e, screen)} /> - +
); } diff --git a/src/app/workspace/screen/tabs.less b/src/app/workspace/screen/tabs.less index 059189f33..227e5db75 100644 --- a/src/app/workspace/screen/tabs.less +++ b/src/app/workspace/screen/tabs.less @@ -1,4 +1,5 @@ @import "../../../app/common/themes/themes.less"; +@import "../../../app/common/icons/icons.less"; #main .screen-tabs .screen-tab { border-top: 1px solid transparent; @@ -238,7 +239,6 @@ .screen-tabs-container-inner { overflow-x: scroll; - } .screen-tabs { @@ -257,14 +257,7 @@ padding: 0 8px 0 8px; .front-icon { - margin-right: 5px; - .svg-icon svg { - width: 14px; - height: 14px; - } - .fa-icon { - font-size: 16px; - } + .positional-icon-visible; } .tab-name { @@ -283,40 +276,27 @@ } // Only one of these will be visible at a time - .end-icon { + .end-icons { // This adjusts the position of the icon to account for the default 8px margin on the parent. We want the positional calculations for this icon to assume it is flush with the edge of the screen tab. margin: 0 -8px 0 0; - .status-indicator { - display: block; - } - .actions { - display: none; - } + .tab-index { - display: none; font-size: 12.5px; } } - - &:hover { - .actions { - display: block; - } - } } - &:hover .screen-tab { - .tab-index { - display: block; - } - - .status-indicator { - display: none; - } + &:not(:hover) .status-indicator{ + .positional-icon-visible; } - &:hover .screen-tab:hover .tab-index { - display: none; + &:hover { + .screen-tab:not(:hover) .tab-index { + .positional-icon-visible; + } + .screen-tab:hover .actions { + .positional-icon-visible; + } } }