qcast-front/src/util/calc-utils.js

173 lines
5.0 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

export const createCalculator = (options = {}) => {
const state = {
currentOperand: '',
previousOperand: '',
operation: undefined,
shouldResetDisplay: false,
allowNegative: options.allowNegative ?? true,
allowDecimal: options.allowDecimal ?? true,
allowZero: options.allowZero ?? true,
decimalPlaces: options.decimalPlaces ?? null,
}
// Expose state for debugging and direct access
const getState = () => ({ ...state })
const clear = () => {
state.currentOperand = ''
state.previousOperand = ''
state.operation = undefined
state.shouldResetDisplay = false
return state.currentOperand
}
const deleteNumber = () => {
if (state.currentOperand.length <= 1) {
state.currentOperand = '0'
} else {
state.currentOperand = state.currentOperand.toString().slice(0, -1)
}
return state.currentOperand
}
const appendNumber = (number) => {
if (number === '.' && !state.allowDecimal) return state.currentOperand
if (number === '.' && state.currentOperand.includes('.')) return state.currentOperand
if (state.shouldResetDisplay) {
state.currentOperand = number.toString()
state.shouldResetDisplay = false
} else {
if (state.currentOperand === '0' && number !== '.') {
state.currentOperand = number.toString()
} else {
state.currentOperand = state.currentOperand.toString() + number.toString()
}
}
return state.currentOperand
}
const chooseOperation = (operation) => {
if (operation === '-' && state.currentOperand === '0' && state.previousOperand === '' && state.allowNegative) {
state.currentOperand = '-'
return state.currentOperand
}
if (state.currentOperand === '' || state.currentOperand === '-') return state.currentOperand
// If there's a previous operation, compute it first
if (state.previousOperand !== '') {
compute()
}
state.operation = operation
state.previousOperand = state.currentOperand
state.currentOperand = ''
return state.previousOperand + state.operation
}
const compute = () => {
// If there's no operation, return the current value
if (!state.operation) return parseFloat(state.currentOperand || '0')
// If there's no current operand but we have a previous one, use previous as current
if (state.currentOperand === '' && state.previousOperand !== '') {
state.currentOperand = state.previousOperand
}
const prev = parseFloat(state.previousOperand)
const current = parseFloat(state.currentOperand)
if (isNaN(prev) || isNaN(current)) return 0
let result
switch (state.operation) {
case '+':
result = prev + current
break
case '-':
result = prev - current
break
case '×':
result = prev * current
break
case '÷':
if (current === 0) {
state.currentOperand = 'Error'
return 0
}
result = prev / current
break
default:
return parseFloat(state.currentOperand || '0')
}
// Apply formatting and constraints
if (state.decimalPlaces !== null) {
result = Number(result.toFixed(state.decimalPlaces))
}
if (!state.allowDecimal) {
result = Math.round(result)
}
if (!state.allowNegative && result < 0) {
result = 0
}
if (!state.allowZero && result === 0) {
result = 1
}
// Update state
state.currentOperand = result.toString()
state.previousOperand = ''
state.operation = undefined
state.shouldResetDisplay = true
return result
}
// Getter methods for the calculator state
const getCurrentOperand = () => state.currentOperand
const getPreviousOperand = () => state.previousOperand
const getOperation = () => state.operation
const getDisplayValue = () => {
if (state.operation && state.previousOperand) {
return `${state.previousOperand} ${state.operation} ${state.currentOperand || ''}`.trim()
}
return state.currentOperand
}
return {
// Core calculator methods
clear,
delete: deleteNumber, // Alias for deleteNumber for compatibility
deleteNumber,
appendNumber,
chooseOperation,
compute,
// State getters
getDisplayValue,
getCurrentOperand,
getPreviousOperand,
getOperation,
// Direct state access (for debugging)
getState,
// Direct property access (for compatibility with CalcInput.jsx)
get currentOperand() { return state.currentOperand },
get previousOperand() { return state.previousOperand },
get operation() { return state.operation },
get shouldResetDisplay() { return state.shouldResetDisplay },
// Setter for direct property access (if needed)
set currentOperand(value) { state.currentOperand = value },
set previousOperand(value) { state.previousOperand = value },
set operation(value) { state.operation = value },
set shouldResetDisplay(value) { state.shouldResetDisplay = value }
}
}