A react calculator
A Pen by jessechumo on CodePen.
A react calculator
A Pen by jessechumo on CodePen.
| <div id="challenge"></div> |
| const isOperator = /[x/+‑]/, | |
| endsWithOperator = /[-x+/]$/, | |
| endsWithNegativeSign = /[x/+][-]$/; | |
| class Keypad extends React.Component { | |
| constructor(props) { | |
| super(props); | |
| } | |
| render() { | |
| return ( | |
| <div className="keypad"> | |
| <div className="top"> | |
| <button | |
| id="clear" | |
| value="AC" | |
| onClick={this.props.clickedClear} | |
| class="AC-0 pad" | |
| > | |
| AC | |
| </button> | |
| <button | |
| id="divide" | |
| value="/" | |
| onClick={this.props.clickedOperator} | |
| class="number-operator pad op" | |
| > | |
| / | |
| </button> | |
| <button | |
| id="multiply" | |
| value="x" | |
| onClick={this.props.clickedOperator} | |
| class="number-operator pad op" | |
| > | |
| x | |
| </button> | |
| </div> | |
| <div className="middle"> | |
| <div> | |
| <button | |
| id="seven" | |
| value={7} | |
| onClick={this.props.clickedNumber} | |
| class="number-operator pad" | |
| > | |
| 7 | |
| </button> | |
| <button | |
| id="eight" | |
| value={8} | |
| onClick={this.props.clickedNumber} | |
| class="number-operator pad" | |
| > | |
| 8 | |
| </button> | |
| <button | |
| id="nine" | |
| value={9} | |
| onClick={this.props.clickedNumber} | |
| class="number-operator pad" | |
| > | |
| 9 | |
| </button> | |
| <button | |
| id="subtract" | |
| value="-" | |
| onClick={this.props.clickedOperator} | |
| class="number-operator pad op" | |
| > | |
| - | |
| </button> | |
| </div> | |
| <div> | |
| <button | |
| id="four" | |
| value={4} | |
| onClick={this.props.clickedNumber} | |
| class="number-operator pad" | |
| > | |
| 4 | |
| </button> | |
| <button | |
| id="five" | |
| value={5} | |
| onClick={this.props.clickedNumber} | |
| class="number-operator pad" | |
| > | |
| 5 | |
| </button> | |
| <button | |
| id="six" | |
| value={6} | |
| onClick={this.props.clickedNumber} | |
| class="number-operator pad" | |
| > | |
| 6 | |
| </button> | |
| <button | |
| id="add" | |
| value="+" | |
| onClick={this.props.clickedOperator} | |
| class="number-operator pad op" | |
| > | |
| + | |
| </button> | |
| </div> | |
| </div> | |
| <div className="bottom"> | |
| <div className="bottom-left"> | |
| <div className="one-two-three"> | |
| <button | |
| id="one" | |
| value={1} | |
| onClick={this.props.clickedNumber} | |
| class="number-operator pad" | |
| > | |
| 1 | |
| </button> | |
| <button | |
| id="two" | |
| value={2} | |
| onClick={this.props.clickedNumber} | |
| class="number-operator pad" | |
| > | |
| 2 | |
| </button> | |
| <button | |
| id="three" | |
| value={3} | |
| onClick={this.props.clickedNumber} | |
| class="number-operator pad" | |
| > | |
| 3 | |
| </button> | |
| </div> | |
| <div className="zero-decimal"> | |
| <button | |
| id="zero" | |
| value={0} | |
| onClick={this.props.clickedNumber} | |
| class="AC-0 pad" | |
| > | |
| 0 | |
| </button> | |
| <button | |
| id="decimal" | |
| value="." | |
| onClick={this.props.clickedDecimal} | |
| class="number-operator pad" | |
| > | |
| . | |
| </button> | |
| </div> | |
| </div> | |
| <div className="bottom-right"> | |
| <button | |
| id="equals" | |
| value="=" | |
| onClick={this.props.clickedEquals} | |
| class="equals pad" | |
| > | |
| = | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| ); | |
| } | |
| } | |
| class App extends React.Component { | |
| constructor(props) { | |
| super(props); | |
| this.state = { | |
| currentValue: "0", // 0 or an operator or a value | |
| prevValue: "", | |
| formula: "", // a string of the expression | |
| currentSign: "pos" | |
| }; | |
| // bindings | |
| this.limitWarning = this.limitWarning.bind(this); | |
| this.clickedNumber = this.clickedNumber.bind(this); | |
| this.clickedOperator = this.clickedOperator.bind(this); | |
| this.clickedDecimal = this.clickedDecimal.bind(this); | |
| this.clickedEquals = this.clickedEquals.bind(this); | |
| this.clickedClear = this.clickedClear.bind(this); | |
| } | |
| // methods | |
| limitWarning() { | |
| this.setState({ | |
| currentValue: "Digit Limit Reached", | |
| prevValue: "Digit Limit Reached" | |
| }); | |
| } | |
| clickedClear(e) { | |
| this.setState({ | |
| currentValue: "0", | |
| prevValue: "", | |
| formula: "" | |
| }); | |
| } | |
| clickedNumber(e) { | |
| if (!this.state.currentValue.includes("Limit")) { | |
| const { currentValue, formula, evaluated } = this.state; | |
| const val = e.target.value; | |
| this.setState({ evaluated: false }); | |
| if (currentValue.length > 21) { | |
| this.limitWarning(); | |
| } else if (evaluated) { | |
| this.setState({ | |
| currentValue: val, | |
| formula: val !== "0" ? val : "" | |
| }); | |
| } else { | |
| this.setState({ | |
| currentValue: | |
| currentValue === "0" || isOperator.test(currentValue) | |
| ? val | |
| : currentValue + val, | |
| formula: | |
| currentValue === "0" && val === "0" | |
| ? formula | |
| : /([^.0-9]0)$/.test(formula) | |
| ? formula.slice(0, -1) + val | |
| : formula + val | |
| }); | |
| } | |
| } | |
| } | |
| clickedOperator(e) { | |
| if (!this.state.currentValue.includes('Limit')) { | |
| const val = e.target.value; | |
| const { formula, prevValue, evaluated } = this.state; | |
| this.setState({ currentValue: val, evaluated: false }); | |
| if (evaluated) { | |
| this.setState({ formula: prevValue + val }); | |
| } else if (!endsWithOperator.test(formula)) { | |
| this.setState({ | |
| prevValue: formula, | |
| formula: formula + val | |
| }); | |
| } else if (!endsWithNegativeSign.test(formula)) { | |
| this.setState({ | |
| formula: (endsWithNegativeSign.test(formula + val)) ? (formula + val) : (prevValue + val) | |
| }); | |
| } else if (val !== "-") { | |
| this.setState({ | |
| formula: prevValue + val | |
| }); | |
| } | |
| } | |
| } | |
| clickedDecimal() { | |
| if (this.state.evaluated === true) { | |
| this.setState({ | |
| currentValue: "0.", | |
| formula: "0." | |
| }); | |
| } else if ( | |
| !this.state.currentValue.includes(".") && | |
| !this.state.currentValue.includes("Limit") | |
| ) { | |
| this.setState({ evaluated: false }); | |
| if(this.state.currentValue.length > 21) { | |
| this.limitWarning; | |
| } else if ( | |
| endsWithOperator.test(this.state.formula) || | |
| (this.state.currentValue === '0' && this.state.formula === '') | |
| ) { | |
| this.setState({ | |
| currentValue: '0.', | |
| formula: this.state.formula + '0.' | |
| }); | |
| } else { | |
| this.setState({ | |
| currentValue: this.state.formula.match(/(-?\d+\.?\d*)$/)[0] + '.', | |
| formula: this.state.formula + '.' | |
| }); | |
| } | |
| } | |
| } | |
| clickedEquals(e) { | |
| if (!this.state.currentValue.includes("Limit")) { | |
| if(this.state.formula !== "") { | |
| let expression = this.state.formula; | |
| while (endsWithOperator.test(expression)) { | |
| expression = expression.slice(0, -1); | |
| } | |
| expression = expression.replace(/x/g, "*").replace(/‑/g, "-"); | |
| let answer = Math.round(1000000000000 * eval(expression)) / 1000000000000; | |
| this.setState({ | |
| currentValue: answer.toString(), | |
| formula: | |
| expression.replace(/\*/g, "⋅").replace(/-/g, "‑") + "=" + answer, | |
| prevValue: answer, | |
| evaluated: true | |
| }); | |
| } | |
| } | |
| } | |
| render() { | |
| return ( | |
| <div> | |
| <div id="calculator"> | |
| <div className="display"> | |
| <div id="formula">{this.state.formula.replace(/x/g, "⋅")}</div> | |
| <div id="display">{this.state.currentValue}</div> | |
| </div> | |
| <Keypad | |
| clickedNumber={this.clickedNumber} | |
| clickedOperator={this.clickedOperator} | |
| clickedClear={this.clickedClear} | |
| clickedDecimal={this.clickedDecimal} | |
| clickedEquals={this.clickedEquals} | |
| /> | |
| </div> | |
| <div id="zeek"> | |
| <a href="https://codepen.io/odkpatrick" target="_blank">zeek</a> | |
| </div> | |
| </div> | |
| ); | |
| } | |
| } | |
| ReactDOM.render(<App />, document.getElementById("challenge")); |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.10.1/umd/react.production.min.js"></script> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.10.1/umd/react-dom.production.min.js"></script> | |
| <script src="https://cdn.freecodecamp.org/testable-projects-fcc/v1/bundle.js"></script> |
| @import 'https://fonts.googleapis.com/css?family=Share+Tech+Mono'; | |
| * { | |
| border-box: content-box; | |
| margin: 0; | |
| padding: 0; | |
| font-family: 'Share Tech Mono', monospace;; | |
| } | |
| #challenge { | |
| height: 100vh; | |
| display: flex; | |
| justify-content: top; | |
| align-items: center; | |
| } | |
| #calculator { | |
| width: 400px; | |
| background-color: #000; | |
| padding: 2px; | |
| font-size: 32px; | |
| } | |
| .display { | |
| text-align: right; | |
| margin-bottom: 15px; | |
| padding: 10px 15px; | |
| color: #dedede; | |
| } | |
| #formula, | |
| #display { | |
| height: 20px; | |
| color: #fff; | |
| padding: 1px solid #000; | |
| } | |
| .keypad { | |
| display: flex; | |
| flex-direction: column; | |
| border: 1px solid #000; | |
| border-width: 1px; | |
| } | |
| .top, | |
| .middle, | |
| .bottom, | |
| .bottom-left { | |
| width: 100%; | |
| display: grid; | |
| } | |
| .top { | |
| grid-template-columns: 2fr 1fr 1fr; | |
| } | |
| .middle { | |
| grid-template-rows: 1fr 1fr; | |
| } | |
| .middle div { | |
| display: grid; | |
| grid-template-columns: 1fr 1fr 1fr 1fr; | |
| } | |
| .bottom { | |
| grid-template-columns: 3fr 1fr; | |
| } | |
| .bottom-left { | |
| grid-template-rows: 1fr 1fr; | |
| } | |
| .one-two-three { | |
| display: grid; | |
| grid-template-columns: 1fr 1fr 1fr; | |
| } | |
| .zero-decimal { | |
| display: grid; | |
| grid-template-columns: 2fr 1fr; | |
| } | |
| .AC-0, | |
| .number-operator, | |
| .equals { | |
| text-align: center; | |
| border: 1px solid #000; | |
| } | |
| .pad { | |
| height: 65px; | |
| font-size: 20px; | |
| } | |
| .equals { | |
| width: 100%; | |
| height: 100%; | |
| } | |
| .pad:hover { | |
| border: 1px solid #FFF; | |
| } | |
| .number-operator, #zero { | |
| background-color: #ffff; | |
| } | |
| .op { | |
| background-color: #F9E79F; | |
| } | |
| #equals { | |
| background-color: #F9E79F; | |
| } | |
| #clear { | |
| background-color: #F9E79F; | |
| } | |
| a { | |
| display: block; | |
| text-align: right; | |
| padding: 5px; | |
| color: green; | |
| text-decoration: none; | |
| } | |
| a:hover { | |
| text-decoration: none; | |
| } |