Move icons out of common, clean up visibility story (#251)

This commit is contained in:
Evan Simkowitz 2024-01-25 16:17:03 -08:00 committed by GitHub
parent 34ec4ff39f
commit b97423268c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 203 additions and 203 deletions

View File

@ -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;
}
}

View File

@ -1241,81 +1241,6 @@ class Modal extends React.Component<ModalProps> {
}
}
interface PositionalIconProps {
children?: React.ReactNode;
}
class FrontIcon extends React.Component<PositionalIconProps> {
render() {
return (
<div className="front-icon positional-icon">
<div className="positional-icon-inner">
{this.props.children}
</div>
</div>
);
}
}
class EndIcon extends React.Component<PositionalIconProps> {
render() {
return (
<div className="end-icon positional-icon">
<div className="positional-icon-inner">
{this.props.children}
</div>
</div>
);
}
}
interface ActionsIconProps {
onClick: React.MouseEventHandler<HTMLDivElement>;
}
class ActionsIcon extends React.Component<ActionsIconProps> {
render() {
return (
<div onClick={this.props.onClick} title="Actions" className="actions">
<div className="icon hoverEffect fa-sharp fa-solid fa-1x fa-ellipsis-vertical"></div>
</div>
);
}
}
interface StatusIndicatorProps {
level: StatusIndicatorLevel;
className?: string;
}
class StatusIndicator extends React.Component<StatusIndicatorProps> {
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 = (
<div
className={`${this.props.className} fa-sharp fa-solid fa-circle-small status-indicator ${statusIndicatorClass}`}
></div>
);
}
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,
};

View File

@ -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;
}
}

View File

@ -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<HTMLDivElement>;
}
export class FrontIcon extends React.Component<PositionalIconProps> {
render() {
return (
<div className={cn("front-icon", "positional-icon", this.props.className)}>
<div className="positional-icon-inner">{this.props.children}</div>
</div>
);
}
}
export class CenteredIcon extends React.Component<PositionalIconProps> {
render() {
return (
<div className={cn("centered-icon", "positional-icon", this.props.className)} onClick={this.props.onClick}>
<div className="positional-icon-inner">{this.props.children}</div>
</div>
);
}
}
interface ActionsIconProps {
onClick: React.MouseEventHandler<HTMLDivElement>;
}
export class ActionsIcon extends React.Component<ActionsIconProps> {
render() {
return (
<CenteredIcon className="actions" onClick={this.props.onClick}>
<div className="icon hoverEffect fa-sharp fa-solid fa-1x fa-ellipsis-vertical"></div>
</CenteredIcon>
);
}
}
interface StatusIndicatorProps {
level: StatusIndicatorLevel;
className?: string;
}
export class StatusIndicator extends React.Component<StatusIndicatorProps> {
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 = (
<CenteredIcon className={cn(this.props.className, "status-indicator")}>
<div className={cn(statusIndicatorClass, "fa-sharp", "fa-solid", "fa-circle-small")}></div>
</CenteredIcon>
);
}
return statusIndicator;
}
}

View File

@ -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;
}

View File

