Skip to content

Commit

Permalink
Merge pull request #3 from amrendrakind/events
Browse files Browse the repository at this point in the history
Math Magicians: Events
  • Loading branch information
amrendrakind committed Apr 29, 2022
2 parents 19f006b + 6b77262 commit 3e1c1ea
Show file tree
Hide file tree
Showing 7 changed files with 237 additions and 17 deletions.
32 changes: 26 additions & 6 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"@testing-library/jest-dom": "^5.16.4",
"@testing-library/react": "^13.1.1",
"@testing-library/user-event": "^13.5.0",
"big.js": "^6.1.1",
"prop-types": "^15.8.1",
"react": "^18.0.0",
"react-dom": "^18.0.0",
Expand Down
2 changes: 1 addition & 1 deletion public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
<title>Calculator - Math Magicians</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
Expand Down
25 changes: 23 additions & 2 deletions src/components/Button.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,23 @@
import React from 'react';
import PropTypes from 'prop-types';
import calculate from '../logic/Calculate';

class Button extends React.Component {
constructor(props) {
super(props);
this.state = {};
this.clickHandler = this.clickHandler.bind(this);
}

clickHandler() {
const { label, OnValueChange, objCalculation } = this.props;
const calc = calculate(objCalculation, label);
OnValueChange(calc);
}

render() {
const { label } = this.props;
return (
<button type="button" className={`calButton ${label === '0' ? 'zeroButton' : ' '}`}>{label}</button>
<button type="button" className={`calButton ${label === '0' ? 'zeroButton' : ' '}`} onClick={this.clickHandler}>{label}</button>
);
}
}
Expand All @@ -21,8 +28,22 @@ export default Button;

Button.defaultProps = {
label: 'Button',
OnValueChange: () => {},
objCalculation: {
total: null,
next: '0',
operation: null,
},
};

// Creating overirde props

Button.propTypes = {
label: PropTypes.string,
OnValueChange: PropTypes.func,
objCalculation: PropTypes.objectOf({
total: PropTypes.string,
next: PropTypes.string.isRequired,
operation: PropTypes.string,
}),
};
31 changes: 23 additions & 8 deletions src/components/Calculator.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,27 +86,42 @@ class Calculator extends React.Component {
constructor(props) {
super(props);
this.state = {
value: '',
total: null,
next: '0',
operation: null,
};
this.OnValueChange = this.OnValueChange.bind(this);
}

OnValueChange(newValue) {
if (newValue.length > 0) {
this.setState((prevState) => ({ value: prevState.value.concat(newValue) }));
} else this.setState({ value: '' });
OnValueChange(calculate) {
this.setState({ ...calculate });
}

render() {
const { value } = this.state;
const { total, next, operation } = this.state;
let val;
if (total !== null && next === null) {
val = total;
}
if (total === null && operation === null) {
val = next;
}
if (total !== null && operation !== null) {
val = total + operation;
}
if (total !== null && operation !== null && next !== null) {
val = total + operation + next;
}
return (
<div className="calContainer">
<input type="text" placeholder="0" className="calInput" value={value} readOnly />
<input type="text" placeholder="0" className="calInput" value={val} readOnly />
<div className="buttonContainer">
{
buttons.map((button) => (
<Button
label={button.label}
isOperator={button.isOperator}
OnValueChange={this.OnValueChange}
objCalculation={this.state}
key={button.id}
/>
))
Expand Down
133 changes: 133 additions & 0 deletions src/logic/Calculate.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
import operate from './Operate';

function isNumber(item) {
return !!item.match(/[0-9]+/);
}

/**
* Given a button name and a calculator data object, return an updated
* calculator data object.
*
* Calculator data object contains:
* total:s the running total
* next:String the next number to be operated on with the total
* operation:String +, -, etc.
*/
export default function calculate(obj, buttonName) {
if (buttonName === 'AC') {
return {
total: null,
next: '0',
operation: null,
};
}

if (isNumber(buttonName)) {
if (buttonName === '0' && obj.next === '0') {
return {};
}
// If there is an operation, update next
if (obj.operation) {
if (obj.next && obj.next !== '0') {
return { ...obj, next: obj.next + buttonName };
}
return { ...obj, next: buttonName };
}
// If there is no operation, update next and clear the value
if (obj.next && obj.next !== '0') {
return {
next: obj.next + buttonName,
total: null,
};
}
return {
next: buttonName,
total: null,
};
}

if (buttonName === '.') {
if (obj.next) {
if (obj.next.includes('.')) {
return { ...obj };
}
return { ...obj, next: `${obj.next}.` };
}
if (obj.operation) {
return { ...obj, next: '0.' };
}
if (obj.total) {
if (obj.total.includes('.')) {
return {};
}
return { ...obj, next: `${obj.total}.` };
}
return { ...obj, next: '0.' };
}

if (buttonName === '=') {
if (obj.next && obj.operation) {
return {
total: operate(obj.total, obj.next, obj.operation),
next: null,
operation: null,
};
}
// '=' with no operation, nothing to do
return {};
}

if (buttonName === '+/-') {
if (obj.next) {
return { ...obj, next: (-1 * parseFloat(obj.next)).toString() };
}
if (obj.total) {
return { ...obj, total: (-1 * parseFloat(obj.total)).toString() };
}
return {};
}

// Button must be an operation

// When the user presses an operation button without having entered
// a number first, do nothing.
// if (!obj.next && !obj.total) {
// return {};
// }

// User pressed an operation after pressing '='
if (!obj.next && obj.total && !obj.operation) {
return { ...obj, operation: buttonName };
}

// User pressed an operation button and there is an existing operation
if (obj.operation) {
if (obj.total && !obj.next) {
return { ...obj, operation: buttonName };
}

if (!obj.total) {
return { total: 0, operation: buttonName };
}

return {
total: operate(obj.total, obj.next, obj.operation),
next: null,
operation: buttonName,
};
}

// no operation yet, but the user typed one

// The user hasn't typed a number yet, just save the operation
if (!obj.next) {
return { operation: buttonName };
}

// save the operation and shift 'next' into 'total'
return {
total: obj.next,
next: null,
operation: buttonName,
};
}
30 changes: 30 additions & 0 deletions src/logic/Operate.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import Big from 'big.js';

export default function operate(numberOne, numberTwo, operation) {
const one = Big(numberOne);
const two = Big(numberTwo);
if (operation === '+') {
return one.plus(two).toString();
}
if (operation === '-') {
return one.minus(two).toString();
}
if (operation === 'x') {
return one.times(two).toString();
}
if (operation === '÷') {
try {
return one.div(two).toString();
} catch (err) {
return "Can't divide by 0.";
}
}
if (operation === '%') {
try {
return one.mod(two).toString();
} catch (err) {
return "Can't find modulo as can't divide by 0.";
}
}
throw Error(`Unknown operation '${operation}'`);
}

0 comments on commit 3e1c1ea

Please sign in to comment.