diff --git a/package-lock.json b/package-lock.json index ba01ea4..3f3539c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3768,6 +3768,11 @@ "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=" }, + "complex.js": { + "version": "2.0.15", + "resolved": "https://registry.npmjs.org/complex.js/-/complex.js-2.0.15.tgz", + "integrity": "sha512-gDBvQU8IG139ZBQTSo2qvDFP+lANMGluM779csXOr6ny1NUtA3wkUnCFjlDNH/moAVfXtvClYt6G0zarFbtz5w==" + }, "compressible": { "version": "2.0.18", "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", @@ -4627,6 +4632,11 @@ "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" }, + "escape-latex": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/escape-latex/-/escape-latex-1.2.0.tgz", + "integrity": "sha512-nV5aVWW1K0wEiUIEdZ4erkGGH8mDxGyxSeqPzRNtWP7ataw+/olFObw7hujFWlVjNsaDFw5VZ5NzVSIqRgfTiw==" + }, "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", @@ -6337,6 +6347,11 @@ } } }, + "javascript-natural-sort": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/javascript-natural-sort/-/javascript-natural-sort-0.7.1.tgz", + "integrity": "sha1-+eIwPUUH9tdDVac2ZNFED7Wg71k=" + }, "jest": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/jest/-/jest-27.5.1.tgz", @@ -7834,6 +7849,22 @@ "tmpl": "1.0.5" } }, + "mathjs": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/mathjs/-/mathjs-10.4.0.tgz", + "integrity": "sha512-2zVrKvORwxeWbewweD5NDxkTgAMvqkwmMljHUPhDPJtX4sGJEL6ugFXQN597rhH9RGtnMxNzz/+hPGIr9UEWrg==", + "requires": { + "@babel/runtime": "^7.17.2", + "complex.js": "^2.0.15", + "decimal.js": "^10.3.1", + "escape-latex": "^1.2.0", + "fraction.js": "^4.2.0", + "javascript-natural-sort": "^0.7.1", + "seedrandom": "^3.0.5", + "tiny-emitter": "^2.1.0", + "typed-function": "^2.0.0" + } + }, "mdn-data": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz", @@ -9897,6 +9928,11 @@ "ajv-keywords": "^3.5.2" } }, + "seedrandom": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/seedrandom/-/seedrandom-3.0.5.tgz", + "integrity": "sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg==" + }, "select-hose": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", @@ -10593,6 +10629,11 @@ "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz", "integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=" }, + "tiny-emitter": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz", + "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==" + }, "tmpl": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", @@ -10707,6 +10748,11 @@ "mime-types": "~2.1.24" } }, + "typed-function": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/typed-function/-/typed-function-2.1.0.tgz", + "integrity": "sha512-bctQIOqx2iVbWGDGPWwIm18QScpu2XRmkC19D8rQGFsjKSgteq/o1hTZvIG/wuDq8fanpBDrLkLq+aEN/6y5XQ==" + }, "typedarray-to-buffer": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", diff --git a/package.json b/package.json index 7ef6489..994fd9f 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,7 @@ "@testing-library/jest-dom": "^5.16.2", "@testing-library/react": "^12.1.4", "@testing-library/user-event": "^13.5.0", + "mathjs": "^10.4.0", "react": "^17.0.2", "react-dom": "^17.0.2", "react-scripts": "5.0.0", diff --git a/src/App.css b/src/App.css deleted file mode 100644 index 4931613..0000000 --- a/src/App.css +++ /dev/null @@ -1,3 +0,0 @@ -.App { - text-align: center; -} diff --git a/src/App.js b/src/App.js index bcffb8e..b87c513 100644 --- a/src/App.js +++ b/src/App.js @@ -1,7 +1,10 @@ -import "./App.css"; - +import Calculator from "./components/Calculator"; function App() { - return <>; + return ( + <> + + + ); } export default App; diff --git a/src/components/Buttons.js b/src/components/Buttons.js new file mode 100644 index 0000000..0eb1661 --- /dev/null +++ b/src/components/Buttons.js @@ -0,0 +1,90 @@ +import React from "react"; +import "./styles/Buttons.css"; +// import CALCULATOR_BUTTONS from "./CalculatorButtons"; + +const Buttons = ({ + inputHandler, + clearInput, + backspace, + changePlusMinus, + calculateAns, +}) => { + return ( +
+ + + + + + + + + + + + + + + + + + + + + + + + +
+ ); +}; + +export default Buttons; diff --git a/src/components/Calculator.js b/src/components/Calculator.js new file mode 100644 index 0000000..e3bb41b --- /dev/null +++ b/src/components/Calculator.js @@ -0,0 +1,144 @@ +import React, { useState } from "react"; +import Display from "./Display"; +import Buttons from "./Buttons"; +import "./styles/Calculator.css"; +import { evaluate, round } from "mathjs"; + +function Calculator() { + const [input, setInput] = useState(""); + const [answer, setAnswer] = useState(""); + + //input + const inputHandler = (event) => { + if (answer === "Invalid Input!!") return; + let val = event.target.innerText; + let str = input + val; + if (str.length > 14) return; + + if (answer !== "") { + setInput(answer + val); + setAnswer(""); + } else setInput(str); + // setInput(str); + }; + + //Clear screen + const clearInput = () => { + setInput(""); + setAnswer(""); + }; + + // check brackets are balanced or not + const checkBracketBalanced = (expr) => { + let stack = []; + for (let i = 0; i < expr.length; i++) { + let x = expr[i]; + if (x === "(") { + stack.push(x); + continue; + } + + if (x === ")") { + if (stack.length === 0) return false; + else stack.pop(); + } + } + return stack.length === 0; + }; + + // calculate final answer + const calculateAns = () => { + if (input === "") return; + let result = 0; + let finalexpression = input; + // finalexpression = input.replaceAll("^", "**"); //for eval() + finalexpression = finalexpression.replaceAll("x", "*"); + finalexpression = finalexpression.replaceAll("÷", "/"); + + // evaluate square root + let noSqrt = input.match(/√[0-9]+/gi); + + if (noSqrt !== null) { + let evalSqrt = input; + for (let i = 0; i < noSqrt.length; i++) { + evalSqrt = evalSqrt.replace( + noSqrt[i], + `sqrt(${noSqrt[i].substring(1)})` + ); + } + finalexpression = evalSqrt; + } + + try { + // check brackets are balanced or not + if (!checkBracketBalanced(finalexpression)) { + const errorMessage = { message: "Brackets are not balanced!" }; + throw errorMessage; + } + result = evaluate(finalexpression); //mathjs + } catch (error) { + result = + error.message === "Brackets are not balanced!" + ? "Brackets are not balanced!" + : "Invalid Input!!"; //error.message; + } + isNaN(result) ? setAnswer(result) : setAnswer(round(result, 3)); + }; + + // remove last character + const backspace = () => { + if (answer !== "") { + setInput(answer.toString().slice(0, -1)); + setAnswer(""); + } else setInput((prev) => prev.slice(0, -1)); + }; + + // change prefix of expression + const changePlusMinus = () => { + //need to change for answer + if (answer === "Invalid Input!!") return; + else if (answer !== "") { + if (input.charAt(0) === "-") { + let plus = "+"; + setInput(plus.concat(answer.slice(1, answer.length))); + } else if (input.charAt(0) === "+") { + let minus = "-"; + setInput(minus.concat(answer.slice(1, answer.length))); + } else { + let minus = "-"; + setInput(minus.concat(answer)); + } + setAnswer(""); + } else { + if (input.charAt(0) === "-") { + let plus = "+"; + setInput((prev) => plus.concat(prev.slice(1, prev.length))); + } else if (input.charAt(0) === "+") { + let minus = "-"; + setInput((prev) => minus.concat(prev.slice(1, prev.length))); + } else { + let minus = "-"; + setInput((prev) => minus.concat(prev)); + } + } + }; + + return ( + <> +
+
+ + +
+
+ + ); +} + +export default Calculator; diff --git a/src/components/Display.js b/src/components/Display.js new file mode 100644 index 0000000..3fe6267 --- /dev/null +++ b/src/components/Display.js @@ -0,0 +1,56 @@ +import React from "react"; +import "./styles/Display.css"; + +const Display = ({ input, setInput, answer }) => { + // const onChangeTagInput = (e) => { + // // setInputVal(e.target.value.replace(/^[0-9 ()+-]+$/, "a")); + // // if(e.target.value === "1") + // setInput(e.target.value); + // // console.log(e.target.value.test(/^[0-9 ()+-]+$/)); + // // console.log(e.target.value); + // }; + + return ( + <> +
+ {answer === "" ? ( + <> + + + ) : ( + <> + + + + )} +
+ + ); +}; + +export default Display; diff --git a/src/components/styles/Buttons.css b/src/components/styles/Buttons.css new file mode 100644 index 0000000..b895f6c --- /dev/null +++ b/src/components/styles/Buttons.css @@ -0,0 +1,39 @@ +.show-btn { + display: grid; + grid-template-columns: auto auto auto auto; + background-color: white; + border-bottom-left-radius: 10px; + border-bottom-right-radius: 10px; +} + +.btn { + width: 80px; + height: 80px; + margin: 6px auto; + font-size: 24px; + font-weight: 500; + cursor: pointer; + border-radius: 50%; + background: none; + border: 0px; +} + +.btn:hover { + background-color: #6dd5ed; + color: white; + transition: background-color 0.3s ease; +} + +.btn:active { + background-color: #48b1bf; + color: white; + transition: background-color 0.3s ease; +} + +.clr { + color: #d66d75; +} + +.exp { + color: #06beb6; +} diff --git a/src/components/styles/Calculator.css b/src/components/styles/Calculator.css new file mode 100644 index 0000000..8c50735 --- /dev/null +++ b/src/components/styles/Calculator.css @@ -0,0 +1,22 @@ +.container { + background-color: gainsboro; + min-height: 100vh; + min-width: 100%; + display: flex; + align-items: center; + justify-content: center; +} + +.main { + border: 0px; + box-shadow: 0px 0px 3px 0.5px #c4e0e5; + border-radius: 10px; + max-width: 400px; +} + +@media (max-width: 600px) { + .main { + width: 100%; + height: 100%; + } +} diff --git a/src/components/styles/Display.css b/src/components/styles/Display.css new file mode 100644 index 0000000..67eb978 --- /dev/null +++ b/src/components/styles/Display.css @@ -0,0 +1,44 @@ +.display { + display: flex; + flex-direction: column; + padding: 10px; + border-top-left-radius: 10px; + border-top-right-radius: 10px; + padding-bottom: 20px; + background: linear-gradient(90deg, #06beb6, #48b1bf 100%); +} + +.expression { + padding: 5px; + margin-bottom: 10px; + height: 50px; +} + +.input, +.value { + padding: 10px; + text-align: right; + background-color: transparent; + color: white; + border: 0px; + letter-spacing: 1.5px; +} + +.input { + font-size: 35px; +} + +.value { + font-size: 15px; +} + +.input::placeholder, +.value::placeholder { + color: white; +} + +.input:focus-visible, +.value:focus-visible { + outline-style: none; + outline-width: 0px; +}