reducers
This commit is contained in:
parent
7d6072383d
commit
9149538440
1
usereducer-starting-project/.eslintcache
Normal file
1
usereducer-starting-project/.eslintcache
Normal file
@ -0,0 +1 @@
|
|||||||
|
[{"/Users/tyrel.souza/code/udemy/react-course/usereducer-starting-project/src/index.js":"1","/Users/tyrel.souza/code/udemy/react-course/usereducer-starting-project/src/App.js":"2","/Users/tyrel.souza/code/udemy/react-course/usereducer-starting-project/src/components/Home/Home.js":"3","/Users/tyrel.souza/code/udemy/react-course/usereducer-starting-project/src/components/Login/Login.js":"4","/Users/tyrel.souza/code/udemy/react-course/usereducer-starting-project/src/components/MainHeader/MainHeader.js":"5","/Users/tyrel.souza/code/udemy/react-course/usereducer-starting-project/src/components/MainHeader/Navigation.js":"6","/Users/tyrel.souza/code/udemy/react-course/usereducer-starting-project/src/components/UI/Card/Card.js":"7","/Users/tyrel.souza/code/udemy/react-course/usereducer-starting-project/src/components/UI/Button/Button.js":"8"},{"size":206,"mtime":1648532752000,"results":"9","hashOfConfig":"10"},{"size":1093,"mtime":1616593928000,"results":"11","hashOfConfig":"10"},{"size":250,"mtime":1616593928000,"results":"12","hashOfConfig":"10"},{"size":3876,"mtime":1663871762917,"results":"13","hashOfConfig":"10"},{"size":368,"mtime":1616593928000,"results":"14","hashOfConfig":"10"},{"size":571,"mtime":1616593928000,"results":"15","hashOfConfig":"10"},{"size":218,"mtime":1616593928000,"results":"16","hashOfConfig":"10"},{"size":353,"mtime":1616593928000,"results":"17","hashOfConfig":"10"},{"filePath":"18","messages":"19","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},"4a8obe",{"filePath":"20","messages":"21","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"22","messages":"23","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"24","messages":"25","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"26","messages":"27","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"28","messages":"29","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"30","messages":"31","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"32","messages":"33","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"34"},"/Users/tyrel.souza/code/udemy/react-course/usereducer-starting-project/src/index.js",[],"/Users/tyrel.souza/code/udemy/react-course/usereducer-starting-project/src/App.js",[],"/Users/tyrel.souza/code/udemy/react-course/usereducer-starting-project/src/components/Home/Home.js",[],"/Users/tyrel.souza/code/udemy/react-course/usereducer-starting-project/src/components/Login/Login.js",[],"/Users/tyrel.souza/code/udemy/react-course/usereducer-starting-project/src/components/MainHeader/MainHeader.js",[],"/Users/tyrel.souza/code/udemy/react-course/usereducer-starting-project/src/components/MainHeader/Navigation.js",[],"/Users/tyrel.souza/code/udemy/react-course/usereducer-starting-project/src/components/UI/Card/Card.js",[],"/Users/tyrel.souza/code/udemy/react-course/usereducer-starting-project/src/components/UI/Button/Button.js",[],["35","36"],{"ruleId":"37","replacedBy":"38"},{"ruleId":"39","replacedBy":"40"},"no-native-reassign",["41"],"no-negated-in-lhs",["42"],"no-global-assign","no-unsafe-negation"]
|
6
usereducer-starting-project/.prettierrc.json
Normal file
6
usereducer-starting-project/.prettierrc.json
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"trailingComma": "es5",
|
||||||
|
"tabWidth": 4,
|
||||||
|
"semi": false,
|
||||||
|
"singleQuote": false
|
||||||
|
}
|
38145
usereducer-starting-project/package-lock.json
generated
Normal file
38145
usereducer-starting-project/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
38
usereducer-starting-project/package.json
Normal file
38
usereducer-starting-project/package.json
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"name": "react-complete-guide",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"private": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@testing-library/jest-dom": "^5.11.6",
|
||||||
|
"@testing-library/react": "^11.2.2",
|
||||||
|
"@testing-library/user-event": "^12.5.0",
|
||||||
|
"react": "^18.0.0",
|
||||||
|
"react-dom": "^18.0.0",
|
||||||
|
"react-scripts": "4.0.1",
|
||||||
|
"web-vitals": "^0.2.4"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"start": "react-scripts start",
|
||||||
|
"build": "react-scripts build",
|
||||||
|
"test": "react-scripts test",
|
||||||
|
"eject": "react-scripts eject"
|
||||||
|
},
|
||||||
|
"eslintConfig": {
|
||||||
|
"extends": [
|
||||||
|
"react-app",
|
||||||
|
"react-app/jest"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"browserslist": {
|
||||||
|
"production": [
|
||||||
|
">0.2%",
|
||||||
|
"not dead",
|
||||||
|
"not op_mini all"
|
||||||
|
],
|
||||||
|
"development": [
|
||||||
|
"last 1 chrome version",
|
||||||
|
"last 1 firefox version",
|
||||||
|
"last 1 safari version"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
BIN
usereducer-starting-project/public/favicon.ico
Normal file
BIN
usereducer-starting-project/public/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.8 KiB |
43
usereducer-starting-project/public/index.html
Normal file
43
usereducer-starting-project/public/index.html
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<meta name="theme-color" content="#000000" />
|
||||||
|
<meta
|
||||||
|
name="description"
|
||||||
|
content="Web site created using create-react-app"
|
||||||
|
/>
|
||||||
|
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
|
||||||
|
<!--
|
||||||
|
manifest.json provides metadata used when your web app is installed on a
|
||||||
|
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
||||||
|
-->
|
||||||
|
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
||||||
|
<!--
|
||||||
|
Notice the use of %PUBLIC_URL% in the tags above.
|
||||||
|
It will be replaced with the URL of the `public` folder during the build.
|
||||||
|
Only files inside the `public` folder can be referenced from the HTML.
|
||||||
|
|
||||||
|
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
||||||
|
work correctly both with client-side routing and a non-root public URL.
|
||||||
|
Learn how to configure a non-root public URL by running `npm run build`.
|
||||||
|
-->
|
||||||
|
<title>React App</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||||
|
<div id="root"></div>
|
||||||
|
<!--
|
||||||
|
This HTML file is a template.
|
||||||
|
If you open it directly in the browser, you will see an empty page.
|
||||||
|
|
||||||
|
You can add webfonts, meta tags, or analytics to this file.
|
||||||
|
The build step will place the bundled scripts into the <body> tag.
|
||||||
|
|
||||||
|
To begin the development, run `npm start` or `yarn start`.
|
||||||
|
To create a production bundle, use `npm run build` or `yarn build`.
|
||||||
|
-->
|
||||||
|
</body>
|
||||||
|
</html>
|
BIN
usereducer-starting-project/public/logo192.png
Normal file
BIN
usereducer-starting-project/public/logo192.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.2 KiB |
BIN
usereducer-starting-project/public/logo512.png
Normal file
BIN
usereducer-starting-project/public/logo512.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 9.4 KiB |
25
usereducer-starting-project/public/manifest.json
Normal file
25
usereducer-starting-project/public/manifest.json
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"short_name": "React App",
|
||||||
|
"name": "Create React App Sample",
|
||||||
|
"icons": [
|
||||||
|
{
|
||||||
|
"src": "favicon.ico",
|
||||||
|
"sizes": "64x64 32x32 24x24 16x16",
|
||||||
|
"type": "image/x-icon"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "logo192.png",
|
||||||
|
"type": "image/png",
|
||||||
|
"sizes": "192x192"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "logo512.png",
|
||||||
|
"type": "image/png",
|
||||||
|
"sizes": "512x512"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"start_url": ".",
|
||||||
|
"display": "standalone",
|
||||||
|
"theme_color": "#000000",
|
||||||
|
"background_color": "#ffffff"
|
||||||
|
}
|
3
usereducer-starting-project/public/robots.txt
Normal file
3
usereducer-starting-project/public/robots.txt
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# https://www.robotstxt.org/robotstxt.html
|
||||||
|
User-agent: *
|
||||||
|
Disallow:
|
41
usereducer-starting-project/src/App.js
Normal file
41
usereducer-starting-project/src/App.js
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import React, { useState, useEffect } from 'react';
|
||||||
|
|
||||||
|
import Login from './components/Login/Login';
|
||||||
|
import Home from './components/Home/Home';
|
||||||
|
import MainHeader from './components/MainHeader/MainHeader';
|
||||||
|
|
||||||
|
function App() {
|
||||||
|
const [isLoggedIn, setIsLoggedIn] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const storedUserLoggedInInformation = localStorage.getItem('isLoggedIn');
|
||||||
|
|
||||||
|
if (storedUserLoggedInInformation === '1') {
|
||||||
|
setIsLoggedIn(true);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const loginHandler = (email, password) => {
|
||||||
|
// We should of course check email and password
|
||||||
|
// But it's just a dummy/ demo anyways
|
||||||
|
localStorage.setItem('isLoggedIn', '1');
|
||||||
|
setIsLoggedIn(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const logoutHandler = () => {
|
||||||
|
localStorage.removeItem('isLoggedIn');
|
||||||
|
setIsLoggedIn(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
<MainHeader isAuthenticated={isLoggedIn} onLogout={logoutHandler} />
|
||||||
|
<main>
|
||||||
|
{!isLoggedIn && <Login onLogin={loginHandler} />}
|
||||||
|
{isLoggedIn && <Home onLogout={logoutHandler} />}
|
||||||
|
</main>
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default App;
|
14
usereducer-starting-project/src/components/Home/Home.js
Normal file
14
usereducer-starting-project/src/components/Home/Home.js
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import Card from '../UI/Card/Card';
|
||||||
|
import classes from './Home.module.css';
|
||||||
|
|
||||||
|
const Home = (props) => {
|
||||||
|
return (
|
||||||
|
<Card className={classes.home}>
|
||||||
|
<h1>Welcome back!</h1>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Home;
|
@ -0,0 +1,7 @@
|
|||||||
|
.home {
|
||||||
|
width: 90%;
|
||||||
|
max-width: 40rem;
|
||||||
|
padding: 3rem;
|
||||||
|
margin: 2rem auto;
|
||||||
|
text-align: center;
|
||||||
|
}
|
124
usereducer-starting-project/src/components/Login/Login.js
Normal file
124
usereducer-starting-project/src/components/Login/Login.js
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
import React, { useState, useEffect, useReducer } from "react"
|
||||||
|
|
||||||
|
import Card from "../UI/Card/Card"
|
||||||
|
import classes from "./Login.module.css"
|
||||||
|
import Button from "../UI/Button/Button"
|
||||||
|
|
||||||
|
const emailReducer = (state, action) => {
|
||||||
|
if (action.type === "USER_INPUT") {
|
||||||
|
return { value: action.val, isValid: action.val.includes("@") }
|
||||||
|
}
|
||||||
|
if (action.type === "INPUT_BLUR") {
|
||||||
|
return { value: state.value, isValid: state.value.includes("@") }
|
||||||
|
}
|
||||||
|
return { value: "", isValid: false }
|
||||||
|
}
|
||||||
|
|
||||||
|
const passwordReducer = (state, action) => {
|
||||||
|
if (action.type === "USER_INPUT") {
|
||||||
|
return { value: action.val, isValid: action.val.trim().length > 6 }
|
||||||
|
}
|
||||||
|
if (action.type === "INPUT_BLUR") {
|
||||||
|
return { value: state.value, isValid: state.value.trim().length > 6 }
|
||||||
|
}
|
||||||
|
return { value: "", isValid: false }
|
||||||
|
}
|
||||||
|
|
||||||
|
const Login = (props) => {
|
||||||
|
const [formIsValid, setFormIsValid] = useState(false)
|
||||||
|
|
||||||
|
const [emailState, dispatchEmail] = useReducer(emailReducer, {
|
||||||
|
value: "",
|
||||||
|
isValid: false,
|
||||||
|
})
|
||||||
|
|
||||||
|
const [passwordState, dispatchPassword] = useReducer(passwordReducer, {
|
||||||
|
value: "",
|
||||||
|
isValid: false,
|
||||||
|
})
|
||||||
|
|
||||||
|
const {isValid: emailIsValid} = emailState;
|
||||||
|
const {isValid: passwordIsValid} = emailState;
|
||||||
|
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const identifier = setTimeout(() => {
|
||||||
|
setFormIsValid(emailIsValid && passwordIsValid)
|
||||||
|
}, 500)
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
clearTimeout(identifier)
|
||||||
|
}
|
||||||
|
}, [emailIsValid, passwordIsValid])
|
||||||
|
|
||||||
|
const emailChangeHandler = (event) => {
|
||||||
|
dispatchEmail({ type: "USER_INPUT", val: event.target.value })
|
||||||
|
|
||||||
|
// setFormIsValid(event.target.value.includes("@") && passwordState.isValid)
|
||||||
|
}
|
||||||
|
|
||||||
|
const passwordChangeHandler = (event) => {
|
||||||
|
dispatchPassword({ type: "USER_INPUT", val: event.target.value })
|
||||||
|
|
||||||
|
// setFormIsValid(emailState.isValid && event.target.value.trim().length > 6)
|
||||||
|
}
|
||||||
|
|
||||||
|
const validateEmailHandler = () => {
|
||||||
|
dispatchEmail({ type: "INPUT_BLUR" })
|
||||||
|
}
|
||||||
|
|
||||||
|
const validatePasswordHandler = () => {
|
||||||
|
dispatchPassword({ type: "INPUT_BLUR" })
|
||||||
|
}
|
||||||
|
|
||||||
|
const submitHandler = (event) => {
|
||||||
|
event.preventDefault()
|
||||||
|
props.onLogin(emailState.value, passwordState.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card className={classes.login}>
|
||||||
|
<form onSubmit={submitHandler}>
|
||||||
|
<div
|
||||||
|
className={`${classes.control} ${
|
||||||
|
!emailState.isValid ? classes.invalid : ""
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<label htmlFor="email">E-Mail</label>
|
||||||
|
<input
|
||||||
|
type="email"
|
||||||
|
id="email"
|
||||||
|
value={emailState.value}
|
||||||
|
onChange={emailChangeHandler}
|
||||||
|
onBlur={validateEmailHandler}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className={`${classes.control} ${
|
||||||
|
!passwordState.isValid ? classes.invalid : ""
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<label htmlFor="password">Password</label>
|
||||||
|
<input
|
||||||
|
type="password"
|
||||||
|
id="password"
|
||||||
|
value={passwordState.value}
|
||||||
|
onChange={passwordChangeHandler}
|
||||||
|
onBlur={validatePasswordHandler}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className={classes.actions}>
|
||||||
|
<Button
|
||||||
|
type="submit"
|
||||||
|
className={classes.btn}
|
||||||
|
disabled={!formIsValid}
|
||||||
|
>
|
||||||
|
Login
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</Card>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Login
|
@ -0,0 +1,56 @@
|
|||||||
|
.login {
|
||||||
|
width: 90%;
|
||||||
|
max-width: 40rem;
|
||||||
|
margin: 2rem auto;
|
||||||
|
padding: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.control {
|
||||||
|
margin: 1rem 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: stretch;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.control label,
|
||||||
|
.control input {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.control label {
|
||||||
|
font-weight: bold;
|
||||||
|
flex: 1;
|
||||||
|
color: #464646;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.control input {
|
||||||
|
flex: 3;
|
||||||
|
font: inherit;
|
||||||
|
padding: 0.35rem 0.35rem;
|
||||||
|
border-radius: 6px;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.control input:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: #4f005f;
|
||||||
|
background: #f6dbfc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.control.invalid input {
|
||||||
|
border-color: red;
|
||||||
|
background: #fbdada;
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
.control {
|
||||||
|
align-items: center;
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import Navigation from './Navigation';
|
||||||
|
import classes from './MainHeader.module.css';
|
||||||
|
|
||||||
|
const MainHeader = (props) => {
|
||||||
|
return (
|
||||||
|
<header className={classes['main-header']}>
|
||||||
|
<h1>A Typical Page</h1>
|
||||||
|
<Navigation isLoggedIn={props.isAuthenticated} onLogout={props.onLogout} />
|
||||||
|
</header>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default MainHeader;
|
@ -0,0 +1,16 @@
|
|||||||
|
.main-header {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 5rem;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
background: #741188;
|
||||||
|
padding: 0 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-header h1 {
|
||||||
|
color: white;
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import classes from './Navigation.module.css';
|
||||||
|
|
||||||
|
const Navigation = (props) => {
|
||||||
|
return (
|
||||||
|
<nav className={classes.nav}>
|
||||||
|
<ul>
|
||||||
|
{props.isLoggedIn && (
|
||||||
|
<li>
|
||||||
|
<a href="/">Users</a>
|
||||||
|
</li>
|
||||||
|
)}
|
||||||
|
{props.isLoggedIn && (
|
||||||
|
<li>
|
||||||
|
<a href="/">Admin</a>
|
||||||
|
</li>
|
||||||
|
)}
|
||||||
|
{props.isLoggedIn && (
|
||||||
|
<li>
|
||||||
|
<button onClick={props.onLogout}>Logout</button>
|
||||||
|
</li>
|
||||||
|
)}
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Navigation;
|
@ -0,0 +1,43 @@
|
|||||||
|
.nav ul {
|
||||||
|
list-style: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav li {
|
||||||
|
margin: 0;
|
||||||
|
margin-left: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav a {
|
||||||
|
text-decoration: none;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav a:hover,
|
||||||
|
.nav a:active {
|
||||||
|
color: #f3cafb;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav button {
|
||||||
|
font: inherit;
|
||||||
|
background: #dd0db0;
|
||||||
|
border: 1px solid #dd0db0;
|
||||||
|
padding: 0.5rem 1.5rem;
|
||||||
|
color: white;
|
||||||
|
cursor: pointer;
|
||||||
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.26);
|
||||||
|
border-radius: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav button:focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav button:hover,
|
||||||
|
.nav button:active {
|
||||||
|
color: #f3cafb;
|
||||||
|
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.26);
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import classes from './Button.module.css';
|
||||||
|
|
||||||
|
const Button = (props) => {
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
type={props.type || 'button'}
|
||||||
|
className={`${classes.button} ${props.className}`}
|
||||||
|
onClick={props.onClick}
|
||||||
|
disabled={props.disabled}
|
||||||
|
>
|
||||||
|
{props.children}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Button;
|
@ -0,0 +1,30 @@
|
|||||||
|
.button {
|
||||||
|
font: inherit;
|
||||||
|
border: 1px solid #4f005f;
|
||||||
|
background: #4f005f;
|
||||||
|
color: white;
|
||||||
|
padding: 0.75rem 3.5rem;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 1.15rem;
|
||||||
|
border-radius: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button:hover,
|
||||||
|
.button:active {
|
||||||
|
background: #741188;
|
||||||
|
border-color: #741188;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button:focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:disabled,
|
||||||
|
button:focus:disabled,
|
||||||
|
button:hover:disabled,
|
||||||
|
button:active:disabled {
|
||||||
|
background: #ccc;
|
||||||
|
border-color: #ccc;
|
||||||
|
color: #666666;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
11
usereducer-starting-project/src/components/UI/Card/Card.js
Normal file
11
usereducer-starting-project/src/components/UI/Card/Card.js
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import classes from './Card.module.css';
|
||||||
|
|
||||||
|
const Card = (props) => {
|
||||||
|
return (
|
||||||
|
<div className={`${classes.card} ${props.className}`}>{props.children}</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Card;
|
@ -0,0 +1,5 @@
|
|||||||
|
.card {
|
||||||
|
background: white;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.26);
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
16
usereducer-starting-project/src/index.css
Normal file
16
usereducer-starting-project/src/index.css
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
|
font-family: sans-serif;
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
main {
|
||||||
|
margin-top: 6rem;
|
||||||
|
}
|
8
usereducer-starting-project/src/index.js
Normal file
8
usereducer-starting-project/src/index.js
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import ReactDOM from 'react-dom/client';
|
||||||
|
|
||||||
|
import './index.css';
|
||||||
|
import App from './App';
|
||||||
|
|
||||||
|
const root = ReactDOM.createRoot(document.getElementById('root'));
|
||||||
|
root.render(<App />);
|
Loading…
Reference in New Issue
Block a user