@import url('https://fonts.googleapis.com/css?family=Source+Code+Pro');
$font-google: 'Source Code Pro', monospace;
$font-google-2: 'Orbitron', monospace;
// VARS
// Color vars
// $color1: #F4DF42;
$color1: #42F4D1;
$color2: #000;
// MIXINS
// Global box shadow mixin
// Set some default values that can be altered
@mixin global-box-shadow($x: 3px, $y: 3px) {
box-shadow: #{$x} #{$y} 0 $color2;
}
* {
box-sizing: border-box;
}
body {
background: linear-gradient(to bottom, $color1 0%, $color1 85%, $color2 85%);
background-size: cover;
font-family: $font-google;
padding: 16px;
}
a:link,
a:visited {
color: $color2;
}
a:hover,
a:active {
text-decoration: none;
}
.calculator-wrap {
cursor: grab;
width: 40vmax;
min-width: 300px;
margin: 20px auto;
}
.calculator-inner {
@include global-box-shadow(6px, 6px);
background-color: $color1;
border: 3px solid $color2;
border-top: none;
display: block;
padding: 20px;
position: relative;
z-index: 2;
&.ui-draggable-dragging,
&:active {
cursor: grabbing;
}
}
.calc-title {
background: mix($color1, $color2, 80%);
color: $color2;
margin: -20px -20px 0;
padding: 20px;
border: 2px solid $color2;
@include global-box-shadow(6px, 6px);
text-transform: uppercase;
text-align: center;
width: 100%;
transform: skew(-8deg) translateX(1.7rem);
}
#calc-display {
@include global-box-shadow;
border: 2px solid $color2;
background-color: transparent;
font-family: $font-google-2;
border-radius: 2px;
display: block;
margin-bottom: 10px;
padding: 12px 12px;
width: 100%;
}
.buttons-wrap {
display: flex;
}
.buttons-operators {
align-items: flex-start;
display: flex;
flex-wrap: wrap;
justify-content: space-around;
}
.button-numbers {
width: 72%;
margin-right: 3%;
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: space-between;
button {
width: 30%;
&:nth-child(3n) {
margin-right: 0;
}
}
}
.buttons-operators {
display: flex;
width: 25%;
button {
background: mix($color1, $color2, 80%);
font-size: 1.25em;
flex-basis: 100%;
}
}
.buttons-methods {
display: flex;
flex-direction: row;
align-items: stretch;
flex-wrap: nowrap;
justify-content: space-between;
button {
background: mix($color1, $color2, 80%);
font-size: 1.25em;
width: 48.5%;
}
}
button {
@include global-box-shadow;
appearance: none;
background-color: transparent;
border: 2px solid $color2;
font-weight: 900;
font-family: $font-google-2;
font-size: 16px;
margin-bottom: 8px;
outline: none;
padding: 8px;
&:active {
box-shadow: none;
transform: translateX(3px) translateY(3px);
outline: none;
}
}
[id="calculator"]:before {
color: $color2;
content: 'DEBUG: Mode = ' attr(data-calc-mode);
font-family: $font-google;
font-size: 12px;
text-transform: uppercase;
position: absolute;
bottom: 7px;
left: 25px;
display: block;
}
// Turn off debug msg via js
.is-debug-mode:before {
content: '';
}
.colophon {
color: $color1;
font-size: 13px;
text-align: center;
margin: 40px auto;
position: relative;
z-index: 1;
a {
color: $color1;
}
}
View Compiled
'use strict';
let calculator = document.getElementById('calculator');
let calcDisplayEl = document.getElementById('calc-display');
let calcDisplayValue = calcDisplayEl.value;
let currentCalcMode = calculator.dataset.calcMode;
let calcBtn = document.getElementById('calc-btn');
let clearBtn = document.getElementById('clear-btn');
let decimalBtn = document.getElementById('decimal-btn');
let percentageBtn = document.getElementById('percentage-btn');
let memory = [];
let debug = false;
// If in debug mode, we output console logs and show debug info
if(!debug) {
// Turn off all console logs conveniently
console.log = function() {}
// Turn off debug data displaying calc mode
calculator.classList.add('is-debug-mode');
}
// Make calculator draggable
$('.calculator-wrap').draggable({
addClasses: true,
});
// Array of number button elements
let numbers = [
document.getElementById('btn-1'),
document.getElementById('btn-2'),
document.getElementById('btn-3'),
document.getElementById('btn-4'),
document.getElementById('btn-5'),
document.getElementById('btn-6'),
document.getElementById('btn-7'),
document.getElementById('btn-8'),
document.getElementById('btn-9'),
document.getElementById('btn-0')
];
// Array of operator button elements
let operators = [
document.getElementById('add-btn'),
document.getElementById('subtract-btn'),
document.getElementById('divide-btn'),
document.getElementById('multiply-btn'),
];
numbers.forEach(function(element) {
// console.log(element);
element.addEventListener('click', function(event) {
event.preventDefault();
// Use 'data-calc-mode' attr on form to set mode
// Mode can be 'number' if number last pressed,
// or 'operator' if operator last pressed'.
currentCalcMode = calculator.dataset.calcMode;
////////////////////////////////////////////////////////
// CALC MODE: INITIAL
////////////////////////////////////////////////////////
if(currentCalcMode === 'initial') {
// Calc is reset to initial. Update display with button value pressed
calcDisplayEl.value = this.value;
// Place this value into memory
memory.push(calcDisplayEl.value);
}
////////////////////////////////////////////////////////
// CALC MODE: NUMBER
////////////////////////////////////////////////////////
if(currentCalcMode === 'number') {
// append the last number pressed to the value
calcDisplayEl.value = calcDisplayEl.value + this.value;
// We remove the last item (a number) from the array, and then update the array with the currect value
memory.pop();
// Update memory
memory.push(calcDisplayEl.value);
}
////////////////////////////////////////////////////////
// CALC MODE: OPERATOR
////////////////////////////////////////////////////////
if(currentCalcMode === 'operator') {
calcDisplayEl.value = this.value;
memory.push(calcDisplayEl.value);
console.log(memory);
}
// Since number has been presed, set data-calc-mode attr to 'number'
// Get updated value of calc-display
let currentDisplayEl = document.getElementById('calc-display');
// Set calc mode to number
calculator.dataset.calcMode = 'number';
// Update calc mode var
currentCalcMode = calculator.dataset.calcMode;
let currentDisplayVal = parseInt(currentDisplayEl.value);
// Place current display value into memory
// memory.push(currentDisplayVal);
console.log(memory);
}, false);
});
// Bind click event to each operator button
operators.forEach(function(element) {
element.addEventListener('click', function(event) {
event.preventDefault();
// Gets value from buttons data-operator attr.
let buttonOperator = this.getAttribute('data-operator');
memory.push({
'operator': buttonOperator
});
// Set calc mode to operator
calculator.dataset.calcMode = 'operator';
console.log(memory);
})
});
////////////////////////////////////////////////////////
// CLEAR BUTTON CLICK
////////////////////////////////////////////////////////
clearBtn.addEventListener('click', (event) => {
event.preventDefault();
// Reset some stuff
calcDisplayEl.value = "0";
memory = [];
// Set calc mode to initial
calculator.dataset.calcMode = 'initial';
// console.log(memory);
console.clear();
}, false);
////////////////////////////////////////////////////////
// DECIMAL BUTTON CLICK
////////////////////////////////////////////////////////
decimalBtn.addEventListener('click', (event) => {
event.preventDefault();
// If displayValue !== 0, append decimal point
if(calcDisplayEl.value !== '0') {
// append the last number pressed to the value
calcDisplayEl.value = calcDisplayEl.value + '.';
}
}, false);
////////////////////////////////////////////////////////
// PERCENTAGE BUTTON CLICK
////////////////////////////////////////////////////////
percentageBtn.addEventListener('click', (event) => {
event.preventDefault();
// If displayValue !== 0, append decimal point
if(calcDisplayEl.value !== '0') {
// append the last number pressed to the value
calcDisplayEl.value = parseInt(calcDisplayEl.value) / 100;
}
memory = [calcDisplayEl.value];
console.log(memory);
}, false);
////////////////////////////////////////////////////////
// CALC BUTTON CLICK
////////////////////////////////////////////////////////
calcBtn.addEventListener('click', (event) => {
event.preventDefault();
console.log(memory);
// Lets calculate a result.
let previousVal = memory[0];
let operator = memory[1];
let currentVal = memory[2];
// Operator returns a function name,
// either 'add', 'subtract', 'multiple' or 'divide'
// We us this funtion to calculate a result
let calcMethodToUse = operator.operator;
let result = '';
switch(calcMethodToUse) {
case 'add':
result = add(previousVal, currentVal);
break;
case 'subtract':
result = subtract(previousVal, currentVal);
break;
case 'multiply':
result = multiply(previousVal, currentVal);
break;
case 'divide':
result = divide(previousVal, currentVal);
break;
}
calcDisplayEl.value = result;
memory = [result];
console.log(memory);
}, false);
/////////////////////////////////////////////////////////
// OPERATOR FUNCTIONS
/////////////////////////////////////////////////////////
// Addition
const add = (a,b) => {
return(+a + +b)
}
// Subtraction
const subtract = (a,b) => {
return(a - b)
}
// Multiply
const multiply = (a,b) => {
return(a * b)
}
// Divide
const divide = (a,b) => {
return(a / b)
}
/////////////////////////////////////////////////////////
// KEY BINDINGS
/////////////////////////////////////////////////////////
window.addEventListener("keyup", checkKeyPressed, false);
function checkKeyPressed(e) {
console.log('click');
// If displayfield is in focus, we can type numbers in directly instead of triggering a click
if(calcDisplayEl.is(':focus')) {
return;
}
// Numbers 0-9
for(let i = 48; i < 58; i++) {
console.log(i);
if (e.keyCode == [i]) {
$('[data-btn-val="' + ([i] - 48) + '"]').trigger('click');
}
}
// Plus key
if (e.which == 107) {
$('[data-operator="add"]').trigger('click');
}
// Minus key
if (e.which == 109) {
$('[data-operator="subtract"]').trigger('click');
}
// Enter (or equals) key
if (e.which == 13 || e.keyCode == 187) {
$('[data-operator="equals"]').trigger('click');
}
// Clear key (cmd + c)
if (e.keyCode == 67) {
console.log('cmd + c');
$('[data-operator="clear"]').trigger('click');
}
}
View Compiled