commit 01e3ff5bba485bc88eaa5c498cc06d4ffc9649d8 Author: Yusuf İpek Date: Mon Jun 14 08:31:36 2021 +0300 finished project diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e621eeb --- /dev/null +++ b/.gitignore @@ -0,0 +1,17 @@ +# dependencies +/node_modules + +# design +/design + +# misc +.DS_Store +.env.local +.env.development.local +.env.test.local +.env.production.local + +npm-debug.log* +yarn-debug.log* +yarn-error.log* + diff --git a/README.md b/README.md new file mode 100644 index 0000000..f9110d5 --- /dev/null +++ b/README.md @@ -0,0 +1,58 @@ +# Another Calculator + +I've taken Frontend-Mentor challenge [https://www.frontendmentor.io/challenges/calculator-app-9lteq5N29](challenge link) and created this calculator. + +## Table of contents + +- [Overview](#overview) + - [The challenge](#the-challenge) + - [Screenshot](#screenshot) + - [Links](#links) +- [My process](#my-process) + - [Built with](#built-with) + - [Useful resources](#useful-resources) +- [Author](#author) + +## Overview + +### The Challenge + +Users should be able to: + +- See the size of the elements adjust based on their device's screen size +- Perform mathmatical operations like addition, subtraction, multiplication, and division +- Adjust the color theme based on their preference +- Have their theme preference check and saved + +### Screenshot + +![desktop-purple](./public/desktop-purple.png) + +![landscape-light](./public/landscape-light.png) + +![mobile-blue](./public/mobile-blue.png) + +### Links + +- [Solution URL](https://github.com/yusufipk/another-calculator) +- [Live Site URL](https://yusufipk.github.io/another-calculator/) + +## My process + +### Built With + +- HTML5 +- CSS custom properties +- Grid +- Flexbox +- Desktop-first workflow + +### Useful Resources + +- [Saving theme with localstorage](https://dev.to/mritunjaysaha/theme-switching-using-local-storage-13i) - This article shows you how to save themes using localstorage. + +## Author + +- Website - [Yusuf İpek](https://yusufipek.me) +- Frontend Mentor - [@yusufipk](https://www.frontendmentor.io/profile/yusufipk) +- Youtube - [Youtube](https://www.youtube.com/channel/UCVBX2n_5egE9XuJL8NUS0Xg) diff --git a/css/App.css b/css/App.css new file mode 100644 index 0000000..370da6b --- /dev/null +++ b/css/App.css @@ -0,0 +1,319 @@ +:root { + --color-main: #3b4664; + --color-white: #ffffff; + + --bg-main: #181f32; + --bg-buttons: rgba(37, 45, 68, 0.6); + + --num: #eae3db; + --number-color: #484b5a; + --number-border: #b3a599; + --button-secondary: #647299; + --border-secondary: #404d70; + --button-tertiary: #d13f30; + --border-tertiary: #922417; + --radio-color: #cf3f34; +} + +body[data-theme="light"] { + --color-main: #e6e6e6; + --color-white: #3a3a32; + + --bg-main: #eeeeee; + --bg-buttons: #d3cdcd; + + --num: #e5e4e0; + --number-color: #3a3a32; + --number-border: #a69c93; + --button-secondary: #388187; + --border-secondary: #1a5d66; + --button-tertiary: #c85401; + --border-tertiary: #873a02; +} + +body[data-theme="purple"] { + --color-main: #17062a; + --color-white: #ffe43b; + + --bg-main: #1e0836; + --bg-buttons: #1e0836; + + --num: #331b4d; + --number-color: #ffe537; + --number-border: #85219b; + --button-secondary: #56077c; + --border-secondary: #bf15f4; + --button-tertiary: #00decf; + --border-tertiary: #6dfaf1; + --radio-color: #00e8d8; +} + +*, +*::after, +*::before { + margin: 0; + padding: 0; + box-sizing: inherit; +} + +html { + font-size: 62.5%; +} + +@media screen and (max-width: 550px) { + html { + font-size: 50%; + } +} + +body { + box-sizing: border-box; + background-color: var(--color-main); + color: var(--color-white); + + transition: background-color 0.5s; +} + +/* LAYOUT */ + +.container { + display: grid; + justify-content: center; + align-content: center; + min-height: 100vh; + + grid-template-columns: repeat(4, 12rem); + grid-template-rows: 7rem minmax(12rem, auto) auto; +} + +.theme { + grid-row: 2; + grid-column: 4; + + display: grid; + grid-template-rows: repeat(2, 1fr); + + justify-items: right; +} + +/* HEADER */ + +.header { + grid-column: 1/-1; + align-items: center; + + /* display: flex; */ + /* justify-content: space-between; */ + + display: grid; + grid-template-columns: repeat(4, 1fr); + grid-template-rows: repeat(2, 1fr); +} + +.header h4 { + grid-row: 2; + font-size: 3rem; + margin-bottom: -1.2rem; +} + +.header p { + font-size: 1.2rem; + font-weight: 800; + + margin-left: 1.5rem; + + grid-row: 2; + grid-column: 3; + + display: flex; + align-self: flex-end; + justify-self: right; + margin-bottom: 3px; + margin-right: -5rem; +} + +/* DISPLAY */ + +.display { + grid-column: 1/-1; + + background-color: var(--bg-main); + border-radius: 5px; + padding: 10px; + margin-top: 1.5rem; + + display: flex; + flex-direction: column; + align-items: flex-end; + justify-content: space-around; + + word-wrap: break-word; + word-break: break-all; + + margin-bottom: 1.3rem; +} + +.display .previousOperand { + font-size: 2rem; +} + +.display .currentOperand { + font-size: 3rem; +} + +/* BUTTONS */ + +.buttons { + background-color: var(--bg-buttons); + grid-column: 1 / -1; + + display: grid; + grid-gap: 1.5rem; + border-radius: 5px; + + justify-items: center; + justify-content: center; + + grid-template-columns: repeat(4, 100px); + + padding: 3rem; +} + +.span-2 { + width: 100%; + grid-column: span 2; +} + +button { + outline: none; + cursor: pointer; + + width: 100%; + padding-top: 0.5rem; + padding-bottom: 0.5rem; + + background-color: var(--num); + color: var(--number-color); + font-size: 3.3rem; + font-weight: 900; + border: none; + border-radius: 10px; + + box-shadow: 0 4px var(--number-border); +} + +button:active { + box-shadow: 0 2px var(--number-border); +} + +@media screen and (max-width: 550px) { + .buttons { + grid-template-columns: repeat(4, 80px); + } + button { + padding: 0; + border-radius: 8px; + } +} + +@media screen and (max-width: 415px) { + .container { + grid-template-columns: repeat(4, 10rem); + } + .buttons { + grid-template-columns: repeat(4, 65px); + } + button { + padding: 0; + } + + .radio { + width: 55% !important; + } + + .header p { + margin-right: -4rem; + } +} +@media screen and (max-width: 370px) { + .container { + grid-template-columns: repeat(4, 9rem); + } + .buttons { + grid-template-columns: repeat(4, 55px); + } + .radio { + width: 60% !important; + } + .header p { + margin-right: -3rem; + } + button { + padding: 0; + border-radius: 8px; + } + .blue { + font-size: 1.8rem !important; + } +} + +.blue { + background-color: var(--button-secondary); + color: #ffffff; + font-size: 2.2rem; + padding: 1.3rem; + box-shadow: 0 4px var(--border-secondary); +} + +.blue:active { + box-shadow: 0 2px var(--border-secondary); +} + +.red { + background-color: var(--button-tertiary); + color: #ffffff; + font-size: 2.2rem; + box-shadow: 0 4px var(--border-tertiary); +} + +.red:active { + box-shadow: 0 2px var(--border-tertiary); +} + +/* radio */ + +.radio { + background-color: var(--bg-buttons); + padding: 0.4rem 0.4rem 0.3rem 0.3rem; + border-radius: 0.9rem; + + grid-row: 2; + grid-column: 1/-1; + width: 46%; +} + +input[type="radio"] { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + opacity: 0; + cursor: pointer; + width: 1rem; + height: 1rem; + border-radius: 1rem; + margin-left: 0.4rem; +} +input[type="radio"]:checked { + display: inline-block; + + opacity: 1; + background-color: var(--radio-color); + border-color: var(--radio-color); +} + +.labels { + grid-column: 1/-1; + + letter-spacing: 0.35rem; + font-size: 1.4rem; +} diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..faae1ef --- /dev/null +++ b/package-lock.json @@ -0,0 +1,12 @@ +{ + "name": "another-calculator-app", + "version": "1.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "version": "1.0.0", + "license": "ISC" + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..3e9aeed --- /dev/null +++ b/package.json @@ -0,0 +1,12 @@ +{ + "name": "another-calculator-app", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC" +} diff --git a/public/desktop-purple.png b/public/desktop-purple.png new file mode 100644 index 0000000..f4be5ca Binary files /dev/null and b/public/desktop-purple.png differ diff --git a/public/landscape-light.png b/public/landscape-light.png new file mode 100644 index 0000000..a9269d1 Binary files /dev/null and b/public/landscape-light.png differ diff --git a/public/mobile-blue.png b/public/mobile-blue.png new file mode 100644 index 0000000..0dd3a55 Binary files /dev/null and b/public/mobile-blue.png differ diff --git a/src/app.js b/src/app.js new file mode 100644 index 0000000..7fca7ee --- /dev/null +++ b/src/app.js @@ -0,0 +1,221 @@ +// Theme +const theme1 = document.getElementById("theme1"); +const theme2 = document.getElementById("theme2"); +const theme3 = document.getElementById("theme3"); + +if (localStorage.getItem("theme") === null) { + localStorage.setItem("theme", "theme1"); +} + +switch (localStorage.getItem("theme")) { + case "theme1": + theme1.checked = true; + break; + case "theme2": + theme2.checked = true; + break; + case "theme3": + theme3.checked = true; + break; +} + +theme1.addEventListener("click", () => { + theme2.checked = false; + theme3.checked = false; + localStorage.setItem("theme", "theme1"); +}); +theme1.addEventListener("click", changeTheme); + +theme2.addEventListener("click", () => { + theme1.checked = false; + theme3.checked = false; + localStorage.setItem("theme", "theme2"); +}); +theme2.addEventListener("click", changeTheme); + +theme3.addEventListener("click", () => { + theme1.checked = false; + theme2.checked = false; + localStorage.setItem("theme", "theme3"); +}); +theme3.addEventListener("click", changeTheme); + +function changeTheme() { + if (theme1.checked) { + document.body.setAttribute("data-theme", ""); + } else if (theme2.checked) { + document.body.setAttribute("data-theme", "light"); + } else if (theme3.checked) { + document.body.setAttribute("data-theme", "purple"); + } +} + +changeTheme(); + +// Functionality +class Calculator { + constructor(prevOperandTextElement, currentOperandElement) { + this.prevOperandTextElement = prevOperandTextElement; + this.currentOperandTextElement = currentOperandTextElement; + this.clear(); + } + + clear() { + this.currentOperand = ""; + this.prevOperand = ""; + this.operation = undefined; + } + + delete() { + this.currentOperand = this.currentOperand.toString().slice("0", -1); + } + + appendNumber(number) { + if (number === "." && this.currentOperand.includes(".")) return; + this.currentOperand = + this.currentOperand.toString() + number.toString("number"); + } + + chooseOperation(operation) { + if (this.currentOperand === "") return; + if (this.prevOperand !== "") { + this.compute(); + } + this.operation = operation; + this.prevOperand = this.currentOperand; + this.currentOperand = ""; + } + + compute() { + let computation; + + const prev = parseFloat(this.prevOperand); + const current = parseFloat(this.currentOperand); + if (isNaN(prev) || isNaN(current)) return; + + switch (this.operation) { + case "+": + computation = prev + current; + break; + case "-": + computation = prev - current; + break; + case "/": + computation = prev / current; + break; + case "x": + computation = prev * current; + break; + case "*": + computation = prev * current; + break; + default: + return; + } + this.currentOperand = computation; + this.operation = undefined; + this.prevOperand = ""; + } + + getDisplayNumber(number) { + const stringNumber = number.toString(); + const integerDigits = parseFloat(stringNumber.split(".")[0]); + const decimalDigits = stringNumber.split(".")[1]; + let integerDisplay; + if (isNaN(integerDigits)) { + integerDisplay = ""; + } else { + integerDisplay = integerDigits.toLocaleString("en", { + maximumFractionDigits: 0, + }); + } + + if (decimalDigits != null) { + return `${integerDisplay}.${decimalDigits}`; + } else { + return integerDisplay; + } + } + + updateDisplay() { + this.currentOperandTextElement.innerText = this.getDisplayNumber( + this.currentOperand + ); + this.prevOperandTextElement.innerText = this.getDisplayNumber( + this.prevOperand + ); + + if (this.operation != undefined) { + this.prevOperandTextElement.innerText = `${this.getDisplayNumber( + this.prevOperand + )} ${this.operation}`; + } + } +} + +const numberButtons = document.querySelectorAll("[data-number]"); +const operationButtons = document.querySelectorAll("[data-operation]"); +const equalsButton = document.querySelector("[data-equals]"); +const deleteButton = document.querySelector("[data-del]"); +const reset = document.querySelector("[data-reset]"); +const prevOperandTextElement = document.querySelector( + "[data-previous-operand]" +); +const currentOperandTextElement = document.querySelector( + "[data-current-operand]" +); + +const calculator = new Calculator( + prevOperandTextElement, + currentOperandTextElement +); + +numberButtons.forEach((button) => { + button.addEventListener("click", () => { + calculator.appendNumber(button.innerText); + calculator.updateDisplay(); + }); +}); + +operationButtons.forEach((button) => { + button.addEventListener("click", () => { + calculator.chooseOperation(button.innerText); + calculator.updateDisplay(); + }); +}); + +equalsButton.addEventListener("click", (button) => { + calculator.compute(); + calculator.updateDisplay(); +}); + +reset.addEventListener("click", (button) => { + calculator.clear(); + calculator.updateDisplay(); +}); + +deleteButton.addEventListener("click", (button) => { + calculator.delete(); + calculator.updateDisplay(); +}); + +document.body.addEventListener("keydown", (e) => { + if (isNaN(e.key) === false || e.key === ".") { + calculator.appendNumber(e.key); + calculator.updateDisplay(); + } else if (e.key === "+" || e.key === "/" || e.key === "*" || e.key === "-") { + calculator.chooseOperation(e.key); + calculator.updateDisplay(); + } else if (e.key === "Enter") { + calculator.compute(); + calculator.updateDisplay(); + } else if (e.key === "Backspace") { + calculator.delete(); + calculator.updateDisplay(); + } else if (e.key === "Delete") { + calculator.clear(); + calculator.updateDisplay(); + } else { + return; + } +}); diff --git a/src/index.html b/src/index.html new file mode 100644 index 0000000..e6beb1b --- /dev/null +++ b/src/index.html @@ -0,0 +1,67 @@ + + + + + + Another Calculator App + + + + +
+
+

calc

+

THEME

+
+
+ + + +
+
+ + + +
+
+
+ +
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +