├── .gitignore ├── README.md ├── package-lock.json ├── package.json ├── public ├── favicon.ico ├── index.html ├── logo192.png ├── logo512.png ├── manifest.json └── robots.txt └── src ├── Alert.js ├── App.js ├── List.js ├── index.css └── index.js /.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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sammibadriddinov/React-project-Task/e5b7edc2ea6a1ee5ba570750a02de171d9f2ca10/README.md -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "reminder", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^4.2.4", 7 | "@testing-library/react": "^9.5.0", 8 | "@testing-library/user-event": "^7.2.1", 9 | "react": "^16.13.1", 10 | "react-dom": "^16.13.1", 11 | "react-icons": "^3.11.0", 12 | "react-scripts": "3.4.3" 13 | }, 14 | "scripts": { 15 | "start": "react-scripts start", 16 | "build": "CI= react-scripts build", 17 | "test": "react-scripts test", 18 | "eject": "react-scripts eject" 19 | }, 20 | "eslintConfig": { 21 | "extends": "react-app" 22 | }, 23 | "browserslist": { 24 | "production": [ 25 | ">0.2%", 26 | "not dead", 27 | "not op_mini all" 28 | ], 29 | "development": [ 30 | "last 1 chrome version", 31 | "last 1 firefox version", 32 | "last 1 safari version" 33 | ] 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sammibadriddinov/React-project-Task/e5b7edc2ea6a1ee5ba570750a02de171d9f2ca10/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | Grocery Bud Complete 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sammibadriddinov/React-project-Task/e5b7edc2ea6a1ee5ba570750a02de171d9f2ca10/public/logo192.png -------------------------------------------------------------------------------- /public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sammibadriddinov/React-project-Task/e5b7edc2ea6a1ee5ba570750a02de171d9f2ca10/public/logo512.png -------------------------------------------------------------------------------- /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/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /src/Alert.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from "react"; 2 | 3 | const Alert = ({ type, msg, removeAlert, list }) => { 4 | useEffect(() => { 5 | const timeout = setTimeout(() => { 6 | removeAlert(); 7 | }, 3000); 8 | return () => clearInterval(timeout); 9 | }, [list]); 10 | return

{msg}

