waveterm/src/elements.tsx
2023-04-03 15:53:28 -07:00

198 lines
6.7 KiB
TypeScript

import * as React from "react";
import * as mobxReact from "mobx-react";
import * as mobx from "mobx";
import {sprintf} from "sprintf-js";
import {boundMethod} from "autobind-decorator";
import cn from "classnames";
import {If, For, When, Otherwise, Choose} from "tsx-control-statements/components";
import type {RemoteType} from "./types";
type OV<V> = mobx.IObservableValue<V>;
function renderCmdText(text : string) : any {
return <span>&#x2318;{text}</span>;
}
class CmdStrCode extends React.Component<{cmdstr : string, onUse : () => void, onCopy : () => void, isCopied : boolean, fontSize : "normal" | "large", limitHeight : boolean}, {}> {
@boundMethod
handleUse(e : any) {
e.stopPropagation();
if (this.props.onUse != null) {
this.props.onUse()
}
}
@boundMethod
handleCopy(e : any) {
e.stopPropagation();
if (this.props.onCopy != null) {
this.props.onCopy();
}
}
render() {
let {isCopied, cmdstr, fontSize, limitHeight} = this.props;
return (
<div className={cn("cmdstr-code", {"is-large": (fontSize == "large")}, {"limit-height": limitHeight})}>
<If condition={isCopied}>
<div key="copied" className="copied-indicator">
<div>copied</div>
</div>
</If>
<div key="use" className="use-button" title="Use Command" onClick={this.handleUse}><i className="fa-sharp fa-solid fa-check"/></div>
<div key="code" className="code-div">
<code>{cmdstr}</code>
</div>
<div key="copy" className="copy-control">
<div className="inner-copy" onClick={this.handleCopy}>
<i title="copy" className="fa-sharp fa-regular fa-copy"/>
</div>
</div>
</div>
);
}
}
class Toggle extends React.Component<{checked : boolean, onChange : (value : boolean) => void}, {}> {
@boundMethod
handleChange(e : any) : void {
let {onChange} = this.props;
if (onChange != null) {
onChange(e.target.checked);
}
}
render() {
return (
<label className="checkbox-toggle">
<input type="checkbox" checked={this.props.checked} onChange={this.handleChange}/>
<span className="slider"/>
</label>
);
}
}
@mobxReact.observer
class RemoteStatusLight extends React.Component<{remote : RemoteType}, {}> {
render() {
let remote = this.props.remote;
let status = "error";
let wfp = false;
if (remote != null) {
status = remote.status;
wfp = remote.waitingforpassword;
}
let icon = "fa-sharp fa-solid fa-circle"
if (status == "connecting") {
icon = (wfp ? "fa-sharp fa-solid fa-key" : "fa-sharp fa-solid fa-rotate");
}
return (
<i className={cn("remote-status", icon, "status-" + status)}/>
);
}
}
@mobxReact.observer
class InlineSettingsTextEdit extends React.Component<{text : string, value : string, onChange : (val : string) => void, maxLength : number, placeholder : string}, {}> {
isEditing : OV<boolean> = mobx.observable.box(false, {name: "inlineedit-isEditing"});
tempText : OV<string>;
@boundMethod
handleChangeText(e : any) : void {
mobx.action(() => {
this.tempText.set(e.target.value);
})();
}
@boundMethod
confirmChange() : void {
mobx.action(() => {
let newText = this.tempText.get();
this.isEditing.set(false);
this.tempText = null;
this.props.onChange(newText);
})();
}
@boundMethod
cancelChange() : void {
mobx.action(() => {
this.isEditing.set(false);
this.tempText = null;
})();
}
@boundMethod
handleKeyDown(e : any) : void {
if (e.code == "Enter") {
e.preventDefault();
e.stopPropagation();
this.confirmChange();
return;
}
if (e.code == "Escape") {
e.preventDefault();
e.stopPropagation();
this.cancelChange();
return;
}
return;
}
@boundMethod
clickEdit() : void {
mobx.action(() => {
this.isEditing.set(true);
this.tempText = mobx.observable.box(this.props.value, {name: "inlineedit-tempText"});
})();
}
render() {
if (this.isEditing.get()) {
return (
<div className={cn("settings-input inline-edit", "edit-active")}>
<div className="field has-addons">
<div className="control">
<input className="input" type="text" onKeyDown={this.handleKeyDown} placeholder={this.props.placeholder} onChange={this.handleChangeText} value={this.tempText.get()} maxLength={this.props.maxLength}/>
</div>
<div className="control">
<div onClick={this.cancelChange} title="Cancel (Esc)" className="button is-prompt-danger is-outlined is-small"><span className="icon is-small"><i className="fa-sharp fa-solid fa-xmark"/></span></div>
</div>
<div className="control">
<div onClick={this.confirmChange} title="Confirm (Enter)" className="button is-prompt-green is-outlined is-small"><span className="icon is-small"><i className="fa-sharp fa-solid fa-check"/></span></div>
</div>
</div>
</div>
);
}
else {
return (
<div onClick={this.clickEdit} className={cn("settings-input inline-edit", "edit-not-active")}>
{this.props.text}
</div>
);
}
}
}
@mobxReact.observer
class InfoMessage extends React.Component<{width : number, children : React.ReactNode}> {
render() {
return (
<div className="info-message">
<div className="message-icon">
<i className="fa-sharp fa-solid fa-circle-info"/>
</div>
<div className="message-content" style={{width: this.props.width}}>
<div className="info-icon">
<i className="fa-sharp fa-solid fa-circle-info"/>
</div>
<div className="info-children">{this.props.children}</div>
</div>
</div>
);
}
}
export {CmdStrCode, Toggle, renderCmdText, RemoteStatusLight, InlineSettingsTextEdit, InfoMessage};