dialogBoxCreate now uses the same logic as other popups, now all popup can be dismissed with escape.

This commit is contained in:
Olivier Gagnon 2021-08-18 00:51:51 -04:00
parent 5c92360310
commit d6b349b6ff
6 changed files with 74 additions and 95 deletions

@ -7,7 +7,7 @@ import { Player } from "../Player";
import { redPillFlag } from "../RedPill"; import { redPillFlag } from "../RedPill";
import { GetServerByHostname } from "../Server/ServerHelpers"; import { GetServerByHostname } from "../Server/ServerHelpers";
import { Settings } from "../Settings/Settings"; import { Settings } from "../Settings/Settings";
import { dialogBoxCreate, dialogBoxOpened} from "../../utils/DialogBox"; import { dialogBoxCreate} from "../../utils/DialogBox";
import { Reviver } from "../../utils/JSONReviver"; import { Reviver } from "../../utils/JSONReviver";
//Sends message to player, including a pop up //Sends message to player, including a pop up
@ -59,12 +59,10 @@ function checkForMessagesToSend() {
} }
if (redpill && redpillOwned && Player.sourceFiles.length === 0 && !redPillFlag && !inMission) { if (redpill && redpillOwned && Player.sourceFiles.length === 0 && !redPillFlag && !inMission) {
if (!dialogBoxOpened) {
sendMessage(redpill, true); sendMessage(redpill, true);
}
} else if (redpill && redpillOwned) { } else if (redpill && redpillOwned) {
//If player has already destroyed a BitNode, message is not forced //If player has already destroyed a BitNode, message is not forced
if (!redPillFlag && !inMission && !dialogBoxOpened) { if (!redPillFlag && !inMission) {
sendMessage(redpill); sendMessage(redpill);
} }
} else if (jumper0 && !jumper0.recvd && Player.hacking_skill >= 25) { } else if (jumper0 && !jumper0.recvd && Player.hacking_skill >= 25) {

@ -3,15 +3,27 @@
* *
* Takes in a prop for rendering the content inside the popup * Takes in a prop for rendering the content inside the popup
*/ */
import * as React from "react"; import React, { useEffect } from "react";
interface IProps<T> { interface IProps<T> {
content: (props: T) => React.ReactElement; content: (props: T) => React.ReactElement;
id: string; id: string;
props: T; props: T;
removePopup: (id: string) => void;
} }
export function Popup<T>(props: IProps<T>): React.ReactElement { export function Popup<T>(props: IProps<T>): React.ReactElement {
function keyDown(event: KeyboardEvent): void {
if(event.key === 'Escape') props.removePopup(props.id);
}
useEffect(() => {
document.addEventListener('keydown', keyDown);
return () => {
document.removeEventListener('keydown', keyDown);
}
});
return ( return (
<div className={"popup-box-content"} id={`${props.id}-content`}> <div className={"popup-box-content"} id={`${props.id}-content`}>
{React.createElement(props.content, props.props)} {React.createElement(props.content, props.props)}

@ -16,6 +16,7 @@ import { removeElementById } from "../../../utils/uiHelpers/removeElementById";
let gameContainer: HTMLElement; let gameContainer: HTMLElement;
(function() {
function getGameContainer(): void { function getGameContainer(): void {
const container = document.getElementById("entire-game-container"); const container = document.getElementById("entire-game-container");
if (container == null) { if (container == null) {
@ -27,9 +28,15 @@ function getGameContainer(): void {
} }
document.addEventListener("DOMContentLoaded", getGameContainer); document.addEventListener("DOMContentLoaded", getGameContainer);
})();
// This variable is used to avoid setting the semi-transparent background
// several times on top of one another. Sometimes there's several popup at once.
let deepestPopupId: string = "";
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
export function createPopup<T>(id: string, rootComponent: (props: T) => React.ReactElement, props: T): HTMLElement | null { export function createPopup<T>(id: string, rootComponent: (props: T) => React.ReactElement, props: T): HTMLElement | null {
let container = document.getElementById(id); let container = document.getElementById(id);
if (container == null) { if (container == null) {
function onClick(this: HTMLElement, event: MouseEvent): any { function onClick(this: HTMLElement, event: MouseEvent): any {
@ -39,19 +46,20 @@ export function createPopup<T>(id: string, rootComponent: (props: T) => React.Re
if(clickedId !== id) return; if(clickedId !== id) return;
removePopup(id); removePopup(id);
} }
const backgroundColor = deepestPopupId === "" ? 'rgba(0,0,0,0.5)' : 'rgba(0,0,0,0)';
container = createElement("div", { container = createElement("div", {
class: "popup-box-container", class: "popup-box-container",
display: "flex", display: "flex",
id: id, id: id,
backgroundColor: 'rgba(0,0,0,0.5)', backgroundColor: backgroundColor,
clickListener: onClick, clickListener: onClick,
}); });
gameContainer.appendChild(container); gameContainer.appendChild(container);
} }
ReactDOM.render(<Popup content={rootComponent} id={id} props={props} />, container); if(deepestPopupId === "") deepestPopupId = id;
ReactDOM.render(<Popup content={rootComponent} id={id} props={props} removePopup={removePopup} />, container);
return container; return container;
} }
@ -67,4 +75,5 @@ export function removePopup(id: string): void {
removeElementById(id); removeElementById(id);
removeElementById(`${id}-close`); removeElementById(`${id}-close`);
if(id === deepestPopupId) deepestPopupId = "";
} }

@ -1,2 +0,0 @@
export function dialogBoxCreate(txt: string | JSX.Element, preformatted?: boolean): void;
export let dialogBoxOpened: boolean;

@ -1,76 +0,0 @@
import { KEY } from "./helpers/keyCodes";
import { DialogBox } from "./ui/DialogBox";
import React from "react";
import ReactDOM from "react-dom";
/**
* Create and display a pop-up dialog box.
* This dialog box does not allow for any interaction and should close when clicking
* outside of it
*/
let dialogBoxes = [];
// Close dialog box when clicking outside
$(document).click(function(event) {
if (dialogBoxOpened && dialogBoxes.length >= 1) {
if (!$(event.target).closest(dialogBoxes[0]).length){
closeTopmostDialogBox();
}
}
});
function closeTopmostDialogBox() {
if (!dialogBoxOpened || dialogBoxes.length === 0) return;
dialogBoxes[0].remove();
dialogBoxes.shift();
if (dialogBoxes.length == 0) {
dialogBoxOpened = false;
} else {
dialogBoxes[0].style.visibility = "visible";
}
}
// Dialog box close buttons
$(document).on('click', '.dialog-box-close-button', function() {
closeTopmostDialogBox();
});
document.addEventListener("keydown", function (event) {
if (event.keyCode == KEY.ESC && dialogBoxOpened) {
closeTopmostDialogBox();
event.preventDefault();
}
});
let dialogBoxOpened = false;
function dialogBoxCreate(txt, preformatted=false) {
const container = document.createElement("div");
container.setAttribute("class", "dialog-box-container");
let elem = txt;
if (typeof txt === 'string') {
if (preformatted) {
// For text files as they are often computed data that
// shouldn't be wrapped and should retain tabstops.
elem = <pre dangerouslySetInnerHTML={{ __html: txt }} />
} else {
elem = <p dangerouslySetInnerHTML={{ __html: txt.replace(/(?:\r\n|\r|\n)/g, '<br />') }} />
}
}
ReactDOM.render(DialogBox(elem), container);
document.body.appendChild(container);
if (dialogBoxes.length >= 1) {
container.style.visibility = "hidden";
}
dialogBoxes.push(container);
setTimeout(function() {
dialogBoxOpened = true;
}, 400);
}
export {dialogBoxCreate, dialogBoxOpened};

38
utils/DialogBox.tsx Normal file

@ -0,0 +1,38 @@
import { KEY } from "./helpers/keyCodes";
import { DialogBox } from "./ui/DialogBox";
import { createPopup } from "../src/ui/React/createPopup";
import { getRandomInt } from "./helpers/getRandomInt";
import React from "react";
import ReactDOM from "react-dom";
interface IProps {
content: JSX.Element;
}
export function MessagePopup(props: IProps): React.ReactElement {
return (<>{props.content}</>);
}
function dialogBoxCreate(txt: string | JSX.Element, preformatted: boolean = false): void {
const popupId = `popup-`+(Array.from(Array(16))).map(() => `${getRandomInt(0, 9)}`).join('');
if (typeof txt === 'string') {
if (preformatted) {
// For text files as they are often computed data that
// shouldn't be wrapped and should retain tabstops.
createPopup(popupId, MessagePopup, {
content: (<pre dangerouslySetInnerHTML={{ __html: txt }} />)
});
} else {
createPopup(popupId, MessagePopup, {
content: (<p dangerouslySetInnerHTML={{ __html: txt.replace(/(?:\r\n|\r|\n)/g, '<br />') }} />)
});
}
} else {
createPopup(popupId, MessagePopup, {
content: txt
});
}
}
export {dialogBoxCreate};