Skip to content
Snippets Groups Projects
Verified Commit 98006e85 authored by João Magalhães's avatar João Magalhães :rocket:
Browse files

feat: added help panel

parent 38d7f957
No related branches found
No related tags found
No related merge requests found
Pipeline #1693 passed
...@@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ...@@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added ### Added
* Support for Ctrl+D (Speedup) and Ctrl+K (Keyboard toggle) shortcuts * Support for Ctrl+D (Speedup) and Ctrl+K (Keyboard toggle) shortcuts
* Help panel
### Changed ### Changed
......
import React, { FC, useEffect, useRef, useState } from "react"; import React, { FC, ReactNode, useEffect, useRef, useState } from "react";
import ReactDOM from "react-dom/client"; import ReactDOM from "react-dom/client";
declare const require: any; declare const require: any;
...@@ -12,6 +12,7 @@ import { ...@@ -12,6 +12,7 @@ import {
Display, Display,
DrawHandler, DrawHandler,
Footer, Footer,
Help,
Info, Info,
KeyboardChip8, KeyboardChip8,
KeyboardGB, KeyboardGB,
...@@ -66,6 +67,7 @@ export const EmulatorApp: FC<EmulatorAppProps> = ({ ...@@ -66,6 +67,7 @@ export const EmulatorApp: FC<EmulatorAppProps> = ({
const [keyaction, setKeyaction] = useState<string>(); const [keyaction, setKeyaction] = useState<string>();
const [modalTitle, setModalTitle] = useState<string>(); const [modalTitle, setModalTitle] = useState<string>();
const [modalText, setModalText] = useState<string>(); const [modalText, setModalText] = useState<string>();
const [modalContents, setModalContents] = useState<ReactNode>();
const [modalVisible, setModalVisible] = useState(false); const [modalVisible, setModalVisible] = useState(false);
const [toastText, setToastText] = useState<string>(); const [toastText, setToastText] = useState<string>();
const [toastError, setToastError] = useState(false); const [toastError, setToastError] = useState(false);
...@@ -213,17 +215,22 @@ export const EmulatorApp: FC<EmulatorAppProps> = ({ ...@@ -213,17 +215,22 @@ export const EmulatorApp: FC<EmulatorAppProps> = ({
const getBackground = () => backgrounds[backgroundIndex]; const getBackground = () => backgrounds[backgroundIndex];
const showModal = async ( const showModal = async (
text: string, title = "Alert",
title = "Alert" text?: string,
contents?: ReactNode
): Promise<boolean> => { ): Promise<boolean> => {
setModalText(text);
setModalTitle(title); setModalTitle(title);
setModalText(text);
setModalContents(contents);
setModalVisible(true); setModalVisible(true);
const result = (await new Promise((resolve) => { const result = (await new Promise((resolve) => {
modalCallbackRef.current = resolve; modalCallbackRef.current = resolve;
})) as boolean; })) as boolean;
return result; return result;
}; };
const showHelp = async (title = "Help") => {
await showModal(title, undefined, <Help />);
};
const showToast = async (text: string, error = false, timeout = 3500) => { const showToast = async (text: string, error = false, timeout = 3500) => {
setToastText(text); setToastText(text);
setToastError(error); setToastError(error);
...@@ -286,8 +293,8 @@ export const EmulatorApp: FC<EmulatorAppProps> = ({ ...@@ -286,8 +293,8 @@ export const EmulatorApp: FC<EmulatorAppProps> = ({
const onBenchmarkClick = async () => { const onBenchmarkClick = async () => {
if (!emulator.benchmark) return; if (!emulator.benchmark) return;
const result = await showModal( const result = await showModal(
"Are you sure you want to start a benchmark?\nThe benchmark is considered an expensive operation!", "Confirm",
"Confirm" "Are you sure you want to start a benchmark?\nThe benchmark is considered an expensive operation!"
); );
if (!result) return; if (!result) return;
const { delta, count, frequency_mhz } = emulator.benchmark(); const { delta, count, frequency_mhz } = emulator.benchmark();
...@@ -310,6 +317,9 @@ export const EmulatorApp: FC<EmulatorAppProps> = ({ ...@@ -310,6 +317,9 @@ export const EmulatorApp: FC<EmulatorAppProps> = ({
const onInformationClick = () => { const onInformationClick = () => {
setInfoVisible(!infoVisible); setInfoVisible(!infoVisible);
}; };
const onHelpClick = () => {
showHelp();
};
const onDebugClick = () => { const onDebugClick = () => {
setDebugVisible(!debugVisible); setDebugVisible(!debugVisible);
}; };
...@@ -378,6 +388,7 @@ export const EmulatorApp: FC<EmulatorAppProps> = ({ ...@@ -378,6 +388,7 @@ export const EmulatorApp: FC<EmulatorAppProps> = ({
<Modal <Modal
title={modalTitle} title={modalTitle}
text={modalText} text={modalText}
contents={modalContents}
visible={modalVisible} visible={modalVisible}
onConfirm={onModalConfirm} onConfirm={onModalConfirm}
onCancel={onModalCancel} onCancel={onModalCancel}
...@@ -621,6 +632,13 @@ export const EmulatorApp: FC<EmulatorAppProps> = ({ ...@@ -621,6 +632,13 @@ export const EmulatorApp: FC<EmulatorAppProps> = ({
style={["simple", "border", "padded"]} style={["simple", "border", "padded"]}
onClick={onInformationClick} onClick={onInformationClick}
/> />
<Button
text={"Help"}
image={require("../res/help.svg")}
imageAlt="help"
style={["simple", "border", "padded"]}
onClick={onHelpClick}
/>
{hasFeature(Feature.Debug) && ( {hasFeature(Feature.Debug) && (
<Button <Button
text={"Debug"} text={"Debug"}
......
.help > ul {
padding-left: 0px;
font-size: 18px;
line-height: 22px;
list-style: none;
margin: 0px 0px 0px 0px;
}
.help > ul > li {
margin: 10px 0px 4px 0px;
}
.help > ul .key {
border: 1px solid #ffffff;
display: inline-block;
padding: 0px 6px 0px 6px;
border-radius: 6px;
font-size: 16px;
}
import React, { FC } from "react";
import "./help.css";
type HelpProps = {
style?: string[];
};
export const Help: FC<HelpProps> = ({ style = [] }) => {
const classes = () => ["help", ...style].join(" ");
return (
<div className={classes()}>
<ul>
<li>
<span className="key">Enter</span> - Start
</li>
<li>
<span className="key">Space</span> - Select
</li>
<li>
<span className="key">A</span> - A
</li>
<li>
<span className="key">S</span> - B
</li>
<li>
<span className="key">Escape</span> - Exit fullscreen
</li>
<li>
<span className="key">Ctrl + D</span> - Turbo speed
</li>
<li>
<span className="key">Ctrl + F</span> - Toggle fullscreen
</li>
<li>
<span className="key">Ctrl + K</span> - Toggle on-screen
keyboard
</li>
</ul>
</div>
);
};
export default Help;
...@@ -5,6 +5,7 @@ export * from "./button-switch/button-switch"; ...@@ -5,6 +5,7 @@ export * from "./button-switch/button-switch";
export * from "./canvas/canvas"; export * from "./canvas/canvas";
export * from "./display/display"; export * from "./display/display";
export * from "./footer/footer"; export * from "./footer/footer";
export * from "./help/help";
export * from "./info/info"; export * from "./info/info";
export * from "./keyboard-chip8/keyboard-chip8"; export * from "./keyboard-chip8/keyboard-chip8";
export * from "./keyboard-gb/keyboard-gb"; export * from "./keyboard-gb/keyboard-gb";
......
import React, { FC, useEffect, useRef } from "react"; import React, { FC, ReactNode, useEffect, useRef } from "react";
import Button from "../button/button"; import Button from "../button/button";
import "./modal.css"; import "./modal.css";
...@@ -8,6 +8,7 @@ declare const require: any; ...@@ -8,6 +8,7 @@ declare const require: any;
type ModalProps = { type ModalProps = {
title?: string; title?: string;
text?: string; text?: string;
contents?: ReactNode;
visible?: boolean; visible?: boolean;
buttons?: boolean; buttons?: boolean;
overlayClose?: boolean; overlayClose?: boolean;
...@@ -18,9 +19,10 @@ type ModalProps = { ...@@ -18,9 +19,10 @@ type ModalProps = {
export const Modal: FC<ModalProps> = ({ export const Modal: FC<ModalProps> = ({
title = "Alert", title = "Alert",
text = "Do you confirm the following operation?", text,
contents,
visible = false, visible = false,
buttons = true, buttons,
overlayClose = true, overlayClose = true,
style = [], style = [],
onConfirm, onConfirm,
...@@ -28,6 +30,10 @@ export const Modal: FC<ModalProps> = ({ ...@@ -28,6 +30,10 @@ export const Modal: FC<ModalProps> = ({
}) => { }) => {
const classes = () => const classes = () =>
["modal", visible ? "visible" : "", ...style].join(" "); ["modal", visible ? "visible" : "", ...style].join(" ");
text =
text ??
(contents ? undefined : "Do you confirm the following operation?");
buttons = buttons ?? (contents ? false : true);
const modalRef = useRef<HTMLDivElement>(null); const modalRef = useRef<HTMLDivElement>(null);
useEffect(() => { useEffect(() => {
const onKeyDown = (event: KeyboardEvent) => { const onKeyDown = (event: KeyboardEvent) => {
...@@ -45,9 +51,12 @@ export const Modal: FC<ModalProps> = ({ ...@@ -45,9 +51,12 @@ export const Modal: FC<ModalProps> = ({
modalRef.current?.focus(); modalRef.current?.focus();
} }
}, [visible]); }, [visible]);
const getTextHtml = (separator = /\n/g) => ({ const getTextHtml = (separator = /\n/g) =>
__html: text.replace(separator, "<br/>") text
}); ? {
__html: text.replace(separator, "<br/>")
}
: undefined;
const onWindowClick = ( const onWindowClick = (
event: React.MouseEvent<HTMLDivElement, MouseEvent> event: React.MouseEvent<HTMLDivElement, MouseEvent>
) => { ) => {
...@@ -73,10 +82,14 @@ export const Modal: FC<ModalProps> = ({ ...@@ -73,10 +82,14 @@ export const Modal: FC<ModalProps> = ({
/> />
</div> </div>
<h2 className="modal-title">{title}</h2> <h2 className="modal-title">{title}</h2>
<p {contents ? (
className="modal-text" contents
dangerouslySetInnerHTML={getTextHtml()} ) : (
></p> <p
className="modal-text"
dangerouslySetInnerHTML={getTextHtml()}
></p>
)}
{buttons && ( {buttons && (
<div className="modal-buttons"> <div className="modal-buttons">
<Button <Button
......
<svg role="img" xmlns="http://www.w3.org/2000/svg" width="48px" height="48px" viewBox="0 0 24 24" aria-labelledby="helpIconTitle" stroke="#ffffff" stroke-width="2" stroke-linecap="square" stroke-linejoin="miter" fill="none" color="#ffffff"> <title id="helpIconTitle">Help</title> <path d="M12 14C12 12 13.576002 11.6652983 14.1186858 11.1239516 14.663127 10.5808518 15 9.82976635 15 9 15 7.34314575 13.6568542 6 12 6 11.1040834 6 10.2998929 6.39272604 9.75018919 7.01541737 9.49601109 7.30334431 9.29624369 7.64043912 9.16697781 8.01061095"/> <line x1="12" y1="17" x2="12" y2="17"/> <circle cx="12" cy="12" r="10"/> </svg>
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment