diff --git a/README.md b/README.md index 73f14e3..0573511 100644 --- a/README.md +++ b/README.md @@ -11,8 +11,7 @@ You'll use props to break down our app into smaller components by passing down r 1. Fork this repository 2. Clone the forked repository onto your local machines 3. In the root directory, type `npm ci`, which installs dependencies for the project -4. Finally, type `npm run dev`, which starts a development server that runs your website in the browser. That server - will reload your website whenever you make any changes to source files +4. Finally, type `npm run dev`, which starts a development server that runs your website in the browser. That server will reload your website whenever you make any changes to source files ## Instructions - Break down `App.jsx` into components @@ -26,8 +25,7 @@ You'll use props to break down our app into smaller components by passing down r - Consider breaking down `App.css` into separate stylesheets for each of the components that you have created. ## Extension 1 -- Get the emails to open and display an email using **conditional rendering** and a component similar to what you - did with the [react-gmail-intro exercise](https://github.com/boolean-uk/react-gmail-intro/blob/main/images/gmail-email-view-intro.png) +- Get the emails to open and display an email using **conditional rendering** and a component similar to what you did with the [react-gmail-intro exercise](https://github.com/boolean-uk/react-gmail-intro/blob/main/images/gmail-email-view-intro.png) - You'll need to use the state to keep track of which email is selected and you'll need a component to display the email - Add a back button so users can return to their inbox diff --git a/src/App.jsx b/src/App.jsx index c902699..c722a74 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,123 +1,64 @@ -import { useState } from 'react' +import { useState } from "react"; -import initialEmails from './data/emails' +import initialEmails from "./data/emails"; -import './styles/App.css' +import "./styles/App.css"; +import List from "./components/EmailList/List"; +import Header from "./components/EmailHeader/Header"; +import LeftMenu from "./components/EmailLeftMenu/LeftMenu"; +import EmailViewer from "./components/EmailViewer/EmailViewer"; -const getReadEmails = emails => emails.filter(email => !email.read) - -const getStarredEmails = emails => emails.filter(email => email.starred) +const getReadEmails = (emails) => emails.filter((email) => !email.read); +const getStarredEmails = (emails) => emails.filter((email) => email.starred); function App() { - const [emails, setEmails] = useState(initialEmails) - const [hideRead, setHideRead] = useState(false) - const [currentTab, setCurrentTab] = useState('inbox') - - const unreadEmails = emails.filter(email => !email.read) - const starredEmails = emails.filter(email => email.starred) + const [emails, setEmails] = useState(initialEmails); + const [emailToView, setEmailToView] = useState(null); + const [hideRead, setHideRead] = useState(false); + const [currentTab, setCurrentTab] = useState("inbox"); - const toggleStar = targetEmail => { - const updatedEmails = emails => - emails.map(email => - email.id === targetEmail.id - ? { ...email, starred: !email.starred } - : email - ) - setEmails(updatedEmails) - } + const handleSearch = (q) => { + const query = q.trim().toLowerCase(); - const toggleRead = targetEmail => { - const updatedEmails = emails => - emails.map(email => - email.id === targetEmail.id ? { ...email, read: !email.read } : email - ) - setEmails(updatedEmails) - } + setEmails( + query + ? initialEmails.filter((e) => e.title.toLowerCase().includes(query)) + : initialEmails + ); + }; - let filteredEmails = emails + let filteredEmails = emails; - if (hideRead) filteredEmails = getReadEmails(filteredEmails) + if (hideRead) filteredEmails = getReadEmails(filteredEmails); - if (currentTab === 'starred') - filteredEmails = getStarredEmails(filteredEmails) + if (currentTab === "starred") + filteredEmails = getStarredEmails(filteredEmails); return (
-
-
- - - - - gmail logo -
- -
- -
-
- -
- -
+
+ setEmailToView(null)} + /> + {emailToView == null ? ( + + ) : ( + + )}
- ) + ); } -export default App +export default App; diff --git a/src/components/EmailHeader/Header.jsx b/src/components/EmailHeader/Header.jsx new file mode 100644 index 0000000..776e038 --- /dev/null +++ b/src/components/EmailHeader/Header.jsx @@ -0,0 +1,30 @@ +import "./style.css"; + +function Header({ onSearch }) { + return ( + <> +
+
+ + + + + gmail logo +
+ +
+ onSearch(e.target.value)} + /> +
+
+ + ); +} + +export default Header; diff --git a/src/components/EmailHeader/style.css b/src/components/EmailHeader/style.css new file mode 100644 index 0000000..475605c --- /dev/null +++ b/src/components/EmailHeader/style.css @@ -0,0 +1,34 @@ +.app > .header { + grid-area: header; + + display: grid; + grid-template-columns: 180px 1fr; + + border-bottom: solid 1px #00000020; +} + +.app > .header > .left-menu { + display: grid; + grid-auto-flow: column; + align-items: center; + justify-content: left; +} + +.app > .header > .left-menu .menu-icon { + width: 25px; + height: 25px; + margin: 10px; +} + +.app > .header > .search { + display: grid; + padding: 6px; +} + +.app > .header > .search .search-bar { + border: none; + border-radius: 5px; + background-color: rgb(240, 240, 240); + padding: 0 10px; + font-size: 1rem; +} diff --git a/src/components/EmailLeftMenu/LeftMenu.jsx b/src/components/EmailLeftMenu/LeftMenu.jsx new file mode 100644 index 0000000..803f83a --- /dev/null +++ b/src/components/EmailLeftMenu/LeftMenu.jsx @@ -0,0 +1,47 @@ +import "./style.css"; + +function LeftMenu({ + emails, + currentTab, + hideRead, + setCurrentTab, + setHideRead, + onClick +}) { + const unreadEmails = emails.filter((email) => !email.read); + const starredEmails = emails.filter((email) => email.starred); + + return ( + + ); +} + +export default LeftMenu; diff --git a/src/components/EmailLeftMenu/style.css b/src/components/EmailLeftMenu/style.css new file mode 100644 index 0000000..af02989 --- /dev/null +++ b/src/components/EmailLeftMenu/style.css @@ -0,0 +1,29 @@ +.app > .left-menu { + grid-area: left-menu; + padding: 10px; + padding-left: 0; +} + +.app > .left-menu .inbox-list { + display: grid; + grid-gap: 10px; +} + +.app > .left-menu .inbox-list .item { + padding: 5px 10px; + display: grid; + grid-auto-flow: column; + justify-content: space-between; + align-items: center; + + border-top-right-radius: 20px; + border-bottom-right-radius: 20px; +} + +.app > .left-menu .inbox-list .item.active { + background-color: #ff000050; +} + +.app > .left-menu .inbox-list .item.toggle { + background-color: transparent; +} diff --git a/src/components/EmailList/List.jsx b/src/components/EmailList/List.jsx new file mode 100644 index 0000000..62b3ded --- /dev/null +++ b/src/components/EmailList/List.jsx @@ -0,0 +1,32 @@ +import "./style.css"; +import ListItem from "./ListItem"; + +function List({ filteredEmails, setEmails, setEmailToView, emailToView }) { + const handleOpen = (e) => { + if (emailToView == null) { + setEmailToView(e); + } else { + setEmailToView(null); + } + + console.log(emailToView); + }; + + return ( +
+ +
+ ); +} + +export default List; diff --git a/src/components/EmailList/ListItem.jsx b/src/components/EmailList/ListItem.jsx new file mode 100644 index 0000000..f0d95e6 --- /dev/null +++ b/src/components/EmailList/ListItem.jsx @@ -0,0 +1,53 @@ +import "./style.css"; + +function ListItem({ emailToDisplay, index, setEmails, onClick }) { + const toggleStar = (targetEmail) => { + const updatedEmails = (emails) => + emails.map((email) => + email.id === targetEmail.id + ? { ...email, starred: !email.starred } + : email + ); + setEmails(updatedEmails); + }; + + const toggleRead = (targetEmail) => { + const updatedEmails = (emails) => + emails.map((email) => + email.id === targetEmail.id ? { ...email, read: !email.read } : email + ); + setEmails(updatedEmails); + }; + + return ( +
  • +
    + toggleRead(emailToDisplay)} + /> +
    + +
    + toggleStar(emailToDisplay)} + /> +
    + +
    {emailToDisplay.sender}
    + +
    {emailToDisplay.title}
    +
  • + ); +} + +export default ListItem; diff --git a/src/components/EmailList/style.css b/src/components/EmailList/style.css new file mode 100644 index 0000000..be51d86 --- /dev/null +++ b/src/components/EmailList/style.css @@ -0,0 +1,71 @@ +.app > .emails { + grid-area: emails; + padding: 10px 0; +} + +.app > .emails > ul { + list-style: none; + display: grid; +} + +.email { + display: grid; + grid-template-columns: 30px 30px 150px 1fr; + padding: 10px; + background-color: white; + font-size: 0.9rem; + + border-bottom: solid 1px #00000020; +} + +.email:first-child { + border-top: solid 1px #00000020; +} + +.email:hover { + box-shadow: -1px 1px 2px 0 #00000040; + z-index: 1; +} + +.email.read { + background-color: rgb(240, 240, 240); +} + +.email.unread { + font-weight: 600; +} + +.email .title { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.email .star-checkbox { + appearance: none; + position: relative; + display: grid; + align-content: center; +} + +.email .star-checkbox:focus { + outline: none; +} + +.email .star-checkbox:checked::before, +.email .star-checkbox::before { + content: ""; + background-size: cover; + width: 20px; + height: 20px; + position: absolute; + top: -3px; +} + +.email .star-checkbox::before { + background-image: url(https://www.gstatic.com/images/icons/material/system/2x/star_border_black_20dp.png); +} + +.email .star-checkbox:checked::before { + background-image: url(https://www.gstatic.com/images/icons/material/system/2x/star_googyellow500_20dp.png); +} diff --git a/src/components/EmailViewer/EmailViewer.jsx b/src/components/EmailViewer/EmailViewer.jsx new file mode 100644 index 0000000..85454ce --- /dev/null +++ b/src/components/EmailViewer/EmailViewer.jsx @@ -0,0 +1,29 @@ +import "./style.css"; + +function EmailViewer({ emailToView, setEmailToView }) { + return ( +
    +
    + + + +
    + +
    +
    1 of 25
    + + +
    + + + +
    sender: {emailToView.sender}
    + +
    title: {emailToView.title}
    + +
    other: {JSON.stringify(emailToView)}
    +
    + ); +} + +export default EmailViewer; diff --git a/src/components/EmailViewer/style.css b/src/components/EmailViewer/style.css new file mode 100644 index 0000000..e69de29 diff --git a/src/styles/App.css b/src/styles/App.css index da375b1..8c74e8e 100644 --- a/src/styles/App.css +++ b/src/styles/App.css @@ -6,141 +6,4 @@ 'header header' 50px 'left-menu emails' 1fr / 180px 1fr; -} - -.app > .header { - grid-area: header; - - display: grid; - grid-template-columns: 180px 1fr; - - border-bottom: solid 1px #00000020; -} - -.app > .header > .left-menu { - display: grid; - grid-auto-flow: column; - align-items: center; - justify-content: left; -} - -.app > .header > .left-menu .menu-icon { - width: 25px; - height: 25px; - margin: 10px; -} - -.app > .header > .search { - display: grid; - padding: 6px; -} - -.app > .header > .search .search-bar { - border: none; - border-radius: 5px; - background-color: rgb(240, 240, 240); - padding: 0 10px; - font-size: 1rem; -} - -.app > .emails { - grid-area: emails; - padding: 10px 0; -} - -.app > .emails > ul { - list-style: none; - display: grid; -} - -.app > .left-menu { - grid-area: left-menu; - padding: 10px; - padding-left: 0; -} - -.app > .left-menu .inbox-list { - display: grid; - grid-gap: 10px; -} - -.app > .left-menu .inbox-list .item { - padding: 5px 10px; - display: grid; - grid-auto-flow: column; - justify-content: space-between; - align-items: center; - - border-top-right-radius: 20px; - border-bottom-right-radius: 20px; -} - -.app > .left-menu .inbox-list .item.active { - background-color: #ff000050; -} - -.app > .left-menu .inbox-list .item.toggle { - background-color: transparent; -} - -.email { - display: grid; - grid-template-columns: 30px 30px 150px 1fr; - padding: 10px; - background-color: white; - font-size: 0.9rem; - - border-bottom: solid 1px #00000020; -} - -.email:first-child { - border-top: solid 1px #00000020; -} - -.email:hover { - box-shadow: -1px 1px 2px 0 #00000040; - z-index: 1; -} - -.email.read { - background-color: rgb(240, 240, 240); -} - -.email.unread { - font-weight: 600; -} - -.email .title { - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} - -.email .star-checkbox { - appearance: none; - position: relative; - display: grid; - align-content: center; -} - -.email .star-checkbox:focus { - outline: none; -} - -.email .star-checkbox:checked::before, -.email .star-checkbox::before { - content: ''; - background-size: cover; - width: 20px; - height: 20px; - position: absolute; - top: -3px; -} - -.email .star-checkbox::before { - background-image: url(https://www.gstatic.com/images/icons/material/system/2x/star_border_black_20dp.png); -} - -.email .star-checkbox:checked::before { - background-image: url(https://www.gstatic.com/images/icons/material/system/2x/star_googyellow500_20dp.png); -} +} \ No newline at end of file