├── .gitignore ├── public ├── favicon.png ├── manifest.json └── index.html ├── src ├── components │ ├── Wrapper.js │ ├── ButtonBox.css │ ├── ButtonBox.js │ ├── Wrapper.css │ ├── Button.js │ ├── Screen.css │ ├── Button.css │ └── Screen.js ├── index.js ├── index.css └── App.js ├── README.md └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | # dependencies 2 | /node_modules 3 | 4 | # production 5 | /build 6 | -------------------------------------------------------------------------------- /public/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madzadev/calculator/HEAD/public/favicon.png -------------------------------------------------------------------------------- /src/components/Wrapper.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import "./Wrapper.css"; 3 | 4 | const Wrapper = ({ children }) => { 5 | return
{children}
; 6 | }; 7 | 8 | export default Wrapper; 9 | -------------------------------------------------------------------------------- /src/components/ButtonBox.css: -------------------------------------------------------------------------------- 1 | .buttonBox { 2 | width: 100%; 3 | height: calc(100% - 110px); 4 | display: grid; 5 | grid-template-columns: repeat(4, 1fr); 6 | grid-template-rows: repeat(5, 1fr); 7 | grid-gap: 10px; 8 | } 9 | -------------------------------------------------------------------------------- /src/components/ButtonBox.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import "./ButtonBox.css"; 3 | 4 | const ButtonBox = ({ children }) => { 5 | return
{children}
; 6 | }; 7 | 8 | export default ButtonBox; 9 | -------------------------------------------------------------------------------- /src/components/Wrapper.css: -------------------------------------------------------------------------------- 1 | .wrapper { 2 | width: 340px; 3 | height: 540px; 4 | padding: 10px; 5 | border-radius: 10px; 6 | background-color: #485461; 7 | background-image: linear-gradient(315deg, #485461 0%, #28313b 74%); 8 | } 9 | -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "Calculator App", 3 | "name": "Calculator App by Madza", 4 | "icons": [ 5 | { 6 | "src": "favicon.png", 7 | "type": "image/x-icon" 8 | } 9 | ], 10 | "start_url": ".", 11 | "display": "standalone" 12 | } 13 | -------------------------------------------------------------------------------- /src/components/Button.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import "./Button.css"; 3 | 4 | const Button = ({ className, value, onClick }) => { 5 | return ( 6 | 9 | ); 10 | }; 11 | 12 | export default Button; 13 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom"; 3 | 4 | import App from "./App"; 5 | import "./index.css"; 6 | 7 | const rootElement = document.getElementById("root"); 8 | ReactDOM.render( 9 | 10 | 11 | , 12 | rootElement 13 | ); 14 | -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | @import url("https://fonts.googleapis.com/css2?family=Montserrat&display=swap"); 2 | 3 | * { 4 | margin: 0; 5 | padding: 0; 6 | font-family: "Montserrat", sans-serif; 7 | } 8 | 9 | body { 10 | height: 100vh; 11 | display: flex; 12 | align-items: center; 13 | justify-content: center; 14 | background-color: #fbb034; 15 | background-image: linear-gradient(315deg, #fbb034 0%, #ffdd00 74%); 16 | } 17 | -------------------------------------------------------------------------------- /src/components/Screen.css: -------------------------------------------------------------------------------- 1 | .screen { 2 | height: 100px; 3 | width: 340px; 4 | margin-bottom: 10px; 5 | padding: 0 10px; 6 | background-color: #4357692d; 7 | border-radius: 10px; 8 | display: flex; 9 | align-items: center; 10 | justify-content: flex-end; 11 | color: white; 12 | font-weight: bold; 13 | box-sizing: border-box; 14 | } 15 | 16 | .responsive-text { 17 | white-space: nowrap; 18 | overflow: hidden; 19 | margin: 0; 20 | } -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Calculator 9 | 10 | 11 | 12 |
13 | 14 | 15 | -------------------------------------------------------------------------------- /src/components/Button.css: -------------------------------------------------------------------------------- 1 | button { 2 | border: none; 3 | background-color: rgb(80, 60, 209); 4 | font-size: 24px; 5 | color: rgb(255, 255, 255); 6 | font-weight: bold; 7 | cursor: pointer; 8 | border-radius: 10px; 9 | outline: none; 10 | } 11 | 12 | button:hover { 13 | background-color: rgb(61, 43, 184); 14 | } 15 | 16 | .equals { 17 | grid-column: 3 / 5; 18 | background-color: rgb(243, 61, 29); 19 | } 20 | 21 | .equals:hover { 22 | background-color: rgb(228, 39, 15); 23 | } 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # React Calculator 2 | 3 | A calculator project built with React. 4 | 5 | ![Calculator Screenshot](https://i.imgur.com/O3CKScM.gif) 6 | 7 | ## Features 8 | 9 | 1. Add, subtract, multiply, divide 10 | 2. Support decimal values 11 | 3. Calculate percentages 12 | 4. Invert the values 13 | 5. Reset functionality 14 | 6. Format larger numbers 15 | 7. Output resize based on length 16 | 17 | ## Installation 18 | 19 | 1. `git clone https://github.com/madzadev/calculator.git` 20 | 21 | 2. `cd calculator` 22 | 23 | 3. `npm install` 24 | 25 | 4. `npm start` 26 | 27 | ## Contributions 28 | 29 | Any feature requests and pull requests are welcome! 30 | 31 | ## License 32 | 33 | The project is under [MIT license](https://choosealicense.com/licenses/mit/). 34 | -------------------------------------------------------------------------------- /src/components/Screen.js: -------------------------------------------------------------------------------- 1 | import React, { useRef, useEffect } from "react"; 2 | import "./Screen.css"; 3 | 4 | const Screen = ({ value }) => { 5 | const textRef = useRef(null); 6 | 7 | useEffect(() => { 8 | const adjustFontSize = () => { 9 | const element = textRef.current; 10 | let fontSize = 70; 11 | 12 | element.style.fontSize = `${fontSize}px`; 13 | 14 | while (element.scrollWidth > element.clientWidth && fontSize > 1) { 15 | fontSize--; 16 | element.style.fontSize = `${fontSize}px`; 17 | } 18 | }; 19 | 20 | adjustFontSize(); 21 | }, [value]); 22 | 23 | return ( 24 |
25 |

26 | {value} 27 |

28 |
29 | ); 30 | }; 31 | 32 | export default Screen; 33 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "homepage": "https://calculator-madza.netlify.app", 3 | "name": "calculator", 4 | "version": "0.1.0", 5 | "private": true, 6 | "dependencies": { 7 | "react": "^16.13.1", 8 | "react-dom": "^16.13.1", 9 | "react-scripts": "3.4.1" 10 | }, 11 | "scripts": { 12 | "start": "react-scripts --openssl-legacy-provider start", 13 | "build": "react-scripts build", 14 | "eject": "react-scripts eject" 15 | }, 16 | "eslintConfig": { 17 | "extends": "react-app" 18 | }, 19 | "browserslist": { 20 | "production": [ 21 | ">0.2%", 22 | "not dead", 23 | "not op_mini all" 24 | ], 25 | "development": [ 26 | "last 1 chrome version", 27 | "last 1 firefox version", 28 | "last 1 safari version" 29 | ] 30 | }, 31 | "devDependencies": { 32 | "@babel/preset-react": "^7.22.5" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | 3 | import Wrapper from "./components/Wrapper"; 4 | import Screen from "./components/Screen"; 5 | import ButtonBox from "./components/ButtonBox"; 6 | import Button from "./components/Button"; 7 | 8 | const btnValues = [ 9 | ["C", "+-", "%", "/"], 10 | [7, 8, 9, "X"], 11 | [4, 5, 6, "-"], 12 | [1, 2, 3, "+"], 13 | [0, ".", "="], 14 | ]; 15 | 16 | const toLocaleString = (num) => 17 | String(num).replace(/(? num.toString().replace(/\s/g, ""); 20 | 21 | const math = (a, b, sign) => 22 | sign === "+" ? a + b : sign === "-" ? a - b : sign === "X" ? a * b : a / b; 23 | 24 | const zeroDivisionError = "Can't divide with 0"; 25 | 26 | const App = () => { 27 | let [calc, setCalc] = useState({ 28 | sign: "", 29 | num: 0, 30 | res: 0, 31 | }); 32 | 33 | const numClickHandler = (e) => { 34 | e.preventDefault(); 35 | const value = e.target.innerHTML; 36 | if (removeSpaces(calc.num).length < 16) { 37 | setCalc({ 38 | ...calc, 39 | num: 40 | removeSpaces(calc.num) % 1 === 0 && !calc.num.toString().includes(".") 41 | ? toLocaleString(Number(removeSpaces(calc.num + value))) 42 | : toLocaleString(calc.num + value), 43 | res: !calc.sign ? 0 : calc.res, 44 | }); 45 | } 46 | }; 47 | 48 | const comaClickHandler = (e) => { 49 | e.preventDefault(); 50 | const value = e.target.innerHTML; 51 | 52 | setCalc({ 53 | ...calc, 54 | num: !calc.num.toString().includes(".") ? calc.num + value : calc.num, 55 | }); 56 | }; 57 | 58 | const signClickHandler = (e) => { 59 | setCalc({ 60 | ...calc, 61 | sign: e.target.innerHTML, 62 | res: !calc.num 63 | ? calc.res 64 | : !calc.res 65 | ? calc.num 66 | : toLocaleString( 67 | math( 68 | Number(removeSpaces(calc.res)), 69 | Number(removeSpaces(calc.num)), 70 | calc.sign 71 | ) 72 | ), 73 | num: 0, 74 | }); 75 | }; 76 | 77 | const equalsClickHandler = () => { 78 | if (calc.sign && calc.num) { 79 | setCalc({ 80 | ...calc, 81 | res: 82 | calc.num === "0" && calc.sign === "/" 83 | ? zeroDivisionError 84 | : toLocaleString( 85 | math( 86 | Number(removeSpaces(calc.res)), 87 | Number(removeSpaces(calc.num)), 88 | calc.sign 89 | ) 90 | ), 91 | sign: "", 92 | num: 0, 93 | }); 94 | } 95 | }; 96 | 97 | const invertClickHandler = () => { 98 | setCalc({ 99 | ...calc, 100 | num: calc.num ? toLocaleString(removeSpaces(calc.num) * -1) : 0, 101 | res: calc.res ? toLocaleString(removeSpaces(calc.res) * -1) : 0, 102 | sign: calc.sign, 103 | }); 104 | }; 105 | 106 | const percentClickHandler = () => { 107 | let num = calc.num ? parseFloat(removeSpaces(calc.num)) : 0; 108 | let res = calc.res ? parseFloat(removeSpaces(calc.res)) : 0; 109 | setCalc({ 110 | ...calc, 111 | num: (num /= Math.pow(100, 1)), 112 | res: (res /= Math.pow(100, 1)), 113 | sign: "", 114 | }); 115 | }; 116 | 117 | const resetClickHandler = () => { 118 | setCalc({ 119 | ...calc, 120 | sign: "", 121 | num: 0, 122 | res: 0, 123 | }); 124 | }; 125 | 126 | const buttonClickHandler = (e, btn) => { 127 | btn === "C" || calc.res === zeroDivisionError 128 | ? resetClickHandler() 129 | : btn === "+-" 130 | ? invertClickHandler() 131 | : btn === "%" 132 | ? percentClickHandler() 133 | : btn === "=" 134 | ? equalsClickHandler() 135 | : btn === "/" || btn === "X" || btn === "-" || btn === "+" 136 | ? signClickHandler(e) 137 | : btn === "." 138 | ? comaClickHandler(e) 139 | : numClickHandler(e); 140 | }; 141 | 142 | return ( 143 | 144 | 145 | 146 | {btnValues.flat().map((btn, i) => { 147 | return ( 148 |