Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
160 changes: 140 additions & 20 deletions src/components/apps/Calculator/Calculator.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -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);
});
</script>


<section class="container">
<header class="app-window-drag-handle"></header>

<section class="show-area">0</section>
<section class="show-area">{displayValue.length < 8? displayValue: displayValue.substring(0,8)}</section>

<section class="buttons-container">
<button class="top-row-button"> AC </button>
<button class="top-row-button">
<button class="top-row-button" on:click={resetCalculator}> AC </button>
<button class="top-row-button" on:click={() => handleOperator('±')}>
<PlusMinus />
</button>
<button class="top-row-button"> % </button>
<button class="operation-button">
<button class="top-row-button" on:click={() => handleOperator('%')}> % </button>
<button class="operation-button" on:click={() => handleOperator('/')}>
<Division />
</button>
<button class="number-button"> 7 </button>
<button class="number-button"> 8 </button>
<button class="number-button"> 9 </button>
<button class="operation-button">
<button class="number-button" on:click={() => inputDigit('7')}> 7 </button>
<button class="number-button" on:click={() => inputDigit('8')}> 8 </button>
<button class="number-button" on:click={() => inputDigit('9')}> 9 </button>
<button class="operation-button" on:click={() => handleOperator('*')}>
<Multiply />
</button>
<button class="number-button"> 4 </button>
<button class="number-button"> 5 </button>
<button class="number-button"> 6 </button>
<button class="operation-button">
<button class="number-button" on:click={() => inputDigit('4')}> 4 </button>
<button class="number-button" on:click={() => inputDigit('5')}> 5 </button>
<button class="number-button" on:click={() => inputDigit('6')}> 6 </button>
<button class="operation-button" on:click={() => handleOperator('-')}>
<Minus />
</button>
<button class="number-button"> 1 </button>
<button class="number-button"> 2 </button>
<button class="number-button"> 3 </button>
<button class="operation-button"> <Plus /> </button>
<button class="number-button curved-bottom-left-button" style:grid-column="1 / span 2">
<button class="number-button" on:click={() => inputDigit('1')}> 1 </button>
<button class="number-button" on:click={() => inputDigit('2')}> 2 </button>
<button class="number-button" on:click={() => inputDigit('3')}> 3 </button>
<button class="operation-button" on:click={() => handleOperator('+')}> <Plus /> </button>
<button class="number-button curved-bottom-left-button" style:grid-column="1 / span 2" on:click={() => inputDigit('0')}>
0
</button>
<button class="number-button"> . </button>
<button class="operation-button curved-bottom-right-button"> <Equal /> </button>
<button class="number-button" on:click={() => inputDigit('.')}> . </button>
<button class="operation-button curved-bottom-right-button" on:click={() => handleOperator('=')}> <Equal /> </button>
</section>
</section>

Expand Down Expand Up @@ -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;
Expand Down