Added calculator app

This commit is contained in:
Prozilla 2024-03-03 13:59:15 +01:00
parent 5e26c27980
commit d2784afba0
No known key found for this signature in database
GPG key ID: 5858DFE71CAF31EE
4 changed files with 288 additions and 16 deletions

View file

@ -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>);
}

View file

@ -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);
}

View file

@ -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`,
};

View file

@ -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)
}),
];