mirror of
https://github.com/wavetermdev/waveterm.git
synced 2025-01-02 18:39:05 +01:00
Modal keybindings (#484)
* first draft at modal keybindings * added modal keybindings and inline settings text edit keybindings * added switch for keybindings in modal footer * removed logs * remove another console.log * fix userinput keybindings -- should be generic:cancel for 2nd one
This commit is contained in:
parent
923cf71e0a
commit
c0c53edb84
@ -8,6 +8,8 @@ import { boundMethod } from "autobind-decorator";
|
||||
import cn from "classnames";
|
||||
import { If } from "tsx-control-statements/components";
|
||||
import { checkKeyPressed, adaptFromReactOrNativeKeyEvent } from "@/util/keyutil";
|
||||
import { GlobalModel } from "@/models";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
|
||||
import "./inlinesettingstextedit.less";
|
||||
|
||||
@ -27,6 +29,11 @@ class InlineSettingsTextEdit extends React.Component<
|
||||
tempText: OV<string>;
|
||||
shouldFocus: boolean = false;
|
||||
inputRef: React.RefObject<any> = React.createRef();
|
||||
curId: string;
|
||||
|
||||
componentDidMount(): void {
|
||||
this.curId = uuidv4();
|
||||
}
|
||||
|
||||
componentDidUpdate(): void {
|
||||
if (this.shouldFocus) {
|
||||
@ -52,6 +59,7 @@ class InlineSettingsTextEdit extends React.Component<
|
||||
this.tempText = null;
|
||||
this.props.onChange(newText);
|
||||
})();
|
||||
this.unregisterKeybindings();
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
@ -60,24 +68,38 @@ class InlineSettingsTextEdit extends React.Component<
|
||||
this.isEditing.set(false);
|
||||
this.tempText = null;
|
||||
})();
|
||||
this.unregisterKeybindings();
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
handleKeyDown(e: any): void {
|
||||
let waveEvent = adaptFromReactOrNativeKeyEvent(e);
|
||||
if (checkKeyPressed(waveEvent, "Enter")) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
handleFocus() {
|
||||
this.registerKeybindings();
|
||||
}
|
||||
|
||||
registerKeybindings() {
|
||||
let keybindManager = GlobalModel.keybindManager;
|
||||
let domain = "inline-settings" + this.curId;
|
||||
keybindManager.registerKeybinding("mainview", domain, "generic:confirm", (waveEvent) => {
|
||||
this.confirmChange();
|
||||
return;
|
||||
}
|
||||
if (checkKeyPressed(waveEvent, "Escape")) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
return true;
|
||||
});
|
||||
keybindManager.registerKeybinding("mainview", domain, "generic:cancel", (waveEvent) => {
|
||||
this.cancelChange();
|
||||
return;
|
||||
}
|
||||
return;
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
unregisterKeybindings() {
|
||||
let domain = "inline-settings" + this.curId;
|
||||
GlobalModel.keybindManager.unregisterDomain(domain);
|
||||
}
|
||||
|
||||
handleBlur() {
|
||||
this.unregisterKeybindings();
|
||||
this.cancelChange();
|
||||
}
|
||||
|
||||
componentWillUnmount(): void {
|
||||
this.unregisterKeybindings();
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
@ -99,7 +121,8 @@ class InlineSettingsTextEdit extends React.Component<
|
||||
ref={this.inputRef}
|
||||
className="input"
|
||||
type="text"
|
||||
onKeyDown={this.handleKeyDown}
|
||||
onFocus={this.handleFocus.bind(this)}
|
||||
onBlur={this.handleBlur.bind(this)}
|
||||
placeholder={this.props.placeholder}
|
||||
onChange={this.handleChangeText}
|
||||
value={this.tempText.get()}
|
||||
|
@ -6,8 +6,11 @@ import * as mobx from "mobx";
|
||||
import { If } from "tsx-control-statements/components";
|
||||
import ReactDOM from "react-dom";
|
||||
import { Button } from "./button";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import { GlobalModel } from "@/models";
|
||||
|
||||
import "./modal.less";
|
||||
import { boundMethod } from "autobind-decorator";
|
||||
|
||||
interface ModalHeaderProps {
|
||||
onClose?: () => void;
|
||||
@ -30,10 +33,52 @@ interface ModalFooterProps {
|
||||
onOk?: () => void;
|
||||
cancelLabel?: string;
|
||||
okLabel?: string;
|
||||
keybindings?: boolean;
|
||||
}
|
||||
|
||||
const ModalFooter: React.FC<ModalFooterProps> = ({ onCancel, onOk, cancelLabel = "Cancel", okLabel = "Ok" }) => (
|
||||
class ModalKeybindings extends React.Component<{ onOk; onCancel }, {}> {
|
||||
curId: string;
|
||||
|
||||
@boundMethod
|
||||
componentDidMount(): void {
|
||||
this.curId = uuidv4();
|
||||
let domain = "modal-" + this.curId;
|
||||
let keybindManager = GlobalModel.keybindManager;
|
||||
if (this.props.onOk) {
|
||||
keybindManager.registerKeybinding("modal", domain, "generic:confirm", (waveEvent) => {
|
||||
this.props.onOk();
|
||||
return true;
|
||||
});
|
||||
}
|
||||
if (this.props.onCancel) {
|
||||
keybindManager.registerKeybinding("modal", domain, "generic:cancel", (waveEvent) => {
|
||||
this.props.onCancel();
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
componentWillUnmount(): void {
|
||||
GlobalModel.keybindManager.unregisterDomain("modal-" + this.curId);
|
||||
}
|
||||
|
||||
render(): React.ReactNode {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
const ModalFooter: React.FC<ModalFooterProps> = ({
|
||||
onCancel,
|
||||
onOk,
|
||||
cancelLabel = "Cancel",
|
||||
okLabel = "Ok",
|
||||
keybindings = true,
|
||||
}) => (
|
||||
<div className="wave-modal-footer">
|
||||
<If condition={keybindings}>
|
||||
<ModalKeybindings onOk={onOk} onCancel={onCancel}></ModalKeybindings>
|
||||
</If>
|
||||
{onCancel && (
|
||||
<Button className="secondary" onClick={onCancel}>
|
||||
{cancelLabel}
|
||||
@ -79,4 +124,4 @@ class Modal extends React.Component<ModalProps> {
|
||||
}
|
||||
}
|
||||
|
||||
export { Modal };
|
||||
export { Modal, ModalKeybindings };
|
||||
|
@ -18,6 +18,8 @@ interface TextFieldProps {
|
||||
className?: string;
|
||||
onChange?: (value: string) => void;
|
||||
onKeyDown?: (event: React.KeyboardEvent<HTMLInputElement>) => void;
|
||||
onFocus?: () => void;
|
||||
onBlur?: () => void;
|
||||
placeholder?: string;
|
||||
defaultValue?: string;
|
||||
decoration?: TextFieldDecorationProps;
|
||||
@ -78,6 +80,9 @@ class TextField extends React.Component<TextFieldProps, TextFieldState> {
|
||||
@boundMethod
|
||||
handleFocus() {
|
||||
this.setState({ focused: true });
|
||||
if (this.props.onFocus) {
|
||||
this.props.onFocus();
|
||||
}
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
@ -91,6 +96,9 @@ class TextField extends React.Component<TextFieldProps, TextFieldState> {
|
||||
this.setState({ error: false, focused: false });
|
||||
}
|
||||
}
|
||||
if (this.props.onBlur) {
|
||||
this.props.onBlur();
|
||||
}
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
|
@ -9,6 +9,7 @@ import { Markdown, Modal, Button, Checkbox } from "@/elements";
|
||||
import { GlobalModel, GlobalCommandRunner } from "@/models";
|
||||
|
||||
import "./alert.less";
|
||||
import { ModalKeybindings } from "../elements/modal";
|
||||
|
||||
@mobxReact.observer
|
||||
class AlertModal extends React.Component<{}, {}> {
|
||||
@ -54,6 +55,7 @@ class AlertModal extends React.Component<{}, {}> {
|
||||
</div>
|
||||
<div className="wave-modal-footer">
|
||||
<If condition={isConfirm}>
|
||||
<ModalKeybindings onOk={this.handleOK} onCancel={this.closeModal}></ModalKeybindings>
|
||||
<Button className="secondary" onClick={this.closeModal}>
|
||||
Cancel
|
||||
</Button>
|
||||
@ -62,6 +64,7 @@ class AlertModal extends React.Component<{}, {}> {
|
||||
</Button>
|
||||
</If>
|
||||
<If condition={!isConfirm}>
|
||||
<ModalKeybindings onOk={this.handleOK} onCancel={null}></ModalKeybindings>
|
||||
<Button autoFocus={true} onClick={this.handleOK}>
|
||||
Ok
|
||||
</Button>
|
||||
|
@ -2,7 +2,7 @@ import * as React from "react";
|
||||
import { GlobalModel } from "@/models";
|
||||
import { Choose, When, If } from "tsx-control-statements/components";
|
||||
import { Modal, PasswordField, TextField, Markdown, Checkbox } from "@/elements";
|
||||
import { checkKeyPressed, adaptFromReactOrNativeKeyEvent } from "@/util/keyutil";
|
||||
import { checkKeyPressed, adaptFromReactOrNativeKeyEvent, KeybindManager } from "@/util/keyutil";
|
||||
|
||||
import "./userinput.less";
|
||||
|
||||
@ -44,17 +44,20 @@ export const UserInputModal = (userInputRequest: UserInputRequest) => {
|
||||
[userInputRequest]
|
||||
);
|
||||
|
||||
function handleTextKeyDown(e: React.KeyboardEvent<HTMLInputElement>) {
|
||||
let waveEvent = adaptFromReactOrNativeKeyEvent(e);
|
||||
if (checkKeyPressed(waveEvent, "Enter")) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
function handleTextFocus() {
|
||||
let keybindManager = GlobalModel.keybindManager;
|
||||
keybindManager.registerKeybinding("modal", "userinput", "generic:confirm", (waveEvent) => {
|
||||
handleSendText();
|
||||
} else if (checkKeyPressed(waveEvent, "Escape")) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
return true;
|
||||
});
|
||||
keybindManager.registerKeybinding("modal", "userinput", "generic:cancel", (waveEvent) => {
|
||||
handleSendCancel();
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
function handleTextBlur() {
|
||||
GlobalModel.keybindManager.unregisterDomain("userinput");
|
||||
}
|
||||
|
||||
React.useEffect(() => {
|
||||
@ -89,7 +92,8 @@ export const UserInputModal = (userInputRequest: UserInputRequest) => {
|
||||
value={responseText}
|
||||
maxLength={400}
|
||||
autoFocus={true}
|
||||
onKeyDown={(e) => handleTextKeyDown(e)}
|
||||
onFocus={() => handleTextFocus()}
|
||||
onBlur={() => handleTextBlur()}
|
||||
/>
|
||||
</If>
|
||||
<If condition={!userInputRequest.publictext}>
|
||||
@ -98,7 +102,8 @@ export const UserInputModal = (userInputRequest: UserInputRequest) => {
|
||||
value={responseText}
|
||||
maxLength={400}
|
||||
autoFocus={true}
|
||||
onKeyDown={(e) => handleTextKeyDown(e)}
|
||||
onFocus={() => handleTextFocus()}
|
||||
onBlur={() => handleTextBlur()}
|
||||
/>
|
||||
</If>
|
||||
</If>
|
||||
|
@ -14,6 +14,7 @@ import * as textmeasure from "@/util/textmeasure";
|
||||
import * as appconst from "@/app/appconst";
|
||||
|
||||
import "./viewremoteconndetail.less";
|
||||
import { ModalKeybindings } from "../elements/modal";
|
||||
|
||||
@mobxReact.observer
|
||||
class ViewRemoteConnDetailModal extends React.Component<{}, {}> {
|
||||
@ -382,6 +383,20 @@ class ViewRemoteConnDetailModal extends React.Component<{}, {}> {
|
||||
</div>
|
||||
</div>
|
||||
<div className="wave-modal-footer">
|
||||
<ModalKeybindings
|
||||
onOk={() => {
|
||||
if (selectedRemoteStatus == "connecting") {
|
||||
return;
|
||||
}
|
||||
this.handleClose();
|
||||
}}
|
||||
onCancel={() => {
|
||||
if (selectedRemoteStatus == "connecting") {
|
||||
return;
|
||||
}
|
||||
this.handleClose();
|
||||
}}
|
||||
></ModalKeybindings>
|
||||
<Button
|
||||
className="secondary"
|
||||
disabled={selectedRemoteStatus == "connecting"}
|
||||
|
@ -476,23 +476,6 @@ class Model {
|
||||
if (isModKeyPress(e)) {
|
||||
return;
|
||||
}
|
||||
if (this.alertMessage.get() != null) {
|
||||
if (checkKeyPressed(waveEvent, "Escape")) {
|
||||
e.preventDefault();
|
||||
this.modalsModel.popModal(() => this.cancelAlert());
|
||||
return;
|
||||
}
|
||||
if (checkKeyPressed(waveEvent, "Enter")) {
|
||||
e.preventDefault();
|
||||
this.confirmAlert();
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (checkKeyPressed(waveEvent, "Escape") && this.modalsModel.store.length > 0) {
|
||||
this.modalsModel.popModal();
|
||||
return;
|
||||
}
|
||||
if (this.keybindManager.processKeyEvent(e, waveEvent)) {
|
||||
return;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user