@ -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<V> = mobx.IObservableValue<V>;
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<HTMLDivElement>;
}> {
render() {
return (
<div
key={this.props.key}
className={cn("item", "unselectable", "hoverEffect", this.props.className)}
onClick={this.props.onClick}
>
<FrontIcon>{this.props.frontIcon}</FrontIcon>
<div className="item-contents truncate">{this.props.contents}</div>
<EndIcon>{this.props.endIcon}</EndIcon>
<div className="end-icons">{this.props.endIcons}</div>
</div>
);
}
}
class HotKeyIcon extends React.Component<{ hotkey: string }> {
render() {
return (
<CenteredIcon className="hotkey">
<span>&#x2318;{this.props.hotkey}</span>
</CenteredIcon>
);
}
}
@mobxReact.observer
class MainSideBar extends React.Component<{}, {}> {
collapsed: mobx.IObservableValue<boolean> = mobx.observable.box(false);
@ -180,15 +189,12 @@ class MainSideBar extends React.Component<{}, {}> {
const sessionIndicator = Math.max(...sessionScreens.map((screen) => screen.statusIndicator.get()));
return (
<SideBarItem
key={index}
className={`${isActive ? "active" : ""}`}
frontIcon={<span className="index">{index + 1}</span>}
contents={session.name.get()}
endIcon={[
<StatusIndicator level={sessionIndicator} />,
<ActionsIcon
onClick={(e) => this.openSessionSettings(e, session)}
/>,
endIcons={[
<StatusIndicator key="statusindicator" level={sessionIndicator} />,
<ActionsIcon key="actions" onClick={(e) => this.openSessionSettings(e, session)} />,
]}
onClick={() => this.handleSessionClick(session.sessionId)}
/>
@ -228,12 +234,12 @@ class MainSideBar extends React.Component<{}, {}> {
<SideBarItem
frontIcon={<i className="fa-sharp fa-regular fa-clock-rotate-left icon" />}
contents="History"
endIcon={[<span className="hotkey">&#x2318;H</span>]}
endIcons={[<HotKeyIcon key="hotkey" hotkey="H" />]}
onClick={this.handleHistoryClick}
/>
{/* <SideBarItem className="hoverEffect unselectable" frontIcon={<FavoritesIcon className="icon" />} contents="Favorites" endIcon={<span className="hotkey">&#x2318;B</span>} onClick={this.handleBookmarksClick}/> */}
<SideBarItem
frontIcon={<i className="fa-sharp fa-regular fa-globe icon "/>}
frontIcon={<i className="fa-sharp fa-regular fa-globe icon " />}
contents="Connections"
onClick={this.handleConnectionsClick}
/>
@ -242,8 +248,12 @@ class MainSideBar extends React.Component<{}, {}> {
<SideBarItem
frontIcon={<WorkspacesIcon className="icon" />}
contents="Workspaces"
endIcon={[
<div className="add_workspace hoverEffect" onClick={this.handleNewSession}>
endIcons={[
<div
key="add_workspace"
className="add_workspace hoverEffect"
onClick={this.handleNewSession}
>
<AddIcon />
</div>,
]}
@ -263,11 +273,11 @@ class MainSideBar extends React.Component<{}, {}> {
frontIcon={<AppsIcon className="icon" />}
contents="Apps"
onClick={this.handlePluginsClick}
endIcon={[<span className="hotkey">&#x2318;A</span>]}
endIcons={[<HotKeyIcon key="hotkey" hotkey="A" />]}
/>
</If>
<SideBarItem
frontIcon={<i className="fa-sharp fa-regular fa-gear icon"/>}
frontIcon={<SettingsIcon className="icon" />}
contents="Settings"
onClick={this.handleSettingsClick}
/>

View File

@ -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 = <div className="tab-index">{renderCmdText(String(index + 1))}</div>;
tabIndex = (
<CenteredIcon className="tab-index">
<div>{renderCmdText(String(index + 1))}</div>
</CenteredIcon>
);
}
let archived = screen.archived.get() ? (
<i title="archived" className="fa-sharp fa-solid fa-box-archive" />
@ -109,19 +114,17 @@ class ScreenTab extends React.Component<
onContextMenu={(event) => this.openScreenSettings(event, screen)}
onDragEnd={this.handleDragEnd}
>
<div className="front-icon">
{this.renderTabIcon(screen)}
</div>
<CenteredIcon className="front-icon">{this.renderTabIcon(screen)}</CenteredIcon>
<div className="tab-name truncate">
{archived}
{webShared}
{screen.name.get()}
</div>
<EndIcon>
<StatusIndicator level={statusIndicatorLevel}/>
<div className="end-icons">
<StatusIndicator level={statusIndicatorLevel} />
{tabIndex}
<ActionsIcon onClick={(e) => this.openScreenSettings(e, screen)} />
</EndIcon>
</div>
</Reorder.Item>
);
}

View File

@ -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;
}
}
}