├── public
├── robots.txt
├── favicon.ico
├── logo192.png
├── logo512.png
├── manifest.json
└── index.html
├── src
├── calc-app.png
├── components
│ ├── Wrapper.js
│ ├── ButtonBox.js
│ ├── Screen.js
│ └── Button.js
├── index.js
├── context
│ └── CalcContext.js
├── App.js
└── index.css
├── .gitignore
├── README.md
└── package.json
/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/src/calc-app.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/candraKriswinarto/react-calculator-app/HEAD/src/calc-app.png
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/candraKriswinarto/react-calculator-app/HEAD/public/favicon.ico
--------------------------------------------------------------------------------
/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/candraKriswinarto/react-calculator-app/HEAD/public/logo192.png
--------------------------------------------------------------------------------
/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/candraKriswinarto/react-calculator-app/HEAD/public/logo512.png
--------------------------------------------------------------------------------
/src/components/Wrapper.js:
--------------------------------------------------------------------------------
1 |
2 | const Wrapper = ({ children }) => {
3 | return (
4 |
{children}
5 | )
6 | }
7 |
8 | export default Wrapper
--------------------------------------------------------------------------------
/src/components/ButtonBox.js:
--------------------------------------------------------------------------------
1 |
2 | const ButtonBox = ({ children }) => {
3 | return (
4 | {children}
5 | )
6 | }
7 |
8 | export default ButtonBox
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom/client';
3 | import './index.css';
4 | import App from './App';
5 |
6 | const root = ReactDOM.createRoot(document.getElementById('root'));
7 | root.render(
8 |
9 |
10 |
11 | );
12 |
--------------------------------------------------------------------------------
/src/components/Screen.js:
--------------------------------------------------------------------------------
1 | import { useContext } from "react"
2 | import { CalcContext } from "../context/CalcContext"
3 | import { Textfit } from 'react-textfit';
4 |
5 | const Screen = () => {
6 | const { calc } = useContext(CalcContext);
7 |
8 | return (
9 | {calc.num ? calc.num : calc.res}
10 | )
11 | }
12 |
13 | export default Screen
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/src/context/CalcContext.js:
--------------------------------------------------------------------------------
1 | import { createContext, useState } from "react"
2 |
3 | export const CalcContext = createContext()
4 | const CalcProvider = ({ children }) => {
5 | const [calc, setCalc] = useState({
6 | sign: "",
7 | num: 0,
8 | res: 0
9 | });
10 |
11 | const providerValue = {
12 | calc, setCalc
13 | }
14 |
15 | return (
16 |
17 | {children}
18 |
19 | )
20 | }
21 |
22 | export default CalcProvider
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
14 | Basic Calculator
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
2 |
3 | 
4 |
5 | ## Available Scripts
6 |
7 | In the project directory, you can run:
8 |
9 | ### `yarn install`
10 |
11 | Instal All dependencies in this project
12 |
13 | ### `yarn start`
14 |
15 | Runs the app in the development mode.
16 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
17 |
18 | ### Link
19 |
20 | Reactjs: https://reactjs.org/docs/create-a-new-react-app.html
21 | react-textfit: https://www.npmjs.com/package/react-textfit
22 | box-shadow: https://box-shadow.dev/
23 |
24 | ### Video Tutorial
25 |
26 | You can see my youtube video for this project in [here](https://youtu.be/o89bhL-S6g8)
--------------------------------------------------------------------------------
/src/App.js:
--------------------------------------------------------------------------------
1 | import Wrapper from "./components/Wrapper";
2 | import Screen from "./components/Screen";
3 | import ButtonBox from './components/ButtonBox'
4 | import Button from './components/Button'
5 | import CalcProvider from "./context/CalcContext";
6 |
7 | const btnValues = [
8 | ["C", "+-", "%", "/"],
9 | [7, 8, 9, "x"],
10 | [4, 5, 6, "-"],
11 | [1, 2, 3, "+"],
12 | [0, ".", "="],
13 | ];
14 |
15 | function App() {
16 | return (
17 |
18 |
19 |
20 |
21 | {btnValues.flat().map((btn, i) => (
22 |
26 | ))}
27 |
28 |
29 |
30 | );
31 | }
32 |
33 | export default App;
34 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "calc-app",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^5.14.1",
7 | "@testing-library/react": "^13.0.0",
8 | "@testing-library/user-event": "^13.2.1",
9 | "react": "^18.1.0",
10 | "react-dom": "^18.1.0",
11 | "react-scripts": "5.0.1",
12 | "react-textfit": "^1.1.1",
13 | "web-vitals": "^2.1.0"
14 | },
15 | "scripts": {
16 | "start": "react-scripts start",
17 | "build": "react-scripts build",
18 | "test": "react-scripts test",
19 | "eject": "react-scripts eject"
20 | },
21 | "eslintConfig": {
22 | "extends": [
23 | "react-app",
24 | "react-app/jest"
25 | ]
26 | },
27 | "browserslist": {
28 | "production": [
29 | ">0.2%",
30 | "not dead",
31 | "not op_mini all"
32 | ],
33 | "development": [
34 | "last 1 chrome version",
35 | "last 1 firefox version",
36 | "last 1 safari version"
37 | ]
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | @import url('https://fonts.googleapis.com/css2?family=Roboto&display=swap');
2 |
3 | * {
4 | margin: 0;
5 | padding: 0;
6 | box-sizing: border-box;
7 | }
8 |
9 | body {
10 | height: 100vh;
11 | background: #f4fefe;
12 | display: flex;
13 | align-items: center;
14 | justify-content: center;
15 | font-family: 'Roboto', sans-serif;
16 | }
17 |
18 | .screen {
19 | height: 4rem;
20 | margin-bottom: 4rem;
21 | }
22 |
23 | .wrapper {
24 | background: #f6f8f9;
25 | width: 20rem;
26 | padding: 2rem;
27 | margin-top: 4rem;
28 | border-radius: 1rem;
29 | box-shadow: 0px 9px 15px -3px rgba(0,0,0,0.1);
30 | }
31 |
32 | .buttonBox {
33 | display: grid;
34 | grid-template-columns: repeat(4, 1fr);
35 | gap: .5rem;
36 | }
37 |
38 | .button {
39 | background: #e9f0f4;
40 | height: 3.5rem;
41 | padding: 10px;
42 | border-radius: 10px;
43 | display: flex;
44 | align-items: center;
45 | justify-content: center;
46 | cursor: pointer;
47 | color: #242424;
48 | border: none;
49 | font-size: 1.5rem;
50 | }
51 |
52 | .button:hover {
53 | border: 2px dotted #242424;
54 | }
55 |
56 | .equals {
57 | background: #4bd086;
58 | grid-column: 3/5;
59 | }
60 |
61 | .opt {
62 | background: #f79505;
63 | }
--------------------------------------------------------------------------------
/src/components/Button.js:
--------------------------------------------------------------------------------
1 | import { useContext } from "react";
2 | import { CalcContext } from '../context/CalcContext'
3 |
4 | const getStyleName = btn => {
5 | const className = {
6 | '=': 'equals',
7 | 'x': 'opt',
8 | '-': 'opt',
9 | '+': 'opt',
10 | '/': 'opt',
11 | }
12 | return className[btn]
13 | }
14 |
15 | const Button = ({ value }) => {
16 | const { calc, setCalc } = useContext(CalcContext);
17 |
18 | // User click comma
19 | const commaClick = () => {
20 | setCalc({
21 | ...calc,
22 | num: !calc.num.toString().includes('.') ? calc.num + value : calc.num
23 | });
24 | }
25 | // User click C
26 | const resetClick = () => {
27 | setCalc({ sign: '', num: 0, res: 0 })
28 | }
29 | // User click number
30 | const handleClickButton = () => {
31 | const numberString = value.toString()
32 |
33 | let numberValue;
34 | if(numberString === '0' && calc.num === 0) {
35 | numberValue = "0"
36 | } else {
37 | numberValue = Number(calc.num + numberString)
38 | }
39 |
40 | setCalc({
41 | ...calc,
42 | num: numberValue
43 | })
44 | }
45 | // User click operation
46 | const signClick = () => {
47 | setCalc({
48 | sign: value,
49 | res: !calc.res && calc.num ? calc.num : calc.res,
50 | num: 0
51 | })
52 | }
53 | // User click equals
54 | const equalsClick = () => {
55 | if(calc.res && calc.num) {
56 | const math = (a, b, sign) => {
57 | const result = {
58 | '+': (a, b) => a + b,
59 | '-': (a, b) => a - b,
60 | 'x': (a, b) => a * b,
61 | '/': (a, b) => a / b,
62 | }
63 | return result[sign](a, b);
64 | }
65 | setCalc({
66 | res: math(calc.res, calc.num, calc.sign),
67 | sign: '',
68 | num: 0
69 | })
70 | }
71 | }
72 | // User click persen
73 | const persenClick = () => {
74 | setCalc({
75 | num: (calc.num / 100),
76 | res: (calc.res / 100),
77 | sign: ''
78 | })
79 | }
80 | // User click invert button
81 | const invertClick = () => {
82 | setCalc({
83 | num: calc.num ? calc.num * -1 : 0,
84 | res: calc.res ? calc.res * -1 : 0,
85 | sign: ''
86 | })
87 | }
88 |
89 | const handleBtnClick = () => {
90 |
91 | const results = {
92 | '.': commaClick,
93 | 'C': resetClick,
94 | '/': signClick,
95 | 'x': signClick,
96 | '-': signClick,
97 | '+': signClick,
98 | '=': equalsClick,
99 | '%': persenClick,
100 | '+-': invertClick
101 | }
102 | if(results[value]) {
103 | return results[value]()
104 | } else {
105 | return handleClickButton()
106 | }
107 | }
108 |
109 | return (
110 |
111 | )
112 | }
113 |
114 | export default Button
--------------------------------------------------------------------------------