diff --git a/examples/web/react/app.tsx b/examples/web/react/app.tsx index 723b29503999db268962174b6e981e03bcd6fe53..82446e8e1e44c3296e3a6365df4ef3d051a89fec 100644 --- a/examples/web/react/app.tsx +++ b/examples/web/react/app.tsx @@ -166,13 +166,82 @@ export const App: FC<AppProps> = ({ emulator, backgrounds = ["264653"] }) => { const [backgroundIndex, setBackgroundIndex] = useState(0); const [romInfo, setRomInfo] = useState<RomInfo>({}); const [framerate, setFramerate] = useState(0); - const [keyaction, setKeyaction] = useState<string | null>(null); + const [keyaction, setKeyaction] = useState<string>(); + const [modalVisible, setModalVisible] = useState(false); + const [modalTitle, setModalTitle] = useState<string>(); + const [modalText, setModalText] = useState<string>(); + const frameRef = useRef<boolean>(false); const errorRef = useRef<boolean>(false); + const modalCallbackRef = + useRef<(value: boolean | PromiseLike<boolean>) => void>(); + + useEffect(() => { + document.body.style.backgroundColor = `#${getBackground()}`; + }, [backgroundIndex]); + useEffect(() => { + switch (keyaction) { + case "Escape": + setFullscreen(false); + setKeyaction(undefined); + break; + case "Fullscreen": + setFullscreen(!fullscreen); + setKeyaction(undefined); + break; + } + }, [keyaction]); + useEffect(() => { + document.addEventListener("keydown", (event) => { + if (event.key === "Escape") { + setKeyaction("Escape"); + event.stopPropagation(); + event.preventDefault(); + } + if (event.key === "f" && event.ctrlKey === true) { + setKeyaction("Fullscreen"); + event.stopPropagation(); + event.preventDefault(); + } + }); + emulator.bind("booted", () => { + const romInfo = emulator.getRomInfo(); + setRomInfo(romInfo); + }); + }, []); + const getPauseText = () => (paused ? "Resume" : "Pause"); const getPauseIcon = () => paused ? require("../res/play.svg") : require("../res/pause.svg"); const getBackground = () => backgrounds[backgroundIndex]; + + const showModal = async ( + text: string, + title = "Alert" + ): Promise<boolean> => { + setModalText(text); + setModalTitle(title); + setModalVisible(true); + const result = (await new Promise((resolve) => { + modalCallbackRef.current = resolve; + })) as boolean; + return result; + }; + + const onModalConfirm = () => { + if (modalCallbackRef.current) { + modalCallbackRef.current(true); + modalCallbackRef.current = undefined; + } + setModalVisible(false); + }; + const onModalCancel = () => { + if (modalCallbackRef.current) { + modalCallbackRef.current(false); + modalCallbackRef.current = undefined; + } + setModalVisible(false); + }; const onPauseClick = () => { emulator.toggleRunning(); setPaused(!paused); @@ -180,6 +249,12 @@ export const App: FC<AppProps> = ({ emulator, backgrounds = ["264653"] }) => { const onResetClick = () => { emulator.reset(); }; + const onBenchmarkClick = async () => { + const result = await showModal( + "Are you sure you want to start a benchmark?\nThe benchmark is considered an expensive operation!", + "Confirm" + ); + }; const onFullscreenClick = () => { setFullscreen(!fullscreen); }; @@ -204,43 +279,16 @@ export const App: FC<AppProps> = ({ emulator, backgrounds = ["264653"] }) => { await handler(undefined, require("../res/storm.png"), 0.2); }); }; - const onKeyDown = (event: KeyboardEvent) => {}; - useEffect(() => { - document.body.style.backgroundColor = `#${getBackground()}`; - }, [backgroundIndex]); - useEffect(() => { - switch (keyaction) { - case "Escape": - setFullscreen(false); - setKeyaction(null); - break; - case "Fullscreen": - setFullscreen(!fullscreen); - setKeyaction(null); - break; - } - }, [keyaction]); - useEffect(() => { - document.addEventListener("keydown", (event) => { - if (event.key === "Escape") { - setKeyaction("Escape"); - event.stopPropagation(); - event.preventDefault(); - } - if (event.key === "f" && event.ctrlKey === true) { - setKeyaction("Fullscreen"); - event.stopPropagation(); - event.preventDefault(); - } - }); - emulator.bind("booted", () => { - const romInfo = emulator.getRomInfo(); - setRomInfo(romInfo); - }); - }, []); + return ( <div className="app"> - <Modal /> + <Modal + visible={modalVisible} + title={modalTitle} + text={modalText} + onConfirm={onModalConfirm} + onCancel={onModalCancel} + /> <Footer color={getBackground()}> Built with â¤ï¸ by{" "} <Link href="https://joao.me" target="_blank"> @@ -313,6 +361,12 @@ export const App: FC<AppProps> = ({ emulator, backgrounds = ["264653"] }) => { imageAlt="reset" onClick={onResetClick} /> + <Button + text={"Benchmark"} + image={require("../res/bolt.svg")} + imageAlt="benchmark" + onClick={onBenchmarkClick} + /> <Button text={"Fullscreen"} image={require("../res/maximise.svg")} @@ -322,7 +376,7 @@ export const App: FC<AppProps> = ({ emulator, backgrounds = ["264653"] }) => { <Button text={"Theme"} image={require("../res/marker.svg")} - imageAlt="marker" + imageAlt="theme" onClick={onThemeClick} /> </ButtonContainer> diff --git a/examples/web/react/components/display/display.tsx b/examples/web/react/components/display/display.tsx index e2e82cd76f3460eb1f0df68b86ebc9357c0a7590..818ed142d4dd5dbbab06f7b4607398716bc6d9f3 100644 --- a/examples/web/react/components/display/display.tsx +++ b/examples/web/react/components/display/display.tsx @@ -66,10 +66,10 @@ export const Display: FC<DisplayProps> = ({ const classes = () => ["display", fullscreen ? "fullscreen" : null, size, ...style].join(" "); - const [width, setWidth] = useState<number | undefined>(undefined); - const [height, setHeight] = useState<number | undefined>(undefined); + const [width, setWidth] = useState<number>(); + const [height, setHeight] = useState<number>(); const canvasRef = useRef<HTMLCanvasElement>(null); - const canvasContentsRef = useRef<CanvasContents | undefined>(undefined); + const canvasContentsRef = useRef<CanvasContents>(); const resizeRef = useRef(() => { const [fullWidth, fullHeight] = crop(options.width / options.height); setWidth(fullWidth); diff --git a/examples/web/react/components/modal/modal.tsx b/examples/web/react/components/modal/modal.tsx index 4b601b58e2a06765c7f681cbf3573bff0a955454..1eac3e9a4daabff7cba5a43cf511d6c8e235fead 100644 --- a/examples/web/react/components/modal/modal.tsx +++ b/examples/web/react/components/modal/modal.tsx @@ -1,4 +1,4 @@ -import React, { ReactNode, FC, ButtonHTMLAttributes } from "react"; +import React, { ReactNode, FC, ButtonHTMLAttributes, useEffect } from "react"; import Button from "../button/button"; import "./modal.css"; @@ -10,16 +10,27 @@ type ModalProps = { text?: string; visible?: boolean; style?: string[]; + onConfirm?: () => void; + onCancel?: () => void; }; export const Modal: FC<ModalProps> = ({ title = "Alert", text = "Do you confirm the following operation?", visible = false, - style = [] + style = [], + onConfirm, + onCancel }) => { const classes = () => ["modal", visible ? "visible" : "", ...style].join(" "); + useEffect(() => { + document.addEventListener("keydown", (event) => { + if (event.key === "Escape") { + onCancel && onCancel(); + } + }); + }, []); return ( <div className={classes()}> <div className="modal-window"> @@ -29,6 +40,7 @@ export const Modal: FC<ModalProps> = ({ size={"medium"} style={["simple", "rounded", "no-text"]} image={require("./close.svg")} + onClick={onCancel} /> </div> <h2 className="modal-title">{title}</h2> @@ -38,11 +50,13 @@ export const Modal: FC<ModalProps> = ({ text={"Cancel"} size={"medium"} style={["simple", "red", "border", "padded-large"]} + onClick={onCancel} /> <Button text={"Confirm"} size={"medium"} style={["simple", "border", "padded-large"]} + onClick={onConfirm} /> </div> </div>