├── src ├── App.css ├── index.css ├── main.jsx ├── App.jsx └── assets │ └── react.svg ├── vite.config.js ├── .gitignore ├── index.html ├── README.md ├── .gitpod.yml ├── .eslintrc.cjs ├── package.json ├── LICENSE └── public └── vite.svg /src/App.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | }) 8 | -------------------------------------------------------------------------------- /src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom/client' 3 | import App from './App.jsx' 4 | import './index.css' 5 | 6 | ReactDOM.createRoot(document.getElementById('root')).render( 7 | 8 | 9 | , 10 | ) 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | FCC Calculator 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # React + Vite 2 | 3 | This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. 4 | 5 | Currently, two official plugins are available: 6 | 7 | - [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh 8 | - [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh 9 | -------------------------------------------------------------------------------- /.gitpod.yml: -------------------------------------------------------------------------------- 1 | # This configuration file was automatically generated by Gitpod. 2 | # Please adjust to your needs (see https://www.gitpod.io/docs/introduction/learn-gitpod/gitpod-yaml) 3 | # and commit this file to your remote git repository to share the goodness with others. 4 | 5 | # Learn more from ready-to-use templates: https://www.gitpod.io/docs/introduction/getting-started/quickstart 6 | 7 | tasks: 8 | - init: npm install && npm run build 9 | command: npm run dev 10 | 11 | 12 | -------------------------------------------------------------------------------- /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { browser: true, es2020: true }, 4 | extends: [ 5 | 'eslint:recommended', 6 | 'plugin:react/recommended', 7 | 'plugin:react/jsx-runtime', 8 | 'plugin:react-hooks/recommended', 9 | ], 10 | ignorePatterns: ['dist', '.eslintrc.cjs'], 11 | parserOptions: { ecmaVersion: 'latest', sourceType: 'module' }, 12 | settings: { react: { version: '18.2' } }, 13 | plugins: ['react-refresh'], 14 | rules: { 15 | 'react/jsx-no-target-blank': 'off', 16 | 'react-refresh/only-export-components': [ 17 | 'warn', 18 | { allowConstantExport: true }, 19 | ], 20 | }, 21 | } 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "javasciptcalculator", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0", 10 | "preview": "vite preview" 11 | }, 12 | "dependencies": { 13 | "bootstrap": "^5.3.3", 14 | "react": "^18.3.1", 15 | "react-dom": "^18.3.1" 16 | }, 17 | "devDependencies": { 18 | "@types/react": "^18.3.3", 19 | "@types/react-dom": "^18.3.0", 20 | "@vitejs/plugin-react": "^4.3.1", 21 | "eslint": "^8.57.0", 22 | "eslint-plugin-react": "^7.34.2", 23 | "eslint-plugin-react-hooks": "^4.6.2", 24 | "eslint-plugin-react-refresh": "^0.4.7", 25 | "vite": "^5.3.1" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Shema Elisa 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/App.jsx: -------------------------------------------------------------------------------- 1 | import './App.css'; 2 | import 'bootstrap/dist/css/bootstrap.min.css'; 3 | import { useEffect, useState } from 'react'; 4 | 5 | export default function App() { 6 | const [expression, setExpression] = useState(""); 7 | const [answer, setAnswer] = useState("0"); 8 | 9 | const trimed = expression.trim(); 10 | 11 | useEffect(() => { 12 | function handlePress(e) { 13 | if (e.key === "Enter") { 14 | calculate(); 15 | } else { 16 | buttonPress(e.key); 17 | } 18 | } 19 | window.addEventListener("keyup", handlePress); 20 | return () => { 21 | window.removeEventListener("keyup", handlePress); 22 | }; 23 | }, [expression, answer]); // Dependency array includes state 24 | 25 | function buttonPress(key) { 26 | if (key === "C") { 27 | setAnswer("0"); 28 | setExpression(""); 29 | } else if (key === "=") { 30 | calculate(); 31 | } else if (isOperator(key)) { 32 | if (trimed === "" || isOperator(trimed.charAt(trimed.length - 1))) return; 33 | setExpression(trimed + " " + key + " "); 34 | } else if (key === ".") { 35 | const lastNumber = trimed.split(/[+\-*/%]/g).pop(); 36 | if (lastNumber.includes(".")) return; 37 | setExpression(expression + key); 38 | } else { 39 | setExpression(expression + key); 40 | } 41 | } 42 | 43 | function calculate() { 44 | try { 45 | const result = eval(trimed.replace(/%/g, "/100")); // Replace % with division 46 | setAnswer(result.toString()); 47 | setExpression(result.toString()); 48 | } catch (error) { 49 | setAnswer("Error"); 50 | } 51 | } 52 | 53 | function isOperator(symbol) { 54 | return /[+\-*/%]/.test(symbol); 55 | } 56 | 57 | return ( 58 |
59 |
60 |
61 |
62 |
63 |
64 | {expression} 65 | {answer} 66 |
67 |
68 |
69 | {[ 70 | ['C', '%', '/', '*'], 71 | ['7', '8', '9', '-'], 72 | ['4', '5', '6', '+'], 73 | ['1', '2', '3', '='], 74 | ['0', '.'] 75 | ].map((row, rowIndex) => ( 76 |
77 | {row.map((buttonText, buttonIndex) => ( 78 |
79 | 86 |
87 | ))} 88 |
89 | ))} 90 |
91 |
92 |
93 |
94 |
95 | ); 96 | } 97 | 98 | function getButtonClass(buttonText) { 99 | switch (buttonText) { 100 | case 'C': 101 | return 'danger'; 102 | case '%': 103 | case '/': 104 | case '*': 105 | case '+': 106 | case '-': 107 | return 'primary'; 108 | case '=': 109 | return 'success'; 110 | default: 111 | return 'secondary'; 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/assets/react.svg: -------------------------------------------------------------------------------- 1 | --------------------------------------------------------------------------------