Exercise assignment:
Build a calculator with React which can Add, Subtract, Multiply and Divide.
See image below for reference
Give it whatever design and colors you want
Each text field should have a placeholder
The result field should be able to be copied but not edited
Make sure your math is correct and calculator is well tested
Post final Github solution and link to the forums: http://community.devslopes.com/category/33/react-flux-course
- Remember the better you make this the better potential you have of using it in the future
Here's my uncompiled JSX and Sass-code:
// Main.jsx as starting point of the app.
var React = require('react');
var ReactDOM = require('react-dom');
var Calculator = require('./components/Calculator.jsx');
var NAV_BUTTONS = [
{ value: '+ Add',
operation: 'add'
},
{ value: '- Subtract',
operation: 'subtract'
},
{ value: 'x Multiply',
operation: 'multiply'
},
{ value: '/ Divide',
operation: 'divide'
}
];
ReactDOM.render(<Calculator navButtons={ NAV_BUTTONS } />, document.getElementById('app'));
// Calculator.jsx - Main app-component
var React = require('react');
var TextBox = require('./TextBox.jsx');
var Button = require('./Button.jsx');
var Calculator = React.createClass({
INIT_STATE: { a: 0,
b: 0,
placeholderText: 'Enter a number ...',
resultBox: '',
aClass: 'input-box',
bClass: 'input-box',
aDisabled: false,
bDisabled: false,
buttonsDisabled: 'disabled' },
operations: {
'add': function() {
return this.state.a + this.state.b;
},
'subtract': function() {
return this.state.a - this.state.b;
},
'multiply': function() {
return this.state.a * this.state.b;
},
'divide': function() {
return this.state.a / this.state.b;
}
},
getInitialState: function() {
return this.INIT_STATE;
},
updateNumbers: function(variable, reference) {
var val = parseFloat(reference.value);
var varClass = [variable + 'Class'];
if (typeof val === 'number' && !isNaN(val)) {
if (this.state[variable + 'Class'].indexOf('invalid-input') > -1) {
this.setState({
[varClass]: 'input-box'
})
}
this.setState({
[variable]: val,
buttonsDisabled: ''
});
} else {
this.setState({
[varClass]: [varClass] + ' invalid-input',
buttonsDisabled: 'disabled'
});
}
},
triggerOperation: function(operation) {
var result = this.operations[operation].call(this);
this.setState({
aDisabled: 'disabled',
bDisabled: 'disabled',
buttonsDisabled: 'disabled'
});
this.refs.resultBox.refs.inputElement.value = result;
},
resetForm: function() {
function resetElement(itemName, placeholder, disabled) {
this.refs[itemName].refs.inputElement.value = ''; // Value must be empty f. placeholder to appear.
this.refs[itemName].refs.inputElement.disabled = disabled;
this.refs[itemName].refs.inputElement.placeholder = placeholder;
}
resetElement.call(this, 'a', this.INIT_STATE.placeholderText);
resetElement.call(this, 'b', this.INIT_STATE.placeholderText);
resetElement.call(this, 'resultBox', this.INIT_STATE.resultBox, 'disabled');
this.setState({
a: 0,
b: 0,
aClass: 'input-box',
bClass: 'input-box',
buttonsDisabled: 'disabled'
});
},
render: function() {
var that = this;
var navButtons = this.props.navButtons.map(function(button) {
return (
<div>
<Button value={ button.value } classDiv="large-3 medium-6 column"
classButton="calculation-method nav-button"
handler={ that.triggerOperation } operation={ button.operation } disabled={ that.state.buttonsDisabled }/>
</div>
);
});
return (
<div className="calculator">
<div className="row">
<h1>Simple calculator</h1>
</div>
<div className="row">
<TextBox divClass="large-6 columns"
placeholder={ this.state.placeholderText }
id="a" textBoxClass={ this.state.aClass }
ref="a"
value={ this.state.a }
changeHandler={ this.updateNumbers }
variable="a"
disabled={ this.state.aDisabled }
/>
<TextBox divClass="large-6 columns"
placeholder={ this.state.placeholderText }
id="b" textBoxClass={ this.state.bClass }
ref="b"
value={ this.state.b }
changeHandler={ this.updateNumbers }
variable="b"
disabled={ this.state.bDisabled }
/>
</div>
<div className="row">
{ navButtons }
</div>
<div className="row">
<TextBox divClass="medium-9 columns"
placeholder={ this.INIT_STATE.resultBox }
ref="resultBox" textBoxClass="input-box"
disabled="disabled" />
<Button value="Clear" classDiv="medium-3 columns"
classButton="attention nav-button"
handler={ this.resetForm } />
</div>
</div>
);
}
});
module.exports = Calculator;
// Button component
var React = require('react');
var Button = React.createClass({
render: function() {
function notify(e) {
this.props.handler(e.target.dataset.operation);
}
return (
<div className={ this.props.classDiv }>
<button href='#' className={ this.props.classButton }
onClick={ notify.bind(this) }
data-operation={ this.props.operation }
disabled={ this.props.disabled } >
{ this.props.value }
</button>
</div>
);
}
});
module.exports = Button;
// TextBox component
var React = require('react');
var TextBox = React.createClass({
notify: function() {
let item = this.refs.inputElement;
this.props.changeHandler(item.dataset.variable, item);
},
render: function() {
return (
<div className={ this.props.divClass }
ref={ this.props.id }>
<input type="text"
placeholder={ this.props.placeholder}
ref="inputElement"
className={ this.props.textBoxClass }
disabled={ this.props.disabled }
onChange={ this.notify }
data-variable={ this.props.variable }
/>
</div>
);
}
});
module.exports = TextBox;
$lightChange: 25%;
$borderRadius: 6px;
@mixin addPseudoClasses($selector, $color) {
#{$selector}:visited, #{$selector}:hover {
color: white;
}
#{$selector}:hover {
background: linear-gradient(lighten($color, $lightChange), $color);
color: white;
cursor: pointer;
}
#{$selector}:active {
opacity: 0.6;
box-shadow: 1px 1px 0 black;
}
}
html, body {
height: 100%;
}
body {
background: linear-gradient(to top, #403B4A , #E7E9BB);
}
.nav-button {
text-decoration: none;
color: green;
padding: 10px 20px;
text-align: center;
font-weight: 900;
font-size: 1.2rem;
margin-bottom: 16px;
display: inline-block;
width: 100%;
border-radius: $borderRadius;
letter-spacing: 1px;
box-shadow: 2px 2px 0 black;
}
.nav-button[disabled] {
color: crimson;
}
.calculation-method {
background: linear-gradient(to top, #abbaab ,#ffffff);
}
@include addPseudoClasses('.calculation-method', #344334);
h1 {
text-align: center;
margin: 20px 0 30px;
letter-spacing: 2px;
}
.attention {
background: linear-gradient(to top, darken(#ED4264, $lightChange), #FFEDBC);
text-transform: uppercase;
color: white;
}
@include addPseudoClasses('.attention', red);
.invalid-input {
border-color: red !important;
background-color: pink !important;
}
input[type=text] {
border-radius: $borderRadius !important;
box-shadow: inset 1px 1px 0 black;
padding-left: 20px;
font-weight: 900;
}
The full project-code can be found on GitHub : https://github.com/mizech/simple-calculator
Here's a live demo: http://n-or.de/simple-calculator/
Have I fulfilled the assignment demands?
Moreover: The calculator works but I'm sure the programming could be improved.
Would appreciate to read your opinions and hints.