Added calculator app
This commit is contained in:
parent
5e26c27980
commit
d2784afba0
4 changed files with 288 additions and 16 deletions
|
|
@ -1,7 +1,207 @@
|
|||
// import styles from "./Calculator.module.css";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import { Button } from "../../_utils/button/Button.jsx";
|
||||
import styles from "./Calculator.module.css";
|
||||
|
||||
export function Calculator() {
|
||||
return (
|
||||
<div></div>
|
||||
);
|
||||
/** @type {import("../../windows/WindowView.jsx").windowProps} */
|
||||
export function Calculator({ active }) {
|
||||
const [input, setInput] = useState("0");
|
||||
const [firstNumber, setFirstNumber] = useState(null);
|
||||
const [secondNumber, setSecondNumber] = useState(null);
|
||||
const [operation, setOperation] = useState(null);
|
||||
const [isIntermediate, setIsIntermediate] = useState(false);
|
||||
|
||||
const log = useCallback(() => {
|
||||
console.info({ input, firstNumber, secondNumber, operation });
|
||||
}, [firstNumber, input, operation, secondNumber]);
|
||||
|
||||
const reset = useCallback(() => {
|
||||
setInput("0");
|
||||
setFirstNumber(null);
|
||||
setSecondNumber(null);
|
||||
setOperation(null);
|
||||
|
||||
log();
|
||||
}, [log]);
|
||||
|
||||
const addInput = useCallback((string) => {
|
||||
let hasReset = false;
|
||||
if (secondNumber != null) {
|
||||
if (isIntermediate) {
|
||||
setFirstNumber(input);
|
||||
setSecondNumber(null);
|
||||
setInput(null);
|
||||
} else {
|
||||
reset();
|
||||
}
|
||||
hasReset = true;
|
||||
}
|
||||
|
||||
if (string === "." && input?.includes("."))
|
||||
return;
|
||||
|
||||
if (string === "-") {
|
||||
if (input === "0") {
|
||||
setInput("-0");
|
||||
} else {
|
||||
setInput((parseFloat(input) * -1).toString());
|
||||
}
|
||||
} else if (string === "%") {
|
||||
setInput((parseFloat(input) / 100).toString());
|
||||
} else if (input === "0" || input === "-0" || input == null || hasReset) {
|
||||
if (string === ".") {
|
||||
setInput(input === "-0" ? "0." : "0.");
|
||||
} else {
|
||||
setInput(input === "-0" ? `-${string}` : string);
|
||||
}
|
||||
} else {
|
||||
setInput(input + string);
|
||||
}
|
||||
|
||||
log();
|
||||
}, [input, isIntermediate, log, reset, secondNumber]);
|
||||
|
||||
const calculate = useCallback((intermediate = false) => {
|
||||
if (firstNumber == null) {
|
||||
|
||||
} else {
|
||||
setSecondNumber(input);
|
||||
|
||||
const a = parseFloat(firstNumber);
|
||||
const b = parseFloat(input);
|
||||
|
||||
let result = 0;
|
||||
switch (operation) {
|
||||
case "×":
|
||||
result = a * b;
|
||||
break;
|
||||
case "÷":
|
||||
result = a / b;
|
||||
break;
|
||||
case "+":
|
||||
result = a + b;
|
||||
break;
|
||||
case "-":
|
||||
result = a - b;
|
||||
break;
|
||||
}
|
||||
|
||||
setInput(result.toString());
|
||||
}
|
||||
|
||||
setIsIntermediate(intermediate);
|
||||
|
||||
log();
|
||||
}, [firstNumber, input, log, operation]);
|
||||
|
||||
const changeOperation = useCallback((operation) => {
|
||||
if (firstNumber != null && secondNumber == null) {
|
||||
calculate(true);
|
||||
} else {
|
||||
setFirstNumber(input);
|
||||
setSecondNumber(null);
|
||||
setInput(null);
|
||||
}
|
||||
|
||||
setOperation(operation);
|
||||
|
||||
log();
|
||||
}, [calculate, firstNumber, input, log, secondNumber]);
|
||||
|
||||
useEffect(() => {
|
||||
const onKeyDown = (event) => {
|
||||
if (!active)
|
||||
return;
|
||||
|
||||
console.log(event.key);
|
||||
|
||||
event.preventDefault();
|
||||
|
||||
switch (event.key) {
|
||||
case "0":
|
||||
case "1":
|
||||
case "2":
|
||||
case "3":
|
||||
case "4":
|
||||
case "5":
|
||||
case "6":
|
||||
case "7":
|
||||
case "8":
|
||||
case "9":
|
||||
addInput(event.key);
|
||||
break;
|
||||
case ".":
|
||||
case ",":
|
||||
addInput(".");
|
||||
break;
|
||||
case "Escape":
|
||||
reset();
|
||||
break;
|
||||
case "=":
|
||||
case "Enter":
|
||||
calculate();
|
||||
break;
|
||||
case "*":
|
||||
changeOperation("×");
|
||||
break;
|
||||
case "/":
|
||||
changeOperation("÷");
|
||||
break;
|
||||
case "+":
|
||||
case "-":
|
||||
changeOperation(event.key);
|
||||
break;
|
||||
case "%":
|
||||
addInput("%");
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener("keydown", onKeyDown);
|
||||
|
||||
return () => {
|
||||
document.removeEventListener("keydown", onKeyDown);
|
||||
};
|
||||
}, [active, addInput, calculate, changeOperation, reset]);
|
||||
|
||||
let calculation = "";
|
||||
if (operation != null)
|
||||
calculation = `${firstNumber} ${operation} ${secondNumber != null ? secondNumber + " =" : ""}`;
|
||||
|
||||
return (<div className={styles.Container}>
|
||||
<div className={styles.Output}>
|
||||
<p className={styles.Calculation}>{calculation}</p>
|
||||
<p className={styles.Preview}>{input ?? firstNumber}</p>
|
||||
</div>
|
||||
<div className={styles.Input}>
|
||||
<div className={styles["Input-row"]}>
|
||||
<Button className={styles.Button} onClick={reset}>C</Button>
|
||||
<Button className={styles.Button} onClick={() => { addInput("-"); }}>+/-</Button>
|
||||
<Button className={styles.Button} onClick={() => { addInput("%"); }}>%</Button>
|
||||
<Button className={styles.Button} onClick={() => { changeOperation("÷"); }}>÷</Button>
|
||||
</div>
|
||||
<div className={styles["Input-row"]}>
|
||||
<Button className={styles.Button} onClick={() => { addInput("7"); }}>7</Button>
|
||||
<Button className={styles.Button} onClick={() => { addInput("8"); }}>8</Button>
|
||||
<Button className={styles.Button} onClick={() => { addInput("9"); }}>9</Button>
|
||||
<Button className={styles.Button} onClick={() => { changeOperation("×"); }}>×</Button>
|
||||
</div>
|
||||
<div className={styles["Input-row"]}>
|
||||
<Button className={styles.Button} onClick={() => { addInput("4"); }}>4</Button>
|
||||
<Button className={styles.Button} onClick={() => { addInput("5"); }}>5</Button>
|
||||
<Button className={styles.Button} onClick={() => { addInput("6"); }}>6</Button>
|
||||
<Button className={styles.Button} onClick={() => { changeOperation("-"); }}>-</Button>
|
||||
</div>
|
||||
<div className={styles["Input-row"]}>
|
||||
<Button className={styles.Button} onClick={() => { addInput("1"); }}>1</Button>
|
||||
<Button className={styles.Button} onClick={() => { addInput("2"); }}>2</Button>
|
||||
<Button className={styles.Button} onClick={() => { addInput("3"); }}>3</Button>
|
||||
<Button className={styles.Button} onClick={() => { changeOperation("+"); }}>+</Button>
|
||||
</div>
|
||||
<div className={styles["Input-row"]}>
|
||||
<Button className={`${styles.Button} ${styles["Button-large"]}`} onClick={() => { addInput("0"); }}>0</Button>
|
||||
<Button className={styles.Button} onClick={() => { addInput("."); }}>.</Button>
|
||||
<Button className={styles.Button} onClick={() => { calculate(); }}>=</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>);
|
||||
}
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
.Container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
|
||||
--output-height: 20%;
|
||||
--button-gap: 0.25rem;
|
||||
}
|
||||
|
||||
.Output {
|
||||
display: flex;
|
||||
gap: 0.25rem;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: flex-end;
|
||||
height: var(--output-height);
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
.Calculation, .Preview {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.Calculation {
|
||||
color: var(--foreground-color-c);
|
||||
height: 1.25rem;
|
||||
}
|
||||
|
||||
.Preview {
|
||||
color: var(--foreground-color-a);
|
||||
font-size: 2.5rem;
|
||||
}
|
||||
|
||||
.Input {
|
||||
display: flex;
|
||||
gap: var(--button-gap);
|
||||
flex-direction: column;
|
||||
height: calc(100% - var(--output-height));
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
.Input-row {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
gap: var(--button-gap);
|
||||
}
|
||||
|
||||
.Button {
|
||||
flex: 1;
|
||||
border-radius: 0.25rem;
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.Input-row:first-of-type .Button {
|
||||
--normal-color: var(--background-color-b);
|
||||
--hover-color: var(--background-color-ba);
|
||||
}
|
||||
|
||||
.Input-row .Button:last-of-type {
|
||||
--text-color: var(--background-color-a);
|
||||
--normal-color: var(--blue-a);
|
||||
--hover-color: var(--blue-b);
|
||||
}
|
||||
|
||||
.Button-large {
|
||||
min-width: calc(50% - var(--button-gap) / 2);
|
||||
}
|
||||
|
|
@ -3,7 +3,9 @@ export const APPS = {
|
|||
SETTINGS: "settings",
|
||||
MEDIA_VIEWER: "media-viewer",
|
||||
TEXT_EDITOR: "text-editor",
|
||||
FILE_EXPLORER: "file-explorer"
|
||||
FILE_EXPLORER: "file-explorer",
|
||||
CALCULATOR: "calculator",
|
||||
BROWSER: "browser",
|
||||
};
|
||||
|
||||
export const APP_NAMES = {
|
||||
|
|
@ -11,7 +13,9 @@ export const APP_NAMES = {
|
|||
SETTINGS: "Settings",
|
||||
MEDIA_VIEWER: "Photos",
|
||||
TEXT_EDITOR: "Notes",
|
||||
FILE_EXPLORER: "Files"
|
||||
FILE_EXPLORER: "Files",
|
||||
CALCULATOR: "Maths",
|
||||
BROWSER: "Browser",
|
||||
};
|
||||
|
||||
export const APP_ICONS = {
|
||||
|
|
@ -20,4 +24,5 @@ export const APP_ICONS = {
|
|||
MEDIA_VIEWER: `${process.env.PUBLIC_URL}/assets/apps/icons/${APPS.MEDIA_VIEWER}.svg`,
|
||||
TEXT_EDITOR: `${process.env.PUBLIC_URL}/assets/apps/icons/${APPS.TEXT_EDITOR}.svg`,
|
||||
FILE_EXPLORER: `${process.env.PUBLIC_URL}/assets/apps/icons/${APPS.FILE_EXPLORER}.svg`,
|
||||
CALCULATOR: `${process.env.PUBLIC_URL}/assets/apps/icons/${APPS.CALCULATOR}.svg`,
|
||||
};
|
||||
|
|
@ -5,22 +5,22 @@ import { WebView } from "../../components/apps/_utils/web-view/WebView.jsx";
|
|||
import { Terminal } from "../../components/apps/terminal/Terminal.jsx";
|
||||
import { TextEditor } from "../../components/apps/text-editor/TextEditor.jsx";
|
||||
import { Settings } from "../../components/apps/settings/Settings.jsx";
|
||||
// import { Calculator } from "../../components/apps/calculator/Calculator.jsx";
|
||||
import { Calculator } from "../../components/apps/calculator/Calculator.jsx";
|
||||
import Vector2 from "../math/vector2.js";
|
||||
import { APPS } from "../../config/apps.config.js";
|
||||
import { APPS, APP_NAMES } from "../../config/apps.config.js";
|
||||
import { Browser } from "../../components/apps/browser/Browser.jsx";
|
||||
import { IMAGE_FORMATS } from "../../config/apps/mediaViewer.config.js";
|
||||
|
||||
export default class AppsManager {
|
||||
static APPS = [
|
||||
new App("Commands", APPS.TERMINAL, Terminal),
|
||||
new App("Settings", APPS.SETTINGS, Settings),
|
||||
new App("Photos", APPS.MEDIA_VIEWER, MediaViewer),
|
||||
new App(APP_NAMES.TERMINAL, APPS.TERMINAL, Terminal),
|
||||
new App(APP_NAMES.SETTINGS, APPS.SETTINGS, Settings),
|
||||
new App(APP_NAMES.MEDIA_VIEWER, APPS.MEDIA_VIEWER, MediaViewer),
|
||||
// new App("Browser", "browser"),
|
||||
// new App("Calculator", "calculator", Calculator, { size: new Vector2(400, 600) }),
|
||||
new App("Notes", APPS.TEXT_EDITOR, TextEditor),
|
||||
new App(APP_NAMES.CALCULATOR, APPS.CALCULATOR, Calculator, { size: new Vector2(400, 600) }),
|
||||
new App(APP_NAMES.TEXT_EDITOR, APPS.TEXT_EDITOR, TextEditor),
|
||||
// new App("Code Editor", "code-editor"),
|
||||
new App("Files", APPS.FILE_EXPLORER, FileExplorer),
|
||||
new App(APP_NAMES.FILE_EXPLORER, APPS.FILE_EXPLORER, FileExplorer),
|
||||
new App("Wordle", "wordle", WebView, {
|
||||
source: "https://prozilla.dev/wordle",
|
||||
size: new Vector2(400, 650)
|
||||
|
|
@ -33,7 +33,7 @@ export default class AppsManager {
|
|||
source: "https://prozilla.dev/minesweeper",
|
||||
size: new Vector2(500, 580)
|
||||
}),
|
||||
new App("Browser", "browser", Browser, {
|
||||
new App(APP_NAMES.BROWSER, APPS.BROWSER, Browser, {
|
||||
size: new Vector2(700, 500)
|
||||
}),
|
||||
];
|
||||
|
|
|
|||
Loading…
Reference in a new issue