"use client"; import type { KeyboardEvent } from "react"; import { useState } from "react"; import styles from "./CalculatorWidget.module.css"; type Operator = "add" | "subtract" | "multiply" | "divide"; type CalculatorButtonProps = { label: string; ariaLabel?: string; className?: string; onClick: () => void; }; const maxDisplayLength = 16; function parseDisplay(value: string): number { const normalizedValue = value.replace(",", "."); const numberValue = Number(normalizedValue); if (!Number.isFinite(numberValue)) { return 0; } return numberValue; } function formatNumber(value: number): string { if (!Number.isFinite(value)) { return "Fehler"; } if (Object.is(value, -0)) { return "0"; } const absoluteValue = Math.abs(value); if (absoluteValue !== 0 && (absoluteValue >= 1e15 || absoluteValue < 1e-9)) { return value.toExponential(8).replace(".", ","); } const roundedValue = Math.round(value * 1e10) / 1e10; return roundedValue.toLocaleString("de-DE", { useGrouping: false, maximumFractionDigits: 10 }); } function calculate(firstValue: number, secondValue: number, operator: Operator): number { if (operator === "add") { return firstValue + secondValue; } if (operator === "subtract") { return firstValue - secondValue; } if (operator === "multiply") { return firstValue * secondValue; } if (operator === "divide") { if (secondValue === 0) { return Number.NaN; } return firstValue / secondValue; } return secondValue; } function getOperatorLabel(operator: Operator | null): string { if (operator === "add") { return "+"; } if (operator === "subtract") { return "−"; } if (operator === "multiply") { return "×"; } if (operator === "divide") { return "÷"; } return ""; } function CalculatorButton({ label, ariaLabel, className, onClick }: CalculatorButtonProps) { return ( ); } export default function CalculatorWidget() { const [display, setDisplay] = useState("0"); const [storedValue, setStoredValue] = useState(null); const [pendingOperator, setPendingOperator] = useState(null); const [waitingForOperand, setWaitingForOperand] = useState(false); const [memory, setMemory] = useState(0); const displayIsError = display === "Fehler"; function resetIfError(): boolean { if (!displayIsError) { return false; } setDisplay("0"); setStoredValue(null); setPendingOperator(null); setWaitingForOperand(false); return true; } function inputDigit(digit: string) { if (resetIfError()) { setDisplay(digit); return; } if (waitingForOperand) { setDisplay(digit); setWaitingForOperand(false); return; } setDisplay((currentDisplay) => { if (currentDisplay === "0") { return digit; } if (currentDisplay.replace("-", "").replace(",", "").length >= maxDisplayLength) { return currentDisplay; } return `${currentDisplay}${digit}`; }); } function inputDecimal() { if (resetIfError()) { setDisplay("0,"); return; } if (waitingForOperand) { setDisplay("0,"); setWaitingForOperand(false); return; } setDisplay((currentDisplay) => { if (currentDisplay.includes(",")) { return currentDisplay; } return `${currentDisplay},`; }); } function clearEntry() { setDisplay("0"); setWaitingForOperand(false); } function clearAll() { setDisplay("0"); setStoredValue(null); setPendingOperator(null); setWaitingForOperand(false); } function backspace() { if (resetIfError() || waitingForOperand) { setDisplay("0"); setWaitingForOperand(false); return; } setDisplay((currentDisplay) => { if (currentDisplay.length <= 1 || (currentDisplay.length === 2 && currentDisplay.startsWith("-"))) { return "0"; } return currentDisplay.slice(0, -1); }); } function toggleSign() { if (resetIfError()) { return; } setDisplay((currentDisplay) => { if (currentDisplay === "0") { return currentDisplay; } return currentDisplay.startsWith("-") ? currentDisplay.slice(1) : `-${currentDisplay}`; }); } function applyUnary(operation: "percent" | "reciprocal" | "square" | "sqrt") { if (resetIfError()) { return; } const currentValue = parseDisplay(display); let nextValue = currentValue; if (operation === "percent") { if (storedValue !== null && pendingOperator) { nextValue = (storedValue * currentValue) / 100; } else { nextValue = currentValue / 100; } } if (operation === "reciprocal") { nextValue = currentValue === 0 ? Number.NaN : 1 / currentValue; } if (operation === "square") { nextValue = currentValue * currentValue; } if (operation === "sqrt") { nextValue = currentValue < 0 ? Number.NaN : Math.sqrt(currentValue); } setDisplay(formatNumber(nextValue)); setWaitingForOperand(true); } function chooseOperator(operator: Operator) { if (resetIfError()) { return; } const currentValue = parseDisplay(display); if (storedValue === null) { setStoredValue(currentValue); } else if (pendingOperator && !waitingForOperand) { const result = calculate(storedValue, currentValue, pendingOperator); setDisplay(formatNumber(result)); setStoredValue(result); } setPendingOperator(operator); setWaitingForOperand(true); } function applyEquals() { if (resetIfError()) { return; } if (storedValue === null || pendingOperator === null) { return; } const currentValue = parseDisplay(display); const result = calculate(storedValue, currentValue, pendingOperator); setDisplay(formatNumber(result)); setStoredValue(null); setPendingOperator(null); setWaitingForOperand(true); } function memoryClear() { setMemory(0); } function memoryRecall() { setDisplay(formatNumber(memory)); setWaitingForOperand(true); } function memoryAdd() { if (resetIfError()) { return; } setMemory((currentMemory) => currentMemory + parseDisplay(display)); setWaitingForOperand(true); } function memorySubtract() { if (resetIfError()) { return; } setMemory((currentMemory) => currentMemory - parseDisplay(display)); setWaitingForOperand(true); } function memoryStore() { if (resetIfError()) { return; } setMemory(parseDisplay(display)); setWaitingForOperand(true); } function handleKeyboard(event: KeyboardEvent) { const key = event.key; const code = event.code; let handled = true; if (/^[0-9]$/.test(key)) { inputDigit(key); } else if (key === "," || key === "." || code === "NumpadDecimal") { inputDecimal(); } else if (key === "+" || code === "NumpadAdd") { chooseOperator("add"); } else if (key === "-" || code === "NumpadSubtract") { chooseOperator("subtract"); } else if (key === "*" || code === "NumpadMultiply") { chooseOperator("multiply"); } else if (key === "/" || code === "NumpadDivide") { chooseOperator("divide"); } else if (key === "Enter" || key === "=" || code === "NumpadEnter") { applyEquals(); } else if (key === "Backspace") { backspace(); } else if (key === "Escape") { clearAll(); } else if (key === "Delete") { clearEntry(); } else if (key === "%") { applyUnary("percent"); } else if (key === "F9") { toggleSign(); } else { handled = false; } if (handled) { event.preventDefault(); event.stopPropagation(); } } return (
{display}
applyUnary("percent")} /> applyUnary("reciprocal")} /> applyUnary("square")} /> applyUnary("sqrt")} /> chooseOperator("divide")} /> inputDigit("7")} /> inputDigit("8")} /> inputDigit("9")} /> chooseOperator("multiply")} /> inputDigit("4")} /> inputDigit("5")} /> inputDigit("6")} /> chooseOperator("subtract")} /> inputDigit("1")} /> inputDigit("2")} /> inputDigit("3")} /> chooseOperator("add")} /> inputDigit("0")} />
); }