<main id="wrapper">
</main>
@font-face {
font-family: 'Seven Segment';
font-style: normal;
font-weight: normal;
src: local('Seven Segment'), url('https://raw.githubusercontent.com/Kemystra/hello-world/main/Seven%20Segment.woff') format('woff');
}
* {
box-sizing: border-box;
}
body {
width: 100vw;
height: 100vh;
margin: 0;
background-color: rgb(158, 189, 214);
}
#wrapper {
width: 100%;
height: 100%;
}
#calculator {
width: 20rem;
height: 30rem;
padding: 0.5rem;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-template-rows: repeat(6, 1fr);
grid-template-areas:
"dsp dsp dsp dsp"
"num num num ."
"num num num ."
"num num num ."
"num num num ."
"cls cls cls cls"
;
background-color: rgb(43, 43, 43);
button {
border: 1px solid black;
border-radius: 5px;
background-color: gray;
transition: background-color 0.1s ease-in;
&:hover {
background-color: rgb(182, 182, 182);
}
&:active {
background-color: orange;
}
}
}
#screen {
grid-area: dsp;
overflow-wrap: anywhere;
padding: 0.2rem;
background-color: rgb(60, 60, 75);
color: white;
font-family: "Seven Segment", sans-serif;
#formula {
font-size: 1.5rem;
margin: 0;
}
#display {
color: orange;
margin: 0;
}
}
#clear {
grid-area: cls;
background-color: #ff3333 !important;
}
#equals {
background-color: #333dff !important;
}
#numbers {
grid-area: num;
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-rows: repeat(4, 1fr);
grid-template-areas: ". . ." ". . ." ". . ." ". zero .";
#zero {
grid-area: zero;
}
}
View Compiled
const EQUALS = "EQUALS";
const ADD = "ADD";
const SUBTRACT = "SUBTRACT";
const MULTIPLY = "MULTIPLY";
const DIVIDE = "DIVIDE";
const CLEAR = "CLEAR";
const NUMBER = "NUMBER";
const OPERATION = "OPERATION";
const DECIMAL = "DECIMAL";
const buttons=[{name:"one",value:1},{name:"two",value:2},
{name:"three",value:3},{name:"four",value:4},{name:"five",value:5},{name:"six",value:6},
{name:"seven",value:7},{name:"eight",value:8},{name:"nine",value:9},{name:"zero",value:0},
{name:ADD,value:"+"},{name:SUBTRACT,value:"-"},
{name:MULTIPLY,value:"*"},{name:DIVIDE,value:"/"}];
// React Code
class Calculator extends React.Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick(event) {
const actionCreators = {
"NUMBER": this.props.pushNumber,
"DECIMAL": this.props.pushDecimal,
"OPERATION": this.props.pushOps,
"EQUALS": this.props.calcExpression,
"CLEAR": this.props.clearDisplay
};
actionCreators[event.target.className](event.target.innerHTML);
}
render() {
const thisClass = this;
return (
<div id="calculator">
<Display currOperation={this.props.currOps} totalOperation={this.props.totalOps} />
<div id="numbers">
{buttons.slice(0, 10).map(type => <Button id={type.name.toLowerCase()} content={type.value}
handleClick={thisClass.handleClick} className={NUMBER} />)}
<Button id="equals" handleClick={thisClass.handleClick} className={EQUALS} content="="/>
<Button id="decimal" handleClick={thisClass.handleClick} className={DECIMAL} content="."/>
</div>
{buttons.slice(10).map(type => <Button id={type.name.toLowerCase()} content={type.value}
handleClick={thisClass.handleClick} className={OPERATION} />)}
<Button id="clear" handleClick={thisClass.handleClick} className={CLEAR} content="CLEAR"/>
</div>
);
}
};
const Button = (props) => {
return (
<button id={props.id} onClick={props.handleClick} className={props.className} >{props.content}</button>
);
}
const Display = (props) => {
return (
<div id="screen">
<p id="formula">{props.totalOperation}</p>
<h1 id="display">{props.currOperation}</h1>
</div>
);
}
// Redux code
const initialState = {
currOps: "0",
totalOps: "",
lastAction: NUMBER
}
let decimalPlaced = false;
const calcReducer = (state = initialState, action) => {
// What happens when switching actions, the most tedious part
if(state.lastAction !== action.type) {
// Special case after equal button is pressed
if(state.lastAction === "EQUALS") {
switch (action.type) {
// Push result from previous calc. to the main formula, and add operator as the currOps.
// Other operation after this is handled normally
case OPERATION:
return {
currOps: action.payload,
totalOps: state.currOps,
lastAction: action.type
};
case NUMBER:
decimalPlaced = false;
return {
currOps: action.payload,
totalOps: action.payload,
lastAction: action.type
};
}
}
if(action.type === NUMBER) {
decimalPlaced = false;
return {
currOps: action.payload,
totalOps: state.totalOps + state.currOps + action.payload,
lastAction: NUMBER
}
}
else if(action.type === OPERATION) {
return {
currOps: action.payload,
totalOps: state.totalOps,
lastAction: OPERATION
};
}
}
switch (action.type) {
case NUMBER:
// If zero is pressed, avoid multiple zeros
if(state.currOps === "0") {
if(action.payload === "0") {
return state
}
else {
// Overwrite zero when other number is pressed
return {
currOps: action.payload,
totalOps: action.payload,
lastAction: NUMBER
};
}
}
// Default behaviour for any number
return {
currOps: state.currOps + action.payload,
totalOps: state.totalOps + action.payload,
lastAction: NUMBER
};
case DECIMAL:
// Check if there's already a decimal point
if(!decimalPlaced) {
// Avoid multiple decimal points in a number
decimalPlaced = true;
return {
currOps: state.currOps + ".",
totalOps: state.totalOps + ".",
lastAction: NUMBER
};
}
return state;
case OPERATION:
// Special case for negative sign
if(action.payload === "-") {
if(state.currOps.includes("-")) {
return state;
}
return {
currOps: state.currOps + "-",
totalOps: state.totalOps,
lastAction: OPERATION
};
}
// Overwrite previous operator
return {
currOps: action.payload,
totalOps: state.totalOps,
lastAction: OPERATION
};
case EQUALS:
// From math.js library, evaluate use formula execution
let ans = math.evaluate(state.totalOps);
return {
currOps: ans,
totalOps: state.totalOps + "=" + ans,
lastAction: EQUALS
};
case CLEAR:
decimalPlaced = false;
return {
currOps: "0",
totalOps: "",
lastAction: NUMBER
}
default:
return state;
}
}
const store = Redux.createStore(calcReducer);
const Provider = ReactRedux.Provider;
const pushNumberAction = (number) => ({
type: NUMBER,
payload: number
});
const pushDecimalAction = () => ({
type: DECIMAL
});
const pushOpsAction = (ops) => ({
type: OPERATION,
payload: ops
});
const calcExpressionAction = () => ({
type: EQUALS
});
const clearDisplayAction = () => ({
type: CLEAR
});
const mapDispatchToProps = (dispatch) => ({
pushNumber: number => {
dispatch(pushNumberAction(number));
},
pushDecimal: () => {
dispatch(pushDecimalAction());
},
pushOps: ops => {
dispatch(pushOpsAction(ops));
},
calcExpression: () => {
dispatch(calcExpressionAction());
},
clearDisplay: () => {
dispatch(clearDisplayAction());
}
});
const mapStateToProps = state => ({
totalOps: state.totalOps,
currOps: state.currOps
});
const connect = ReactRedux.connect
const ConnectedCalc = connect(mapStateToProps, mapDispatchToProps)(Calculator);
// Main renderer
window.onload = () => {
ReactDOM.render(
<Provider store={store}>
<ConnectedCalc />
</Provider>
, document.getElementById("wrapper"));
}
View Compiled
This Pen doesn't use any external CSS resources.