Archived
1
0
This repository has been archived on 2024-09-09. You can view files and clone it, but cannot push or open issues or pull requests.
code-server/packages/electron-browser/src/dialog.ts

195 lines
4.6 KiB
TypeScript
Raw Normal View History

2019-01-08 01:46:19 +01:00
import { IDisposable } from "@coder/disposable";
import { Emitter } from "@coder/emitter";
import "./dialog.scss";
/**
* Dialog options.
*/
export interface IDialogOptions {
message?: string;
detail?: string;
buttons?: string[];
input?: {
value: string;
selection?: {
start: number;
end: number;
};
};
}
export interface IDialogAction {
buttonIndex?: number;
key?: IKey;
}
/**
* Pressed keys.
*/
export enum IKey {
Enter = "Enter",
Escape = "Escape",
}
export class Dialog {
private options: IDialogOptions;
private overlay: HTMLElement;
private cachedActiveElement: HTMLElement;
private input: HTMLInputElement;
private actionEmitter: Emitter<IDialogAction>;
private errors: HTMLElement;
private buttons: HTMLElement[];
public constructor(options: IDialogOptions) {
this.options = options;
this.actionEmitter = new Emitter();
const msgBox = document.createElement("div");
msgBox.classList.add("msgbox");
if (this.options.message) {
const messageDiv = document.createElement("div");
messageDiv.classList.add("msg");
messageDiv.innerText = this.options.message;
msgBox.appendChild(messageDiv);
}
if (this.options.detail) {
const detailDiv = document.createElement("div");
detailDiv.classList.add("detail");
detailDiv.innerText = this.options.detail;
msgBox.appendChild(detailDiv);
}
if (this.options.input) {
msgBox.classList.add("input");
this.input = document.createElement("input");
this.input.classList.add("input");
this.input.value = this.options.input.value;
this.input.addEventListener("keydown", (event) => {
if (event.key === IKey.Enter) {
event.preventDefault();
this.actionEmitter.emit({
buttonIndex: undefined,
key: IKey.Enter,
});
}
});
msgBox.appendChild(this.input);
}
this.errors = document.createElement("div");
this.errors.classList.add("errors");
msgBox.appendChild(this.errors);
if (this.options.buttons && this.options.buttons.length > 0) {
this.buttons = this.options.buttons.map((buttonText, buttonIndex) => {
const button = document.createElement("button");
button.innerText = buttonText;
button.addEventListener("click", () => {
this.actionEmitter.emit({
buttonIndex,
key: undefined,
});
});
return button;
});
const buttonWrapper = document.createElement("div");
buttonWrapper.classList.add("button-wrapper");
this.buttons.forEach((b) => buttonWrapper.appendChild(b));
msgBox.appendChild(buttonWrapper);
}
this.overlay = document.createElement("div");
this.overlay.style.cssText = `display: flex; align-items: center; justify-content: center; top: 0; left: 0; right: 0; bottom: 0; z-index: 15; position: absolute; background: rgba(0, 0, 0, 0.4); opacity: 0; transition: 300ms opacity ease;`;
this.overlay.appendChild(msgBox);
setTimeout(() => {
this.overlay.style.opacity = "1";
});
}
/**
* Register a function to be called when the user performs an action.
*/
public onAction(callback: (action: IDialogAction) => void): IDisposable {
return this.actionEmitter.event(callback);
}
/**
* Input value if this dialog has an input.
*/
public get inputValue(): string {
return this.input ? this.input.value : undefined;
}
/**
* Display or remove an error.
*/
public set error(error: string) {
while (this.errors.lastChild) {
this.errors.removeChild(this.errors.lastChild);
}
if (error) {
const errorDiv = document.createElement("error");
errorDiv.innerText = error;
this.errors.appendChild(errorDiv);
}
}
/**
* Show the dialog.
*/
public show(): void {
if (!this.cachedActiveElement) {
this.cachedActiveElement = document.activeElement as HTMLElement;
document.body.appendChild(this.overlay);
document.addEventListener("keydown", this.onKeydown);
if (this.input) {
this.input.focus();
if (this.options.input.selection) {
this.input.setSelectionRange(
this.options.input.selection.start,
this.options.input.selection.end
);
}
} else if (this.buttons) {
this.buttons[0].focus();
}
}
}
/**
* Remove the dialog and clean up.
*/
public hide(): void {
if (this.cachedActiveElement) {
this.overlay.remove();
document.removeEventListener("keydown", this.onKeydown);
this.cachedActiveElement.focus();
this.cachedActiveElement = null;
}
}
/**
* Capture escape.
*/
private onKeydown = (event: KeyboardEvent): void => {
if (event.key === "Escape") {
event.preventDefault();
event.stopPropagation();
this.actionEmitter.emit({
buttonIndex: undefined,
key: IKey.Escape,
});
}
}
}