; 11 | }; 12 | 13 | export default Alert; 14 | -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from "react"; 2 | import List from "./List"; 3 | import Alert from "./Alert"; 4 | 5 | function App() { 6 | const [name, setName] = useState(""); 7 | const [list, setList] = useState([]); 8 | const [isEditing, setIsEditing] = useState(false); 9 | const [editID, setEditID] = useState(null); 10 | const [alert, setAlert] = useState({ 11 | show: false, 12 | msg: "", 13 | type: "", 14 | }); 15 | const handleSubmit = (e) => { 16 | e.preventDefault(); 17 | if (!name) { 18 | // Alert display 19 | showAlert(true, "danger", "Пожалуйста заполните форму"); 20 | } else if (name && isEditing) { 21 | // edit 22 | setList( 23 | list.map((item) => { 24 | if (item.id === editID) { 25 | return { ...item, title: name }; 26 | } 27 | return item; 28 | }) 29 | ); 30 | setName(""); 31 | setEditID(null); 32 | setIsEditing(false); 33 | showAlert(true, "success", "Успешно было изменено"); 34 | } else { 35 | showAlert(true, "success", "Заметка было добавлено успешно"); 36 | const newItem = { id: new Date().getTime().toString(), title: name }; 37 | setList([...list, newItem]); 38 | setName(""); 39 | } 40 | }; 41 | const showAlert = (show = false, type = "", msg = "") => { 42 | setAlert({ show, type, msg }); 43 | }; 44 | const clearList = () => { 45 | showAlert(true, "danger", "Заметка было полностью очищена"); 46 | setList([]); 47 | }; 48 | const removeItem = (id) => { 49 | showAlert(true, "danger", "Заметка было удалено"); 50 | setList(list.filter((item) => item.id !== id)); 51 | }; 52 | const editItem = (id) => { 53 | const specificItem = list.find((item) => item.id === id); 54 | setIsEditing(true); 55 | setEditID(id); 56 | setName(specificItem.title); 57 | }; 58 | return ( 59 |
60 |
61 | {alert.show && } 62 |

Список Заметки

63 |
64 | setName(e.target.value)} 70 | /> 71 | 74 |
75 | 76 | {list.length > 0 && ( 77 |
78 | 79 | 82 |
83 | )} 84 |
85 | ); 86 | } 87 | 88 | export default App; 89 | -------------------------------------------------------------------------------- /src/List.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { FaEdit, FaTrash } from "react-icons/fa"; 3 | const List = ({ items, removeItem, editItem }) => { 4 | return ( 5 |
6 | {items.map((item) => { 7 | const { id, title } = item; 8 | return ( 9 |
10 |

{title}

11 |
12 | 19 | 26 |
27 |
28 | ); 29 | })} 30 |
31 | ); 32 | }; 33 | 34 | export default List; 35 | -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | /* 2 | =============== 3 | Variables 4 | =============== 5 | */ 6 | 7 | :root { 8 | /* dark shades of primary color*/ 9 | --clr-primary-1: hsl(205, 86%, 17%); 10 | --clr-primary-2: hsl(205, 77%, 27%); 11 | --clr-primary-3: hsl(205, 72%, 37%); 12 | --clr-primary-4: hsl(205, 63%, 48%); 13 | /* primary/main color */ 14 | --clr-primary-5: hsl(205, 78%, 60%); 15 | /* lighter shades of primary color */ 16 | --clr-primary-6: hsl(205, 89%, 70%); 17 | --clr-primary-7: hsl(205, 90%, 76%); 18 | --clr-primary-8: hsl(205, 86%, 81%); 19 | --clr-primary-9: hsl(205, 90%, 88%); 20 | --clr-primary-10: hsl(205, 100%, 96%); 21 | /* darkest grey - used for headings */ 22 | --clr-grey-1: hsl(209, 61%, 16%); 23 | --clr-grey-2: hsl(211, 39%, 23%); 24 | --clr-grey-3: hsl(209, 34%, 30%); 25 | --clr-grey-4: hsl(209, 28%, 39%); 26 | /* grey used for paragraphs */ 27 | --clr-grey-5: hsl(210, 22%, 49%); 28 | --clr-grey-6: hsl(209, 23%, 60%); 29 | --clr-grey-7: hsl(211, 27%, 70%); 30 | --clr-grey-8: hsl(210, 31%, 80%); 31 | --clr-grey-9: hsl(212, 33%, 89%); 32 | --clr-grey-10: hsl(210, 36%, 96%); 33 | --clr-white: #fff; 34 | --clr-red-dark: hsl(360, 67%, 44%); 35 | --clr-red-light: hsl(360, 71%, 66%); 36 | --clr-green-dark: hsl(125, 67%, 44%); 37 | --clr-green-light: hsl(125, 71%, 66%); 38 | --clr-black: #222; 39 | --transition: all 0.3s linear; 40 | --spacing: 0.1rem; 41 | --radius: 0.25rem; 42 | --light-shadow: 0 5px 15px rgba(0, 0, 0, 0.1); 43 | --dark-shadow: 0 5px 15px rgba(0, 0, 0, 0.2); 44 | --max-width: 1170px; 45 | --fixed-width: 620px; 46 | } 47 | /* 48 | =============== 49 | Global Styles 50 | =============== 51 | */ 52 | 53 | *, 54 | ::after, 55 | ::before { 56 | margin: 0; 57 | padding: 0; 58 | box-sizing: border-box; 59 | } 60 | body { 61 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, 62 | Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; 63 | background: var(--clr-grey-10); 64 | color: var(--clr-grey-1); 65 | line-height: 1.5; 66 | font-size: 0.875rem; 67 | } 68 | ul { 69 | list-style-type: none; 70 | } 71 | a { 72 | text-decoration: none; 73 | } 74 | h1, 75 | h2, 76 | h3, 77 | h4 { 78 | letter-spacing: var(--spacing); 79 | text-transform: capitalize; 80 | line-height: 1.25; 81 | margin-bottom: 0.75rem; 82 | } 83 | h1 { 84 | font-size: 3rem; 85 | } 86 | h2 { 87 | font-size: 2rem; 88 | } 89 | h3 { 90 | font-size: 1.25rem; 91 | } 92 | h4 { 93 | font-size: 0.875rem; 94 | } 95 | p { 96 | margin-bottom: 1.25rem; 97 | color: var(--clr-grey-5); 98 | } 99 | @media screen and (min-width: 800px) { 100 | h1 { 101 | font-size: 4rem; 102 | } 103 | h2 { 104 | font-size: 2.5rem; 105 | } 106 | h3 { 107 | font-size: 1.75rem; 108 | } 109 | h4 { 110 | font-size: 1rem; 111 | } 112 | body { 113 | font-size: 1rem; 114 | } 115 | h1, 116 | h2, 117 | h3, 118 | h4 { 119 | line-height: 1; 120 | } 121 | } 122 | .btn { 123 | text-transform: uppercase; 124 | background: transparent; 125 | color: var(--clr-black); 126 | padding: 0.375rem 0.75rem; 127 | letter-spacing: var(--spacing); 128 | display: inline-block; 129 | transition: var(--transition); 130 | font-size: 0.875rem; 131 | border: 2px solid var(--clr-black); 132 | cursor: pointer; 133 | box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2); 134 | border-radius: var(--radius); 135 | } 136 | .btn:hover { 137 | color: var(--clr-white); 138 | background: var(--clr-black); 139 | } 140 | /* section */ 141 | .section { 142 | padding: 5rem 0; 143 | } 144 | 145 | .section-center { 146 | width: 90vw; 147 | margin: 0 auto; 148 | max-width: 35rem; 149 | margin-top: 8rem; 150 | } 151 | @media screen and (min-width: 992px) { 152 | .section-center { 153 | width: 95vw; 154 | } 155 | } 156 | main { 157 | min-height: 100vh; 158 | display: grid; 159 | place-items: center; 160 | } 161 | /* 162 | =============== 163 | Grocery List 164 | =============== 165 | */ 166 | .section-center { 167 | background: var(--clr-white); 168 | border-radius: var(--radius); 169 | box-shadow: var(--light-shadow); 170 | transition: var(--transition); 171 | padding: 2rem; 172 | } 173 | .section-center:hover { 174 | box-shadow: var(--dark-shadow); 175 | } 176 | .alert { 177 | margin-bottom: 1rem; 178 | height: 1.25rem; 179 | display: grid; 180 | align-items: center; 181 | text-align: center; 182 | font-size: 0.7rem; 183 | border-radius: 0.25rem; 184 | letter-spacing: var(--spacing); 185 | text-transform: capitalize; 186 | } 187 | .alert-danger { 188 | color: #721c24; 189 | background: #f8d7da; 190 | } 191 | .alert-success { 192 | color: #155724; 193 | background: #d4edda; 194 | } 195 | .grocery-form h3 { 196 | color: var(--clr-primary-1); 197 | margin-bottom: 1.5rem; 198 | text-align: center; 199 | } 200 | .form-control { 201 | display: flex; 202 | justify-content: center; 203 | } 204 | .grocery { 205 | padding: 0.25rem; 206 | padding-left: 1rem; 207 | background: var(--clr-grey-10); 208 | border-top-left-radius: var(--radius); 209 | border-bottom-left-radius: var(--radius); 210 | border-color: transparent; 211 | font-size: 1rem; 212 | flex: 1 0 auto; 213 | color: var(--clr-grey-5); 214 | } 215 | .grocery::placeholder { 216 | font-family: var(--ff-secondary); 217 | color: var(--clr-grey-5); 218 | } 219 | .submit-btn { 220 | background: var(--clr-primary-8); 221 | border-color: transparent; 222 | flex: 0 0 5rem; 223 | display: grid; 224 | align-items: center; 225 | padding: 0.25rem; 226 | text-transform: capitalize; 227 | letter-spacing: 2px; 228 | border-top-right-radius: var(--radius); 229 | border-bottom-right-radius: var(--radius); 230 | cursor: pointer; 231 | content: var(--clr-primary-5); 232 | transition: var(--transition); 233 | font-size: 0.85rem; 234 | } 235 | .submit-btn:hover { 236 | background: var(--clr-primary-5); 237 | color: var(--clr-white); 238 | } 239 | 240 | .grocery-container { 241 | margin-top: 2rem; 242 | } 243 | 244 | .grocery-item { 245 | display: flex; 246 | align-items: center; 247 | justify-content: space-between; 248 | margin-bottom: 0.5rem; 249 | transition: var(--transition); 250 | padding: 0.25rem 1rem; 251 | border-radius: var(--radius); 252 | text-transform: capitalize; 253 | } 254 | .grocery-item:hover { 255 | color: var(--clr-grey-5); 256 | background: var(--clr-grey-10); 257 | } 258 | .grocery-item:hover .title { 259 | color: var(--clr-grey-5); 260 | } 261 | .title { 262 | margin-bottom: 0; 263 | color: var(--clr-grey-1); 264 | letter-spacing: 2px; 265 | transition: var(--transition); 266 | } 267 | .edit-btn, 268 | .delete-btn { 269 | background: transparent; 270 | border-color: transparent; 271 | cursor: pointer; 272 | font-size: 0.7rem; 273 | margin: 0 0.15rem; 274 | transition: var(--transition); 275 | } 276 | .edit-btn { 277 | color: var(--clr-green-light); 278 | } 279 | .edit-btn:hover { 280 | color: var(--clr-green-dark); 281 | } 282 | .delete-btn { 283 | color: var(--clr-red-light); 284 | } 285 | .delete-btn:hover { 286 | color: var(--clr-red-dark); 287 | } 288 | .clear-btn { 289 | text-transform: capitalize; 290 | width: 10rem; 291 | height: 1.5rem; 292 | display: grid; 293 | align-items: center; 294 | background: transparent; 295 | border-color: transparent; 296 | color: var(--clr-red-light); 297 | margin: 0 auto; 298 | font-size: 0.85rem; 299 | letter-spacing: var(--spacing); 300 | cursor: pointer; 301 | transition: var(--transition); 302 | margin-top: 1.25rem; 303 | } 304 | .clear-btn:hover { 305 | color: var(--clr-red-dark); 306 | } 307 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.css'; 4 | import App from './App'; 5 | 6 | ReactDOM.render( 7 | 8 | 9 | , 10 | document.getElementById('root') 11 | ); 12 | --------------------------------------------------------------------------------