From b93b22a2234f2e78f85a31ed15530afd22aa3bfd Mon Sep 17 00:00:00 2001 From: Daan Vanden Bosch Date: Tue, 7 Jan 2020 17:40:01 +0100 Subject: [PATCH] Made Dialog reusable. --- src/core/gui/{ResultPopup.css => Dialog.css} | 30 ++++++--- src/core/gui/{ResultPopup.ts => Dialog.ts} | 63 +++++++++++-------- src/viewer/controllers/TextureController.ts | 4 +- .../model/ModelToolBarController.ts | 4 +- 4 files changed, 62 insertions(+), 39 deletions(-) rename src/core/gui/{ResultPopup.css => Dialog.css} (59%) rename src/core/gui/{ResultPopup.ts => Dialog.ts} (58%) diff --git a/src/core/gui/ResultPopup.css b/src/core/gui/Dialog.css similarity index 59% rename from src/core/gui/ResultPopup.css rename to src/core/gui/Dialog.css index 73e46030..80c780e2 100644 --- a/src/core/gui/ResultPopup.css +++ b/src/core/gui/Dialog.css @@ -1,4 +1,5 @@ -.core_ResultPopup { +.core_Dialog { + z-index: 20; display: flex; flex-direction: column; outline: none; @@ -9,34 +10,43 @@ box-shadow: black 0 0 10px -2px; } -.core_ResultPopup:focus-within { +.core_Dialog:focus-within { border: var(--border-focus); } -.core_ResultPopup h1 { +.core_Dialog h1 { font-size: 20px; margin: 0 0 10px 0; padding-bottom: 4px; border-bottom: var(--border); } -.core_ResultPopup_description { +.core_Dialog_description { user-select: text; cursor: text; } -.core_ResultPopup_body { +.core_Dialog_body { user-select: text; overflow: auto; margin: 4px 0; } -.core_ResultPopup_body ul { - cursor: text; -} - -.core_ResultPopup_footer { +.core_Dialog_footer { display: flex; flex-direction: row; justify-content: flex-end; } + +.core_Dialog_modal_overlay { + outline: none; + z-index: 10; + position: fixed; + top: 0; + bottom: 0; + left: 0; + right: 0; + background-color: black; + opacity: 50%; + backdrop-filter: blur(5px); +} diff --git a/src/core/gui/ResultPopup.ts b/src/core/gui/Dialog.ts similarity index 58% rename from src/core/gui/ResultPopup.ts rename to src/core/gui/Dialog.ts index 5ca0363d..0436aaa8 100644 --- a/src/core/gui/ResultPopup.ts +++ b/src/core/gui/Dialog.ts @@ -1,55 +1,62 @@ import { ResizableWidget } from "./ResizableWidget"; import { Widget } from "./Widget"; import { div, h1, li, section, ul } from "./dom"; -import { Problem, Result } from "../Result"; +import { Result } from "../Result"; import { Button } from "./Button"; -import "./ResultPopup.css"; +import "./Dialog.css"; -const POPUP_WIDTH = 500; -const POPUP_HEIGHT = 500; +const DIALOG_WIDTH = 500; +const DIALOG_MAX_HEIGHT = 500; -export class ResultPopup extends ResizableWidget { +export class Dialog extends ResizableWidget { private x = 0; private y = 0; private prev_mouse_x = 0; private prev_mouse_y = 0; + private readonly overlay_element: HTMLElement; readonly element: HTMLElement; readonly children: readonly Widget[] = []; readonly dismiss_button = this.disposable(new Button({ text: "Dismiss" })); - constructor(title: string, description: string, problems: readonly Problem[] = []) { + constructor(title: string, description: string, content: Node | string) { super(); let header_element: HTMLElement; this.element = section( - { className: "core_ResultPopup", tabIndex: 0 }, + { className: "core_Dialog", tabIndex: 0 }, (header_element = h1(title)), - div({ className: "core_ResultPopup_description" }, description), - div( - { className: "core_ResultPopup_body" }, - ul(...problems.map(problem => li(problem.ui_message))), - ), - div({ className: "core_ResultPopup_footer" }, this.dismiss_button.element), + div({ className: "core_Dialog_description" }, description), + div({ className: "core_Dialog_body" }, content), + div({ className: "core_Dialog_footer" }, this.dismiss_button.element), ); - this.element.style.width = `${POPUP_WIDTH}px`; - this.element.style.maxHeight = `${POPUP_HEIGHT}px`; + this.element.style.width = `${DIALOG_WIDTH}px`; + this.element.style.maxHeight = `${DIALOG_MAX_HEIGHT}px`; this.set_position( - (window.innerWidth - POPUP_WIDTH) / 2, - (window.innerHeight - POPUP_HEIGHT) / 2, + (window.innerWidth - DIALOG_WIDTH) / 2, + (window.innerHeight - DIALOG_MAX_HEIGHT) / 2, ); this.element.addEventListener("keydown", this.keydown); header_element.addEventListener("mousedown", this.mousedown); + this.overlay_element = div({ className: "core_Dialog_modal_overlay", tabIndex: -1 }); + this.overlay_element.addEventListener("focus", () => this.element.focus()); + document.body.append(this.overlay_element); + this.disposables(this.dismiss_button.onclick.observe(() => this.dispose())); this.finalize_construction(); } + dispose(): void { + super.dispose(); + this.overlay_element.remove(); + } + set_position(x: number, y: number): void { this.x = x; this.y = y; @@ -87,27 +94,33 @@ export class ResultPopup extends ResizableWidget { } /** - * Shows a popup if `result` failed or succeeded with problems. + * Shows a dialog window if `result` failed or succeeded with problems. * * @param result * @param problems_message - Message to show if problems occurred when result is successful. * @param error_message - Message to show if result failed. */ -export function show_result_popup( +export function show_result_dialog( result: Result, problems_message: string, error_message: string, ): void { - let popup: ResultPopup | undefined; + let dialog: Dialog | undefined; if (!result.success) { - popup = new ResultPopup("Error", error_message, result.problems); + dialog = new Dialog("Error", error_message, create_result_body(result)); } else if (result.problems.length) { - popup = new ResultPopup("Problems", problems_message, result.problems); + dialog = new Dialog("Problems", problems_message, create_result_body(result)); } - if (popup) { - document.body.append(popup.element); - popup.focus(); + if (dialog) { + document.body.append(dialog.element); + dialog.focus(); } } + +function create_result_body(result: Result): HTMLElement { + const body = ul(...result.problems.map(problem => li(problem.ui_message))); + body.style.cursor = "text"; + return body; +} diff --git a/src/viewer/controllers/TextureController.ts b/src/viewer/controllers/TextureController.ts index cb6b5b5f..9c2cfec8 100644 --- a/src/viewer/controllers/TextureController.ts +++ b/src/viewer/controllers/TextureController.ts @@ -11,8 +11,8 @@ import { list_property } from "../../core/observable"; import { ListProperty } from "../../core/observable/property/list/ListProperty"; import { prs_decompress } from "../../core/data_formats/compression/prs/decompress"; import { failure, Result, result_builder } from "../../core/Result"; -import { show_result_popup } from "../../core/gui/ResultPopup"; import { Severity } from "../../core/Severity"; +import { show_result_dialog } from "../../core/gui/Dialog"; const logger = LogManager.get("viewer/controllers/TextureController"); @@ -75,7 +75,7 @@ export class TextureController extends Controller { result = failure(); } - show_result_popup( + show_result_dialog( result, `Encountered some problems while opening "${file.name}".`, `Couldn't open "${file.name}".`, diff --git a/src/viewer/controllers/model/ModelToolBarController.ts b/src/viewer/controllers/model/ModelToolBarController.ts index 0def1c3c..571f963e 100644 --- a/src/viewer/controllers/model/ModelToolBarController.ts +++ b/src/viewer/controllers/model/ModelToolBarController.ts @@ -11,8 +11,8 @@ import { parse_afs } from "../../../core/data_formats/parsing/afs"; import { LogManager } from "../../../core/Logger"; import { prs_decompress } from "../../../core/data_formats/compression/prs/decompress"; import { failure, Result, result_builder, success } from "../../../core/Result"; -import { show_result_popup } from "../../../core/gui/ResultPopup"; import { Severity } from "../../../core/Severity"; +import { show_result_dialog } from "../../../core/gui/Dialog"; const logger = LogManager.get("viewer/controllers/model/ModelToolBarController"); @@ -136,7 +136,7 @@ export class ModelToolBarController extends Controller { result = failure(); } - show_result_popup( + show_result_dialog( result, `Encountered some problems while opening "${file.name}".`, `Couldn't open "${file.name}".`,