Last active
May 1, 2018 16:44
-
-
Save sayes2x/b151e5cf30409ab74c93e8d00d6e9f1b to your computer and use it in GitHub Desktop.
Calculator
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<div id="root"></div> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const calcMain = (key, ...que) => { | |
const calc = {}; | |
// handle next key press after equal | |
if(/=/.test(que[que.length - 2])) { | |
// if operation continue calculating | |
if(/[\+\-x÷]/.test(key)) { | |
que = [que[que.length - 1]] | |
// if anything else, start over | |
} else { | |
que = []; | |
} | |
} | |
// Determine calc.que | |
const number = /\d/.test(key); | |
if (number) calc.que = keyNumber(key, ...que); | |
const operation = /[\+\-x÷]/.test(key); | |
if (operation) calc.que = keyOperation(key, ...que); | |
const equal = /=/.test(key); | |
if (equal) calc.que = keyEqual(key, ...que); | |
const allClear = /AC/.test(key); | |
if (allClear) calc.que = keyAllClear(); | |
const clear = /^C/.test(key); | |
if (clear) calc.que = keyClear(...que); | |
const dot = /\./.test(key); | |
if (dot) calc.que = keyDot(key, ...que); | |
const percent = /%/.test(key); | |
if (percent) calc.que = keyPercent(key, ...que); | |
// Determine calc.result | |
if (calc.que.length === 0) { | |
calc.result = '0'; | |
} else if (calc.que[calc.que.length - 1] === 'Divide by zero error!') { | |
calc.result = 'ERROR'; | |
} else { | |
calc.result = lastNumber(...calc.que); | |
} | |
// Determine calc.displayQue | |
if (calc.result === 'ERROR') { | |
calc.displayQue = calc.que[calc.que.length - 1]; | |
} else { | |
calc.displayQue = calc.que.join('').slice(-24); | |
} | |
// return calc object | |
return calc; | |
}; | |
// returns last string that is a 'number' in an array | |
// this will always return what should be displayed as result | |
const lastNumber = (...que) => que.filter(n => /\d/.test(n)).pop(); | |
const keyNumber = (key, ...que) => { | |
// Que conditions | |
const emptyQue = que.length === 0; | |
const lastElement = que.length - 1; | |
const lastElementNumber = /[\d\.]/.test(que[lastElement]); | |
const lastElementOnlyZero = /^0$/.test(que[lastElement]); | |
const lastElementOperation = /[\+\-x÷]/.test(que[lastElement]); | |
let queLengthLessTen; | |
emptyQue | |
? (queLengthLessTen = true) | |
: (queLengthLessTen = que[lastElement].length < 10); | |
if (queLengthLessTen) { | |
if (emptyQue || lastElementOperation) { | |
que.push(key); | |
} else if (lastElementOnlyZero) { | |
que.pop(); | |
que.push(key); | |
} else if (lastElementNumber) { | |
const newNumber = que.pop().concat(key); | |
que.push(newNumber); | |
} | |
} | |
return que; | |
}; | |
const keyOperation = (key, ...que) => { | |
//Que Conditions | |
const lastElement = que.length - 1; | |
const lastElementNumber = /[\d\.]/.test(que[lastElement]); | |
const lastElementEndDot = /\.$/.test(que[lastElement]); | |
const lastElementPercent = /%/.test(que[lastElement]); | |
const lastElementOperation = /[\+\-x÷]/.test(que[lastElement]); | |
if (lastElementEndDot) { | |
var numberDot = que.pop(); | |
numberDot = numberDot.replace(/\.$/, ''); | |
que.push(numberDot); | |
} | |
if (lastElementNumber || lastElementPercent) { | |
que.push(key); | |
} | |
if (lastElementOperation) { | |
que.pop(); | |
que.push(key); | |
} | |
return que; | |
}; | |
const keyEqual = (key, ...que) => { | |
//Que Conditions | |
const elementCount3Plus = que.length >= 3; | |
const lastElement = que.length - 1; | |
const lastElementNumber = /[\d\.]/.test(que[lastElement]); | |
const lastElementEndDot = /\.$/.test(que[lastElement]); | |
const lastElementPercent = /%/.test(que[lastElement]); | |
const lastElementOperation = /[\+\-x÷]/.test(que[lastElement]); | |
if (elementCount3Plus) { | |
if (lastElementEndDot) { | |
var numberDot = que.pop(); | |
numberDot = numberDot.replace(/\.$/, ''); | |
que.push(numberDot); | |
} | |
if (lastElementNumber || lastElementPercent) { | |
que.push(key); | |
que.push(calculateResult(...que)); | |
} | |
if (lastElementOperation) { | |
que.pop(); | |
que.push(key); | |
que.push(calculateResult(...que)); | |
} | |
} | |
return que; | |
}; | |
const calculateResult = (...que) => { | |
// Handle % | |
let percentResult = handlePercent(...que); | |
// Handle x & ÷ | |
let timesDivideResult = []; | |
try { | |
timesDivideResult = handleTimesDivide(...percentResult); | |
} catch (error) { | |
timesDivideResult = [error.message]; | |
} | |
if (timesDivideResult[0] === 'Divide by zero error!') | |
return timesDivideResult[0]; | |
// Handle + & - | |
que = handlePlusMinus(...timesDivideResult); | |
return display(que[0]); | |
}; | |
const display = result => { | |
let resultStr = ''; | |
// to prevent numbers like 9999999999 from being rounded to 10000000000 | |
if (result > 999999999 && result < 10000000000) { | |
resultStr = result.toString(); | |
resultStr = resultStr.slice(0, 10); | |
// to handle all other cases | |
} else { | |
let digits = 9; | |
resultStr = result.toPrecision(digits); | |
while (resultStr.length > 10) { | |
digits -= 1; | |
resultStr = result.toPrecision(digits); | |
} | |
// get rid of unneeded zeros in scientific notation | |
resultStr = resultStr.replace(/.0+e/, 'e'); | |
} | |
return +resultStr > 9999999999 | |
? resultStr.toString() | |
: (+resultStr).toString(); | |
}; | |
const handlePercent = (...que) => { | |
while (que.findIndex(elem => /%/.test(elem)) !== -1) { | |
const index = que.findIndex(elem => /%/.test(elem)); | |
que[index - 1] = percentNumber(que[index - 1], que[index - 2]); | |
que[index - 2] = percentOperator(que[index - 2]); | |
que.splice(index, 1); | |
} | |
return que; | |
}; | |
const percentNumber = (number, operator) => { | |
number = number / 100; | |
if (/\+/.test(operator)) { | |
number += 1; | |
} else if (/\-/.test(operator)) { | |
number = 1 - number; | |
} | |
return number; | |
}; | |
const percentOperator = operator => (/[\+\-]/.test(operator) ? 'x' : operator); | |
const handleTimesDivide = (...que) => { | |
while (que.findIndex(elem => /[x÷]/.test(elem)) !== -1) { | |
const index = que.findIndex(elem => /[x÷]/.test(elem)); | |
if ( | |
(que[index + 1] === '0' || que[index + 1] === 0) && | |
/÷/.test(que[index]) | |
) { | |
throw new Error('Divide by zero error!'); | |
} | |
const func = /x/.test(que[index]) ? (x, y) => x * y : (x, y) => x / y; | |
const result = func(+que[index - 1], +que[index + 1]); | |
que.splice(index - 1, 3, result); | |
} | |
return que; | |
}; | |
const handlePlusMinus = (...que) => { | |
while (que.findIndex(elem => /\s[\+\-]\s/.test(elem)) !== -1) { | |
const index = que.findIndex(elem => /\s[\+\-]\s/.test(elem)); | |
const func = /\+/.test(que[index]) ? (x, y) => x + y : (x, y) => x - y; | |
const result = func(+que[index - 1], +que[index + 1]); | |
que.splice(index - 1, 3, result); | |
} | |
return que; | |
}; | |
const keyAllClear = () => { | |
return []; | |
}; | |
const keyClear = (...que) => { | |
que.pop(); | |
return que; | |
}; | |
const keyDot = (key, ...que) => { | |
//Que Conditions | |
const emptyQue = que.length === 0; | |
const lastElement = que.length - 1; | |
const lastElementOperation = /[\+\-x÷]/.test(que[lastElement]); | |
const lastElementNumber = /[\d\.]/.test(que[lastElement]); | |
const lastElementHasDot = /\./.test(que[lastElement]); | |
if (emptyQue || lastElementOperation) { | |
que.push('0.'); | |
} else if (lastElementNumber && !lastElementHasDot) { | |
const newNumber = que.pop().concat(key); | |
que.push(newNumber); | |
} | |
return que; | |
}; | |
const keyPercent = (key, ...que) => { | |
//Que Conditions | |
const elementCount3Plus = que.length >= 3; | |
const lastElement = que.length - 1; | |
const lastElementNumber = /[\d\.]/.test(que[lastElement]); | |
const lastElementEndDot = /\.$/.test(que[lastElement]); | |
if (elementCount3Plus && lastElementNumber) { | |
if (lastElementEndDot) { | |
var numberDot = que.pop(); | |
numberDot = numberDot.replace(/\.$/, ''); | |
que.push(numberDot); | |
} | |
que.push(key); | |
} | |
return que; | |
}; | |
class Calculator extends React.Component { | |
constructor(props) { | |
super(props); | |
this.state = { | |
keys: ['AC', 'C', '%', ' ÷ ', '7', '8', '9', ' x ', '4', '5', '6', ' - ', '1', '2', '3', ' + ', '0', '.', ' = '], | |
result: 0, | |
que: [], | |
displayQue: [] | |
} | |
} | |
handleClick = (key) => { | |
const { que } = this.state; | |
const calc = calcMain(key, ...que); | |
this.setState({ | |
result: calc.result, | |
que: calc.que, | |
displayQue: calc.displayQue | |
}); | |
} | |
handleKeyDown = (event) => { | |
const pressed = ['Escape', 'Delete', 'a', 'A', 'Backspace', 'c', 'C', 'Enter', '=', '+', '-', '*', '/', 'p', 'P']; | |
const sent = ['AC', 'AC', 'AC', 'AC', 'C', 'C', 'C', ' = ', ' = ', ' + ', ' - ', ' x ', ' ÷ ', '%', '%']; | |
let key = ''; | |
if(pressed.indexOf(event.key) !== -1) { | |
key = sent[pressed.indexOf(event.key)]; | |
} | |
if(/[0-9\.%]/.test(event.key)) { | |
key = event.key; | |
} | |
if (key !== '') this.handleClick(key); | |
} | |
componentDidMount() { | |
window.addEventListener('keydown', this.handleKeyDown); | |
window.focus(); | |
} | |
componentWillUnmount() { | |
window.removeEventListener('keydown', this.handleKeyDown); | |
} | |
render() { | |
return( | |
<div> | |
<div className='calculatorApp'> | |
<div className="topLeft"></div><div className="top"></div><div className="topRight"></div> | |
<div className="left"></div> | |
<div className="calculator"> | |
<Label /> | |
<Screen result={this.state.result} que={this.state.displayQue} /> | |
<Keypad onClick={this.handleClick} keys={this.state.keys} /> | |
</div> | |
<div className="right"></div> | |
<div className="bottomLeft"></div><div className="bottom"></div><div className="bottomRight"></div> | |
</div> | |
<h3>Designed & Coded by <a href="https://www.scottaprice.com/" target="_blank">Scott A. Price</a></h3> | |
</div> | |
); | |
} | |
} | |
const Label = () => { | |
return( | |
<p className='label'>Digital Calculator</p> | |
) | |
} | |
const Screen = props => { | |
return( | |
<div className='screen'> | |
<Result result={props.result}/> | |
<Que que={props.que} /> | |
</div> | |
) | |
} | |
const Result = ({ result }) => { | |
return( | |
<p className='result'>{result}</p> | |
) | |
} | |
const Que = ({ que }) => { | |
return( | |
<p className='que'>{que}</p> | |
) | |
} | |
const Keypad = props => { | |
handleClick = key => { | |
props.onClick(key); | |
} | |
keys = props.keys.map(key => { | |
return( | |
<Key onClick={handleClick} name={key} /> | |
); | |
}); | |
return( | |
<div className='keypad'>{keys}</div> | |
) | |
} | |
const Key = props => { | |
const { name } = props; | |
handleClick = () => { | |
props.onClick(name); | |
} | |
let className = 'key'; | |
if(/C/.test(name)) { | |
className = 'key clear'; | |
} else if(/\+/.test(name)) { | |
className = 'key twoRows'; | |
} else if(/\d|\./.test(name)) { | |
className = 'key number'; | |
} | |
return( | |
<button onClick={handleClick} className={className} ><span>{name}</span></button> | |
) | |
} | |
ReactDOM.render( | |
<Calculator />, | |
document.getElementById("root") | |
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<script src="https://fb.me/react-15.1.0.min.js"></script> | |
<script src="https://fb.me/react-dom-15.1.0.min.js"></script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
:root { | |
--mult: 1; | |
} | |
body { | |
margin: 25px; | |
background-image: url("https://s26.postimg.org/s8bnp2l1l/wood-textures-high-quality-1.jpg"); | |
} | |
.calculatorApp { | |
width: calc(250px * var(--mult)); | |
height: 416px; | |
display: grid; | |
grid-template-rows: 3% 1fr 3%; | |
grid-template-columns: 3% 1fr 3%; | |
margin: auto; | |
border-radius: calc(10px * var(--mult)); | |
box-shadow: calc(10px * var(--mult)) calc(10px * var(--mult)) calc(15px * var(--mult)) #8B642D; | |
} | |
.topLeft { | |
background: radial-gradient(farthest-corner at bottom right,#272C32,#4f5864 50%); | |
border-top-left-radius: calc(10px * var(--mult)); | |
} | |
.top { | |
background: linear-gradient(#4f5864, #272C32); | |
} | |
.topRight { | |
background: linear-gradient(to bottom right, #4f5864, black); | |
border-top-right-radius: calc(10px * var(--mult)); | |
} | |
.left { | |
background: linear-gradient(to right, #4f5864, #272C32); | |
} | |
.right { | |
background: linear-gradient(to right, #272C32, black); | |
} | |
.bottomLeft { | |
background: linear-gradient(to bottom right, #4f5864, black); | |
border-bottom-left-radius: calc(10px * var(--mult)); | |
} | |
.bottom { | |
background: linear-gradient(#272C32, black); | |
} | |
.bottomRight { | |
background: radial-gradient(circle at top left, #272C32, black 50%); | |
border-bottom-right-radius: calc(10px * var(--mult)); | |
} | |
.calculator { | |
background-color: #272C32; | |
padding: 13px 0; | |
} | |
.label { | |
color: white; | |
text-align: center; | |
font-family: 'Montserrat Alternates', sans-serif; | |
font-weight: bold; | |
margin: 0; | |
padding-bottom: 15px; | |
} | |
.screen { | |
background-color: #81A17C; | |
width: calc(200px * var(--mult)); | |
height: 52px; | |
margin: auto; | |
font-family: 'Aldrich', sans-serif; | |
border-radius: calc(5px * var(--mult)); | |
border-width: calc(3px * var(--mult)); | |
border-style: solid; | |
border-left-color: #0b0d0e; | |
border-top-color: #0b0d0e; | |
border-right-color: #4f5864; | |
border-bottom-color: #4f5864; | |
} | |
.result { | |
font-weight: bold; | |
text-align: right; | |
font-size: calc(27px * var(--mult)); | |
margin: 0; | |
padding: calc(5px * var(--mult)) calc(5px * var(--mult)) 0; | |
} | |
.que { | |
color: #404040; | |
text-align: right; | |
font-size: calc(12px * var(--mult)); | |
margin: 0; | |
padding: 0 5px; | |
} | |
.keypad { | |
width: calc(200px * var(--mult)); | |
height: 260px; | |
display: grid; | |
grid-template-columns: repeat(4, 1fr); | |
grid-template-rows: repeat(5, 1fr); | |
grid-gap: calc(10px * var(--mult)); | |
margin: auto; | |
padding-top: 15px; | |
} | |
.key { | |
color: white; | |
background: radial-gradient(#474747, #595959); | |
font-family: 'Montserrat Alternates', sans-serif; | |
font-weight: bold; | |
border-radius: calc(5px * var(--mult)); | |
border-width: calc(1px * var(--mult)); | |
border-style: solid; | |
border-left-color: #737373; | |
border-top-color: #737373; | |
border-right-color: #1a1a1a; | |
border-bottom-color: #1a1a1a; | |
} | |
.key:focus {outline: 0;} | |
.key:active { | |
border-left-color: #1a1a1a; | |
border-top-color: #1a1a1a; | |
border-right-color: #737373; | |
border-bottom-color: #737373; | |
} | |
span { | |
position: relative; | |
} | |
.key:active span { | |
top: calc(1px * var(--mult)); | |
left: calc(1px * var(--mult)); | |
} | |
.twoRows { | |
grid-row: 4 / span 2; | |
grid-column-start: 4; | |
} | |
.clear { | |
color: white; | |
background: radial-gradient(#BF6C80, #c98393); | |
border-left-color: #d8a6b2; | |
border-top-color: #d8a6b2; | |
border-right-color: #8e3e51; | |
border-bottom-color: #8e3e51; | |
} | |
.clear:active { | |
border-left-color: #8e3e51; | |
border-top-color: #8e3e51; | |
border-right-color: #d8a6b2; | |
border-bottom-color: #d8a6b2; | |
} | |
.number { | |
color: white; | |
background-color: #88888A; | |
background: radial-gradient(#88888A, #98989a); | |
border-left-color: #b2b2b3; | |
border-top-color: #b2b2b3; | |
border-right-color: #58585a; | |
border-bottom-color: #58585a; | |
} | |
.number:active { | |
border-left-color: #58585a; | |
border-top-color: #58585a; | |
border-right-color: #b2b2b3; | |
border-bottom-color: #b2b2b3; | |
} | |
h3 { | |
text-align: center; | |
font-family: 'Montserrat Alternates', sans-serif; | |
font-size: 14px; | |
} | |
a { | |
text-decoration: none; | |
color: #8e3e51; | |
} | |
@media screen and (min-width: 600px) { | |
:root { | |
--mult: 2; | |
} | |
body { | |
margin: 35px; | |
} | |
.calculatorApp { | |
height: 771px; | |
} | |
.calculator { | |
padding: 17px 0; | |
} | |
.label { | |
font-size: 30px; | |
padding-bottom: 0; | |
} | |
.screen { | |
height: 100px; | |
margin: 15px auto 35px; | |
} | |
.que { | |
padding: 0 10px 10px; | |
} | |
.keypad { | |
height: 495px; | |
padding-top: 0; | |
} | |
.key { | |
font-size: 30px; | |
} | |
h3 { | |
font-size: 20px; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment