├── .gitignore ├── README.md ├── package-lock.json ├── package.json ├── public ├── favicon.ico ├── index.html ├── logo192.png ├── logo512.png ├── manifest.json └── robots.txt └── src ├── App.js ├── components ├── AddNote.js ├── Header.js ├── Note.js ├── NotesList.js └── Search.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: -------------------------------------------------------------------------------- 1 | # React Notes App 2 | By: Payden Dyer 3 | 4 | Web App that can add and delete notes 5 | 6 | https://paydendyer.github.io/react-notes-app 7 | ## Technology Used: 8 | 1. React.js 9 | 2. CSS Grid 10 | 3. Git 11 | 12 | ## Description of Application: 13 | This is a web app built with React that can save and delete notes to the computer's local storage. It features a search bar and a dark mode toggler. Tutorial from: https://www.youtube.com/watch?v=8KB3DHI-QbM&t=191 14 | 15 | ## Setup Instructions 16 | After cloning the respository, run npx start in the terminal 17 | 18 | ## Known Bugs: 19 | None 20 | 21 | ## License 22 | Payden Dyer 2/14/2022 -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-notes-app", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^5.16.2", 7 | "@testing-library/react": "^12.1.2", 8 | "@testing-library/user-event": "^13.5.0", 9 | "nanoid": "^3.2.0", 10 | "react": "^17.0.2", 11 | "react-dom": "^17.0.2", 12 | "react-icons": "^4.3.1", 13 | "react-scripts": "^3.0.1", 14 | "web-vitals": "^2.1.4" 15 | }, 16 | "scripts": { 17 | "start": "react-scripts start", 18 | "build": "react-scripts build", 19 | "test": "react-scripts test", 20 | "eject": "react-scripts eject" 21 | }, 22 | "eslintConfig": { 23 | "extends": [ 24 | "react-app", 25 | "react-app/jest" 26 | ] 27 | }, 28 | "browserslist": { 29 | "production": [ 30 | ">0.2%", 31 | "not dead", 32 | "not op_mini all" 33 | ], 34 | "development": [ 35 | "last 1 chrome version", 36 | "last 1 firefox version", 37 | "last 1 safari version" 38 | ] 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paydendyer/react-notes-app/a0e650467cc2964437675292d9dde97216c0e6d3/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | Payden Dyer | Notes App 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paydendyer/react-notes-app/a0e650467cc2964437675292d9dde97216c0e6d3/public/logo192.png -------------------------------------------------------------------------------- /public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paydendyer/react-notes-app/a0e650467cc2964437675292d9dde97216c0e6d3/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/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { nanoid } from 'nanoid'; 3 | import { useState, useEffect } from 'react'; 4 | import NotesList from './components/NotesList'; 5 | import Search from './components/Search'; 6 | import Header from './components/Header'; 7 | const App = () => { 8 | const [notes, setNotes] = useState([ 9 | { 10 | id: nanoid(), 11 | text: "This is my first note!", 12 | date: "02/14/2022" 13 | }, 14 | { 15 | id: nanoid(), 16 | text: "This is my second note!", 17 | date: "02/10/2022" 18 | }, 19 | { 20 | id: nanoid(), 21 | text: "This is my third note!", 22 | date: "02/12/2022" 23 | }, 24 | ]); 25 | 26 | const [searchText, setSearchText] = useState(''); 27 | 28 | const [darkMode, setDarkMode] = useState(false); 29 | 30 | useEffect(() => { 31 | const savedNotes = JSON.parse( 32 | localStorage.getItem('react-notes-app-data') 33 | ); 34 | 35 | if(savedNotes) { 36 | setNotes(savedNotes); 37 | } 38 | },[]); 39 | 40 | useEffect(() => { 41 | localStorage.setItem('react-notes-app-data', JSON.stringify(notes) 42 | ); 43 | }, [notes]); 44 | 45 | const addNote = (text) => { 46 | const date = new Date(); 47 | const newNote = { 48 | id: nanoid, 49 | text: text, 50 | date: date.toLocaleDateString() 51 | } 52 | const newNotes = [...notes, newNote]; 53 | setNotes(newNotes); 54 | }; 55 | 56 | const deleteNote = (id) => { 57 | const newNotes = notes.filter((note)=> note.id !== id); 58 | setNotes(newNotes); 59 | } 60 | 61 | return ( 62 |
63 |
64 |
65 | 66 | 68 | note.text.toLowerCase().includes(searchText) 69 | )} 70 | handleAddNote={addNote} 71 | handleDeleteNote={deleteNote} 72 | /> 73 |
74 |
75 | ); 76 | }; 77 | 78 | export default App; -------------------------------------------------------------------------------- /src/components/AddNote.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { useState } from 'react'; 3 | const AddNote = ({ handleAddNote }) => { 4 | const [noteText, setNoteText] = useState(''); 5 | const characterLimit = 200; 6 | 7 | const handleChange = (event) => { 8 | if(characterLimit - event.target.value.length >=0){ 9 | setNoteText(event.target.value); 10 | } 11 | }; 12 | 13 | const handleSaveClick = () => { 14 | if(noteText.trim().length > 0){ 15 | handleAddNote(noteText) 16 | setNoteText(''); 17 | } 18 | }; 19 | 20 | return(
21 | 28 |
29 | {characterLimit - noteText.length} Remaining 30 | 31 |
32 |
33 | ); 34 | }; 35 | export default AddNote; -------------------------------------------------------------------------------- /src/components/Header.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const Header = ({ handleToggleDarkMode }) => { 4 | return( 5 |
6 |

Notes

7 | 15 |
16 | ) 17 | } 18 | export default Header; -------------------------------------------------------------------------------- /src/components/Note.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { MdDeleteForever } from 'react-icons/md'; 3 | const Note = ({ id, text, date, handleDeleteNote }) => { 4 | return( 5 |
6 | {text} 7 |
8 | {date} 9 | handleDeleteNote(id)} 11 | className="delete-icon" 12 | size="1.3em" 13 | /> 14 |
15 |
16 | ) 17 | }; 18 | 19 | export default Note; -------------------------------------------------------------------------------- /src/components/NotesList.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Note from './Note'; 3 | import AddNote from './AddNote'; 4 | const NotesList = ({ notes,handleAddNote, handleDeleteNote }) => { 5 | return( 6 |
7 | {notes.map((note)=> ( 8 | 14 | ))} 15 | 16 |
17 | ); 18 | }; 19 | export default NotesList; -------------------------------------------------------------------------------- /src/components/Search.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {MdSearch} from 'react-icons/md'; 3 | const Search = ({ handleSearchNote }) => { 4 | return ( 5 |
6 | 7 | 8 | handleSearchNote(event.target.value) 9 | } 10 | type="text" placeholder="type to search"> 11 |
12 | ); 13 | }; 14 | export default Search; -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 | monospace; 13 | } 14 | 15 | .header { 16 | display: flex; 17 | align-items: center; 18 | justify-content: space-between; 19 | } 20 | 21 | .dark-mode { 22 | background-color: black; 23 | } 24 | 25 | .dark-mode h1 { 26 | color: white; 27 | } 28 | 29 | .container { 30 | max-width: 960px; 31 | margin-right: auto; 32 | margin-left: auto; 33 | padding-right: 15px; 34 | padding-left: 15px; 35 | min-height: 100vh; 36 | } 37 | .notes-list { 38 | display: grid; 39 | grid-gap: 1rem; 40 | grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); 41 | } 42 | .note.new { 43 | background-color: #67d7cc; 44 | } 45 | 46 | textarea { 47 | border: none; 48 | resize: none; 49 | background-color: #67d7cc; 50 | } 51 | 52 | textarea:focus { 53 | outline: none; 54 | } 55 | 56 | .save { 57 | background-color: #e1e1e1; 58 | border: none; 59 | border-radius: 15px; 60 | padding: 5px 10px 5px 10px; 61 | } 62 | 63 | .save:hover { 64 | background-color: #ededed; 65 | cursor: pointer; 66 | } 67 | 68 | .note{ 69 | background-color: #fef68a; 70 | border-radius: 10px; 71 | padding: 1rem; 72 | min-height: 170px; 73 | display: flex; 74 | flex-direction: column; 75 | justify-content: space-between; 76 | white-space: pre-wrap; 77 | } 78 | 79 | .note-footer { 80 | display: flex; 81 | align-items: center; 82 | justify-content: space-between; 83 | } 84 | 85 | .delete-icon { 86 | cursor: pointer; 87 | } 88 | 89 | .search { 90 | display: flex; 91 | align-items: center; 92 | background-color: rgb(233, 233, 233); 93 | border-radius: 10px; 94 | padding: 5px; 95 | margin-bottom: 1.5em; 96 | } 97 | .search input { 98 | border: none; 99 | background-color: rgb(233, 233, 233); 100 | } 101 | 102 | .search input:focus { 103 | outline: none; 104 | } -------------------------------------------------------------------------------- /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 | --------------------------------------------------------------------------------