Tab Settings (#117)

* use Modal component

* icons selector

* do not limit icons in the backend
This commit is contained in:
Red J Adaya
2023-12-04 09:52:59 -08:00
committed by GitHub
parent 7310481383
commit 0c756838e0
6 changed files with 174 additions and 137 deletions
+6 -4
View File
@@ -1122,10 +1122,12 @@ interface ModalFooterProps {
const ModalFooter: React.FC<ModalFooterProps> = ({ onCancel, onOk, cancelLabel = "Cancel", okLabel = "Ok" }) => (
<div className="wave-modal-footer">
<Button theme="secondary" onClick={onCancel}>
{cancelLabel}
</Button>
<Button onClick={onOk}>{okLabel}</Button>
{onCancel && (
<Button theme="secondary" onClick={onCancel}>
{cancelLabel}
</Button>
)}
{onOk && <Button onClick={onOk}>{okLabel}</Button>}
</div>
);
+29 -9
View File
@@ -388,6 +388,25 @@
}
}
.screen-settings-modal {
width: 640px;
min-height: 329px;
.wave-modal-content {
gap: 24px;
.wave-modal-body {
display: flex;
padding: 0px 20px;
flex-direction: column;
align-items: flex-start;
gap: 4px;
align-self: stretch;
width: 100%;
}
}
}
.erconn-modal {
width: 502px;
min-height: 411px;
@@ -732,31 +751,32 @@
fill: @tab-pink;
}
.tab-colors {
.tab-colors,
.tab-icons {
display: flex;
flex-direction: row;
align-items: center;
.tab-color-sep {
.tab-color-sep,
.tab-icon-sep {
padding-left: 10px;
padding-right: 10px;
font-weight: bold;
}
.tab-color-cur {
width: 100px;
}
.tab-color-icon {
.tab-color-icon,
.tab-icon-icon {
width: 1.1em;
vertical-align: middle;
}
.tab-color-name {
.tab-color-name,
.tab-icon-name {
margin-left: 1em;
}
.tab-color-select {
.tab-color-select,
.tab-icon-select {
cursor: pointer;
margin: 5px;
&:hover {
+130 -97
View File
@@ -7,8 +7,16 @@ import * as mobx from "mobx";
import { boundMethod } from "autobind-decorator";
import { If, For } from "tsx-control-statements/components";
import cn from "classnames";
import { GlobalModel, GlobalCommandRunner, TabColors, MinFontSize, MaxFontSize } from "../../../model/model";
import { Toggle, InlineSettingsTextEdit, SettingsError, InfoMessage } from "../common";
import {
GlobalModel,
GlobalCommandRunner,
TabColors,
MinFontSize,
MaxFontSize,
TabIcons,
Screen,
} from "../../../model/model";
import { Toggle, InlineSettingsTextEdit, SettingsError, InfoMessage, Modal } from "../common";
import { LineType, RendererPluginType, ClientDataType, CommandRtnType } from "../../../types/types";
import { ConnectionDropdown } from "../../connections_deprecated/connections";
import { PluginModel } from "../../../plugins/plugins";
@@ -53,12 +61,13 @@ Are you sure you want to stop web-sharing this tab?
class ScreenSettingsModal extends React.Component<{ sessionId: string; screenId: string }, {}> {
shareCopied: OV<boolean> = mobx.observable.box(false, { name: "ScreenSettings-shareCopied" });
errorMessage: OV<string> = mobx.observable.box(null, { name: "ScreenSettings-errorMessage" });
screen: Screen;
constructor(props: any) {
super(props);
let { sessionId, screenId } = props;
let screen = GlobalModel.getScreenById(sessionId, screenId);
if (screen == null) {
this.screen = GlobalModel.getScreenById(sessionId, screenId);
if (this.screen == null) {
return;
}
}
@@ -84,6 +93,15 @@ class ScreenSettingsModal extends React.Component<{ sessionId: string; screenId:
commandRtnHandler(prtn, this.errorMessage);
}
@boundMethod
selectTabIcon(icon: string): void {
if (this.screen.getTabIcon() == icon) {
return;
}
let prtn = GlobalCommandRunner.screenSetSettings(this.screen.screenId, { tabicon: icon }, false);
util.commandRtnHandler(prtn, this.errorMessage);
}
@boundMethod
handleChangeArchived(val: boolean): void {
let { sessionId, screenId } = this.props;
@@ -203,108 +221,123 @@ class ScreenSettingsModal extends React.Component<{ sessionId: string; screenId:
render() {
let { sessionId, screenId } = this.props;
let inline = false;
let screen = GlobalModel.getScreenById(sessionId, screenId);
let screen = this.screen;
if (screen == null) {
return null;
}
console.log("screen.getTabIcon()", screen.getTabIcon());
let color: string = null;
let icon: string = null;
let curRemote = GlobalModel.getRemote(GlobalModel.getActiveScreen().getCurRemoteInstance().remoteid);
return (
<div className={cn("modal screen-settings-modal settings-modal prompt-modal is-active")}>
<div className="modal-background" />
<div className="modal-content">
{this.shareCopied.get() && <div className="copied-indicator" />}
<header>
<div className="modal-title">tab settings ({screen.name.get()})</div>
<div className="close-icon hoverEffect" title="Close (Escape)" onClick={this.closeModal}>
<XmarkIcon />
</div>
</header>
<div className="inner-content">
<div className="settings-field">
<div className="settings-label">Tab Id</div>
<div className="settings-input">{screen.screenId}</div>
</div>
<div className="settings-field">
<div className="settings-label">Name</div>
<div className="settings-input">
<InlineSettingsTextEdit
placeholder="name"
text={screen.name.get() ?? "(none)"}
value={screen.name.get() ?? ""}
onChange={this.inlineUpdateName}
maxLength={50}
showIcon={true}
/>
</div>
</div>
<div className="settings-field">
<div className="settings-label">Connection</div>
<div className="settings-input">
<ConnectionDropdown
curRemote={curRemote}
onSelectRemote={this.selectRemote}
allowNewConn={false}
/>
</div>
</div>
<div className="settings-field">
<div className="settings-label">Tab Color</div>
<div className="settings-input">
<div className="tab-colors">
<div className="tab-color-cur">
<SquareIcon className={cn("tab-color-icon", "color-" + screen.getTabColor())} />
<span className="tab-color-name">{screen.getTabColor()}</span>
</div>
<div className="tab-color-sep">|</div>
<For each="color" of={TabColors}>
<div
key={color}
className="tab-color-select"
onClick={() => this.selectTabColor(color)}
>
<SquareIcon className={cn("tab-color-icon", "color-" + color)} />
</div>
</For>
</div>
</div>
</div>
<div className="settings-field">
<div className="settings-label">
<div>Archived</div>
<InfoMessage width={400}>
Archive will hide the tab. Commands and output will be retained in history.
</InfoMessage>
</div>
<div className="settings-input">
<Toggle checked={screen.archived.get()} onChange={this.handleChangeArchived} />
</div>
</div>
<div className="settings-field">
<div className="settings-label">
<div>Actions</div>
<InfoMessage width={400}>
Delete will remove the tab, removing all commands and output from history.
</InfoMessage>
</div>
<div className="settings-input">
<div
onClick={this.handleDeleteScreen}
className="button is-prompt-danger is-outlined is-small"
>
Delete Tab
</div>
</div>
</div>
<SettingsError errorMessage={this.errorMessage} />
<Modal className="screen-settings-modal">
<Modal.Header onClose={this.closeModal} title={`tab settings (${screen.name.get()})`} />
<div className="wave-modal-body">
<div className="settings-field">
<div className="settings-label">Tab Id</div>
<div className="settings-input">{screen.screenId}</div>
</div>
<footer>
<div onClick={this.closeModal} className="button is-wave-green is-outlined is-small">
Close
<div className="settings-field">
<div className="settings-label">Name</div>
<div className="settings-input">
<InlineSettingsTextEdit
placeholder="name"
text={screen.name.get() ?? "(none)"}
value={screen.name.get() ?? ""}
onChange={this.inlineUpdateName}
maxLength={50}
showIcon={true}
/>
</div>
</footer>
</div>
<div className="settings-field">
<div className="settings-label">Connection</div>
<div className="settings-input">
<ConnectionDropdown
curRemote={curRemote}
onSelectRemote={this.selectRemote}
allowNewConn={false}
/>
</div>
</div>
<div className="settings-field">
<div className="settings-label">Tab Color</div>
<div className="settings-input">
<div className="tab-colors">
<div className="tab-color-cur">
<SquareIcon className={cn("tab-color-icon", "color-" + screen.getTabColor())} />
<span className="tab-color-name">{screen.getTabColor()}</span>
</div>
<div className="tab-color-sep">|</div>
<For each="color" of={TabColors}>
<div
key={color}
className="tab-color-select"
onClick={() => this.selectTabColor(color)}
>
<SquareIcon className={cn("tab-color-icon", "color-" + color)} />
</div>
</For>
</div>
</div>
</div>
<div className="settings-field">
<div className="settings-label">Tab Icon</div>
<div className="settings-input">
<div className="tab-icons">
<div className="tab-icon-cur">
<If condition={screen.getTabIcon() == "default"}>
<SquareIcon className={cn("tab-color-icon", "color-white")} />
</If>
<If condition={screen.getTabIcon() != "default"}>
<i className={`fa-sharp fa-solid fa-${screen.getTabIcon()}`}></i>
</If>
<span className="tab-icon-name">{screen.getTabIcon()}</span>
</div>
<div className="tab-icon-sep">|</div>
<For each="icon" of={TabIcons}>
<div
key={color}
className="tab-icon-select"
onClick={() => this.selectTabIcon(icon)}
>
<i className={`fa-sharp fa-solid fa-${icon}`}></i>
</div>
</For>
</div>
</div>
</div>
<div className="settings-field">
<div className="settings-label">
<div>Archived</div>
<InfoMessage width={400}>
Archive will hide the tab. Commands and output will be retained in history.
</InfoMessage>
</div>
<div className="settings-input">
<Toggle checked={screen.archived.get()} onChange={this.handleChangeArchived} />
</div>
</div>
<div className="settings-field">
<div className="settings-label">
<div>Actions</div>
<InfoMessage width={400}>
Delete will remove the tab, removing all commands and output from history.
</InfoMessage>
</div>
<div className="settings-input">
<div
onClick={this.handleDeleteScreen}
className="button is-prompt-danger is-outlined is-small"
>
Delete Tab
</div>
</div>
</div>
<SettingsError errorMessage={this.errorMessage} />
</div>
</div>
<Modal.Footer cancelLabel="Close" onCancel={this.closeModal} />
</Modal>
);
}
}
+8 -13
View File
@@ -11,24 +11,19 @@ import cn from "classnames";
import { debounce } from "throttle-debounce";
import dayjs from "dayjs";
import { GlobalCommandRunner, TabColors, TabIcons } from "../../../model/model";
import type { LineType, RenderModeType, LineFactoryProps, CommandRtnType } from "../../../types/types";
import type { LineType, RenderModeType, LineFactoryProps } from "../../../types/types";
import * as T from "../../../types/types";
import localizedFormat from "dayjs/plugin/localizedFormat";
import { InlineSettingsTextEdit, RemoteStatusLight, Button } from "../../common/common";
import { Button } from "../../common/common";
import { getRemoteStr } from "../../common/prompt/prompt";
import { GlobalModel, ScreenLines, Screen, Session } from "../../../model/model";
import { Line } from "../../line/linecomps";
import { LinesView } from "../../line/linesview";
import { ConnectionDropdown } from "../../connections_deprecated/connections";
import * as util from "../../../util/util";
import { TextField, InputDecoration } from "../../common/common";
import { TextField } from "../../common/common";
import { ReactComponent as EllipseIcon } from "../../assets/icons/ellipse.svg";
import { ReactComponent as Check12Icon } from "../../assets/icons/check12.svg";
import { ReactComponent as GlobeIcon } from "../../assets/icons/globe.svg";
import { ReactComponent as StatusCircleIcon } from "../../assets/icons/statuscircle.svg";
import { ReactComponent as ArrowsUpDownIcon } from "../../assets/icons/arrowsupdown.svg";
import { ReactComponent as CircleIcon } from "../../assets/icons/circle.svg";
import { ReactComponent as AddIcon } from "../../assets/icons/add.svg";
import { ReactComponent as SquareIcon } from "../../assets/icons/tab/square.svg";
import "./screenview.less";
@@ -439,12 +434,12 @@ class ScreenWindowView extends React.Component<{ session: Session; screen: Scree
/>
</If>
<If condition={screen.filterRunning.get()}>
<div className='filter-running'>
<div className="filter-running">
<Button
variant="outlined"
color="color-yellow"
style={{borderRadius: '999px'}}
onClick={this.disableFilter}
variant="outlined"
color="color-yellow"
style={{ borderRadius: "999px" }}
onClick={this.disableFilter}
>
Showing Running Commands &nbsp;
<i className="fa-sharp fa-solid fa-xmark" />
+1 -1
View File
@@ -11,7 +11,7 @@ import cn from "classnames";
import { debounce } from "throttle-debounce";
import dayjs from "dayjs";
import localizedFormat from "dayjs/plugin/localizedFormat";
import { GlobalModel, GlobalCommandRunner, Session, ScreenLines, Screen } from "../../../model/model";
import { GlobalModel, GlobalCommandRunner, Session, Screen, TabIcons } from "../../../model/model";
import { renderCmdText } from "../../common/common";
import { ReactComponent as SquareIcon } from "../../assets/icons/tab/square.svg";
import { ReactComponent as ActionsIcon } from "../../assets/icons/tab/actions.svg";
-13
View File
@@ -764,10 +764,6 @@ func ScreenSetCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (ss
}
if pk.Kwargs["tabicon"] != "" {
icon := pk.Kwargs["tabicon"]
err = validateIcon(icon, "screen tabicon")
if err != nil {
return nil, err
}
updateMap[sstore.ScreenField_TabIcon] = icon
varsUpdated = append(varsUpdated, "tabicon")
setNonAnchor = true
@@ -1993,15 +1989,6 @@ func validateColor(color string, typeStr string) error {
return fmt.Errorf("invalid %s, valid colors are: %s", typeStr, formatStrs(ColorNames, "or", false))
}
func validateIcon(icon string, typeStr string) error {
for _, c := range TabIcons {
if icon == c {
return nil
}
}
return fmt.Errorf("invalid %s, valid icons are: %s", typeStr, formatStrs(TabIcons, "or", false))
}
func validateRemoteColor(color string, typeStr string) error {
for _, c := range RemoteColorNames {
if color == c {