mirror of
https://github.com/wavetermdev/waveterm.git
synced 2024-12-21 16:38:23 +01:00
codeedit ui updates (#366)
* codeedit UI updates * deal with long messages, cleanup
This commit is contained in:
parent
089862d990
commit
adf83c73b1
@ -67,6 +67,11 @@
|
||||
}
|
||||
}
|
||||
|
||||
&.term-inline {
|
||||
padding: 2px 8px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
@ -22,6 +22,7 @@ interface ButtonProps {
|
||||
style?: React.CSSProperties;
|
||||
autoFocus?: boolean;
|
||||
className?: string;
|
||||
termInline?: boolean;
|
||||
}
|
||||
|
||||
class Button extends React.Component<ButtonProps> {
|
||||
@ -40,12 +41,31 @@ class Button extends React.Component<ButtonProps> {
|
||||
}
|
||||
|
||||
render() {
|
||||
const { leftIcon, rightIcon, theme, children, disabled, variant, color, style, autoFocus, className } =
|
||||
this.props;
|
||||
const {
|
||||
leftIcon,
|
||||
rightIcon,
|
||||
theme,
|
||||
children,
|
||||
disabled,
|
||||
variant,
|
||||
color,
|
||||
style,
|
||||
autoFocus,
|
||||
termInline,
|
||||
className,
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<button
|
||||
className={cn("wave-button", theme, variant, color, { disabled: disabled }, className)}
|
||||
className={cn(
|
||||
"wave-button",
|
||||
theme,
|
||||
variant,
|
||||
color,
|
||||
{ disabled: disabled },
|
||||
{ "term-inline": termInline },
|
||||
className
|
||||
)}
|
||||
onClick={this.handleClick}
|
||||
disabled={disabled}
|
||||
style={style}
|
||||
|
@ -61,6 +61,7 @@
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
padding-right: 5px;
|
||||
padding-left: 5px;
|
||||
}
|
||||
|
||||
div.icon {
|
||||
|
@ -45,24 +45,17 @@
|
||||
overflow: auto;
|
||||
}
|
||||
}
|
||||
section {
|
||||
// transition: height 0.3s ease-in-out;
|
||||
}
|
||||
.messageContainer {
|
||||
position: absolute;
|
||||
bottom: -3px;
|
||||
left: 14px;
|
||||
.message {
|
||||
background: var(--app-success-color);
|
||||
border-radius: 6px;
|
||||
margin-bottom: 1rem;
|
||||
padding: 4px 1rem;
|
||||
max-width: 80vw;
|
||||
&.error {
|
||||
background: var(--app-error-color);
|
||||
}
|
||||
|
||||
.code-message {
|
||||
text-wrap: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
color: var(--term-bright-green);
|
||||
&.error {
|
||||
color: var(--term-bright-red);
|
||||
}
|
||||
}
|
||||
|
||||
.readonly {
|
||||
position: absolute;
|
||||
top: 0.2em;
|
||||
@ -83,14 +76,38 @@
|
||||
.split-horizontal {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
height: calc(100% - var(--termlineheight) - 7px);
|
||||
border-bottom: 1px solid var(--app-border-color);
|
||||
}
|
||||
|
||||
.split-vertical {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.code-statusbar {
|
||||
display: flex;
|
||||
gap: var(--termpad);
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
font-size: var(--termfontsize);
|
||||
line-height: var(--termlineheight);
|
||||
background-color: var(--app-panel-bg-color);
|
||||
border-top: 1px solid var(--app-border-color);
|
||||
padding: 3px var(--termpad) 3px var(--termpad);
|
||||
height: calc(var(--termlineheight) + 11px);
|
||||
|
||||
.wave-button {
|
||||
line-height: var(--termlineheight) !important;
|
||||
text-wrap: nowrap;
|
||||
}
|
||||
|
||||
select.dropdown {
|
||||
max-width: 200px;
|
||||
}
|
||||
}
|
||||
|
||||
.gutter {
|
||||
flex-shrink: 0;
|
||||
flex-grow: 0;
|
||||
|
@ -4,11 +4,14 @@
|
||||
import * as React from "react";
|
||||
import Editor, { Monaco } from "@monaco-editor/react";
|
||||
import type * as MonacoTypes from "monaco-editor/esm/vs/editor/editor.api";
|
||||
import cn from "classnames";
|
||||
import { If } from "tsx-control-statements/components";
|
||||
import { Markdown } from "@/elements";
|
||||
import { GlobalModel, GlobalCommandRunner } from "@/models";
|
||||
import Split from "react-split-it";
|
||||
import loader from "@monaco-editor/loader";
|
||||
import { checkKeyPressed, adaptFromReactOrNativeKeyEvent } from "@/util/keyutil";
|
||||
import { Button, Dropdown } from "@/elements";
|
||||
|
||||
import "./code.less";
|
||||
|
||||
@ -57,7 +60,7 @@ class SourceCodeRenderer extends React.Component<
|
||||
isSave: boolean;
|
||||
isClosed: boolean;
|
||||
editorHeight: number;
|
||||
message: { status: string; text: string };
|
||||
message: { status: "success" | "error"; text: string };
|
||||
isPreviewerAvailable: boolean;
|
||||
showPreview: boolean;
|
||||
editorFraction: number;
|
||||
@ -71,13 +74,14 @@ class SourceCodeRenderer extends React.Component<
|
||||
static codeCache = new Map();
|
||||
|
||||
// which languages have preview options
|
||||
languagesWithPreviewer = ["markdown"];
|
||||
filePath;
|
||||
cacheKey;
|
||||
originalCode;
|
||||
monacoEditor: any; // reference to mounted monaco editor. TODO need the correct type
|
||||
markdownRef;
|
||||
syncing;
|
||||
languagesWithPreviewer: string[] = ["markdown", "mdx"];
|
||||
filePath: string;
|
||||
cacheKey: string;
|
||||
originalCode: string;
|
||||
monacoEditor: MonacoTypes.editor.IStandaloneCodeEditor; // reference to mounted monaco editor. TODO need the correct type
|
||||
markdownRef: React.RefObject<HTMLDivElement>;
|
||||
syncing: boolean;
|
||||
monacoOptions: MonacoTypes.editor.IEditorOptions & MonacoTypes.editor.IGlobalEditorOptions;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
@ -164,6 +168,10 @@ class SourceCodeRenderer extends React.Component<
|
||||
this.monacoEditor = editor;
|
||||
this.setInitialLanguage(editor);
|
||||
this.setEditorHeight();
|
||||
setTimeout(() => {
|
||||
let opts = this.getEditorOptions();
|
||||
editor.updateOptions(opts);
|
||||
}, 2000);
|
||||
editor.onKeyDown((e: MonacoTypes.IKeyboardEvent) => {
|
||||
let waveEvent = adaptFromReactOrNativeKeyEvent(e.browserEvent);
|
||||
if (checkKeyPressed(waveEvent, "Cmd:s") && this.state.isSave) {
|
||||
@ -232,8 +240,8 @@ class SourceCodeRenderer extends React.Component<
|
||||
}
|
||||
}
|
||||
|
||||
handleLanguageChange = (event) => {
|
||||
const selectedLanguage = event.target.value;
|
||||
handleLanguageChange = (e: any) => {
|
||||
const selectedLanguage = e.target.value;
|
||||
this.setState({
|
||||
selectedLanguage,
|
||||
isPreviewerAvailable: this.languagesWithPreviewer.includes(selectedLanguage),
|
||||
@ -320,7 +328,7 @@ class SourceCodeRenderer extends React.Component<
|
||||
let allowEditing = this.getAllowEditing();
|
||||
if (!allowEditing) {
|
||||
const noOfLines = Math.max(this.state.code.split("\n").length, 5);
|
||||
const lineHeight = Math.ceil(GlobalModel.getTermFontSize() * 1.5);
|
||||
const lineHeight = Math.ceil(GlobalModel.lineHeightEnv.lineHeight);
|
||||
_editorHeight = Math.min(noOfLines * lineHeight + 10, fullWindowHeight);
|
||||
}
|
||||
this.setState({ editorHeight: _editorHeight }, () => {
|
||||
@ -339,6 +347,27 @@ class SourceCodeRenderer extends React.Component<
|
||||
return !(this.props.readOnly || this.state.isClosed);
|
||||
}
|
||||
|
||||
updateEditorOpts(): void {
|
||||
if (!this.monacoEditor) {
|
||||
return;
|
||||
}
|
||||
let opts = this.getEditorOptions();
|
||||
this.monacoEditor.updateOptions(opts);
|
||||
}
|
||||
|
||||
getEditorOptions(): MonacoTypes.editor.IEditorOptions {
|
||||
let opts: MonacoTypes.editor.IEditorOptions = {
|
||||
scrollBeyondLastLine: false,
|
||||
fontSize: GlobalModel.getTermFontSize(),
|
||||
fontFamily: GlobalModel.getTermFontFamily(),
|
||||
readOnly: !this.getAllowEditing(),
|
||||
};
|
||||
if (this.state.showPreview) {
|
||||
opts.minimap = { enabled: false };
|
||||
}
|
||||
return opts;
|
||||
}
|
||||
|
||||
getCodeEditor = () => (
|
||||
<div style={{ maxHeight: this.props.opts.maxSize.height }}>
|
||||
{this.state.showReadonly && <div className="readonly">{"read-only"}</div>}
|
||||
@ -348,12 +377,7 @@ class SourceCodeRenderer extends React.Component<
|
||||
defaultLanguage={this.state.selectedLanguage}
|
||||
value={this.state.code}
|
||||
onMount={this.handleEditorDidMount}
|
||||
options={{
|
||||
scrollBeyondLastLine: false,
|
||||
fontSize: GlobalModel.getTermFontSize(),
|
||||
fontFamily: GlobalModel.getTermFontFamily(),
|
||||
readOnly: !this.getAllowEditing(),
|
||||
}}
|
||||
options={this.getEditorOptions()}
|
||||
onChange={this.handleEditorChange}
|
||||
/>
|
||||
</div>
|
||||
@ -375,22 +399,23 @@ class SourceCodeRenderer extends React.Component<
|
||||
togglePreview = () => {
|
||||
this.saveLineState({ showPreview: !this.state.showPreview });
|
||||
this.setState({ showPreview: !this.state.showPreview });
|
||||
setTimeout(() => this.updateEditorOpts(), 0);
|
||||
};
|
||||
|
||||
getEditorControls = () => {
|
||||
const { selectedLanguage, isSave, languages, isPreviewerAvailable, showPreview } = this.state;
|
||||
let allowEditing = this.getAllowEditing();
|
||||
return (
|
||||
<div className="buttonContainer">
|
||||
{isPreviewerAvailable && (
|
||||
<div className="button">
|
||||
<>
|
||||
<If condition={isPreviewerAvailable}>
|
||||
<Button theme="primary" termInline={true}>
|
||||
<div onClick={this.togglePreview} className={`preview`}>
|
||||
{`${showPreview ? "hide" : "show"} preview (`}
|
||||
{renderCmdText("P")}
|
||||
{`)`}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</Button>
|
||||
</If>
|
||||
<select className="dropdown" value={selectedLanguage} onChange={this.handleLanguageChange}>
|
||||
{languages.map((lang, index) => (
|
||||
<option key={index} value={lang}>
|
||||
@ -398,25 +423,23 @@ class SourceCodeRenderer extends React.Component<
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
{allowEditing && (
|
||||
<div className={`button ${isSave ? "" : "disabled"}`}>
|
||||
<If condition={allowEditing}>
|
||||
<Button theme="primary" termInline={true}>
|
||||
<div onClick={() => this.doSave()}>
|
||||
{`save (`}
|
||||
{renderCmdText("S")}
|
||||
{`)`}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{allowEditing && (
|
||||
<div className="button">
|
||||
</Button>
|
||||
<Button className="primary" termInline={true}>
|
||||
<div onClick={this.doClose} className={`close`}>
|
||||
{`close (`}
|
||||
{renderCmdText("D")}
|
||||
{`)`}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Button>
|
||||
</If>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@ -461,8 +484,16 @@ class SourceCodeRenderer extends React.Component<
|
||||
{this.getCodeEditor()}
|
||||
{isPreviewerAvailable && showPreview && this.getPreviewer()}
|
||||
</Split>
|
||||
{this.getEditorControls()}
|
||||
{message && this.getMessage()}
|
||||
<div className="flex-spacer" />
|
||||
<div className="code-statusbar">
|
||||
<If condition={message != null}>
|
||||
<div className={cn("code-message", { error: message.status == "error" })}>
|
||||
{this.state.message.text}
|
||||
</div>
|
||||
</If>
|
||||
<div className="flex-spacer" />
|
||||
{this.getEditorControls()}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user