From 4c5d2e2f6cf2da8937deced4cee80631095ed271 Mon Sep 17 00:00:00 2001 From: Tony Snearly Date: Tue, 7 Oct 2025 07:29:40 -0500 Subject: [PATCH] feat: enhance calculator functionality with operator handling and input validation --- .../apps/Calculator/Calculator.svelte | 160 +++++++++++++++--- 1 file changed, 140 insertions(+), 20 deletions(-) diff --git a/src/components/apps/Calculator/Calculator.svelte b/src/components/apps/Calculator/Calculator.svelte index ffbc763c..7633d4f3 100644 --- a/src/components/apps/Calculator/Calculator.svelte +++ b/src/components/apps/Calculator/Calculator.svelte @@ -5,43 +5,154 @@ import PlusMinus from '~icons/majesticons/plus-minus-2'; import Division from '~icons/ph/divide-bold'; import Multiply from '~icons/uil/multiply'; + import { onMount } from 'svelte'; + + let displayValue = '0'; + let firstOperand = null; + let operator = null; + let waitingForSecondOperand = false; + + function inputDigit(digit) { + // If starting to enter the second operand, reset the display for the new value. + if (waitingForSecondOperand === true) { + // If the user starts with a decimal, show '0.' + displayValue = digit === '.' ? '0.' : String(digit); + waitingForSecondOperand = false; + return; + } + + // Prevent multiple decimals in the current number + if (digit === '.' && displayValue.includes('.')) { + return; + } + + // If display is '0' and digit is not '.', replace it; otherwise append. + if (displayValue === '0' && digit !== '.') { + displayValue = String(digit); + } else { + displayValue = displayValue + String(digit); + } + } + + function handleOperator(nextOperator) { + const inputValue = parseFloat(displayValue); + + // Unary operators that act immediately on the current display value + if (nextOperator === '±') { + // Toggle sign + if (displayValue === '0' || displayValue === '0.0' || displayValue === '') return; + displayValue = displayValue.startsWith('-') ? displayValue.slice(1) : '-' + displayValue; + return; + } + + if (nextOperator === '%') { + // Percent: if there is a firstOperand and an operator and we're entering the second operand, + // treat the percent as (firstOperand * current / 100). Otherwise just divide current by 100. + let result; + if (firstOperand !== null && operator && !waitingForSecondOperand) { + result = (firstOperand * inputValue) / 100; + } else { + result = inputValue / 100; + } + // Normalize small floating point issues by converting to string simply + displayValue = String(result); + return; + } + + // Binary operators behave as before + if (firstOperand === null) { + firstOperand = inputValue; + } else if (operator) { + const result = performCalculation[operator](firstOperand, inputValue); + displayValue = String(result); + firstOperand = result; + } + + waitingForSecondOperand = true; + operator = nextOperator; + } + + const performCalculation = { + '/': (firstOperand, secondOperand) => (firstOperand / secondOperand).toFixed(2), + '*': (firstOperand, secondOperand) => (firstOperand * secondOperand).toFixed(2), + '+': (firstOperand, secondOperand) => (firstOperand + secondOperand).toFixed(2), + '-': (firstOperand, secondOperand) => (firstOperand - secondOperand).toFixed(2), + '=': (firstOperand, secondOperand) => secondOperand // For equals, just return the second operand if no previous operator + }; + + function resetCalculator() { + displayValue = '0'; + firstOperand = null; + operator = null; + waitingForSecondOperand = false; + } + + function handleBackspace() { + // If we're waiting for the second operand (i.e. user just pressed an operator), + // there's nothing typed yet to delete. + if (waitingForSecondOperand) return; + + if (!displayValue || displayValue.length === 0) { + displayValue = '0'; + return; + } + + // Remove the last character + displayValue = displayValue.slice(0, -1); + + // Normalize empty or lone '-' to '0' + if (displayValue === '' || displayValue === '-') displayValue = '0'; + } + + function onKeyDown(e: KeyboardEvent) { + if (e.key === 'Backspace') { + e.preventDefault(); + handleBackspace(); + } + } + + onMount(() => { + window.addEventListener('keydown', onKeyDown); + return () => window.removeEventListener('keydown', onKeyDown); + }); +
-
0
+
{displayValue.length < 8? displayValue: displayValue.substring(0,8)}
- - + - - + - - - - + + + - - - - + + + - - - - - + + + + - - + +
@@ -88,14 +199,23 @@ .top-row-button { background-color: hsla(240, 5%, 12%, 0.2); } + .top-row-button:hover { + background-color: hsl(0deg 1% 58%); + } .number-button { background-color: hsla(240, 5%, 80%, 0.25); } + .number-button:hover { + background-color: hsl(280deg 2% 55%); + } .operation-button { background-color: hsl(37deg 98% 51%); } + .operation-button:hover { + background-color: hsl(33deg 56% 98%); + } .curved-bottom-left-button { border-radius: 0 0 0 0.7rem;