A Pen by David Middleton on CodePen.
Created
May 15, 2020 21:43
-
-
Save middletond1/ef8561666df7cb3d736f7c085f6d9fd1 to your computer and use it in GitHub Desktop.
Google Keep Clone
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<html> | |
<head> | |
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Google+Sans:400,500&display=swap"></link> | |
<link rel="stylesheet" href="style.css"></link> | |
</head> | |
<body> | |
<div class="modal"> | |
<div class="modal-content"> | |
<input class="modal-title" placeholder="Title" type="text"> | |
<input class="modal-text" placeholder="Take a note..." type="text"> | |
<span class="modal-close-button">Close</span> | |
</div> | |
</div> | |
<main> | |
<header> | |
<img class="header-logo" src="https://www.gstatic.com/images/branding/product/1x/keep_48dp.png"> | |
<h2 class="header-title">Keep</h2> | |
</header> | |
<div id="form-container"> | |
<form id="form" autocomplete="off"> | |
<input id="note-title" placeholder="Title" type="text"> | |
<input id="note-text" placeholder="Take a note..." type="text"> | |
<div id="form-buttons"> | |
<button type="submit" id="submit-button">Submit</button> | |
<button type="button" id="form-close-button">Close</button> | |
</div> | |
</form> | |
</div> | |
<div id="notes"></div> | |
<div id="placeholder"> | |
<img id="placeholder-logo" src="https://icon.now.sh/lightbulb_outline"> | |
<p id="placeholder-text">Notes you add appear here</p> | |
</div> | |
<div id="color-tooltip"> | |
<div class="color-option" data-color="#fff" id="white"></div> | |
<div class="color-option" data-color="#d7aefb" id="purple"></div> | |
<div class="color-option" data-color="#fbbc04" id="orange"></div> | |
<div class="color-option" data-color="#a7ffeb" id="teal"></div> | |
</div> | |
</main> | |
<script src="app.js"></script> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class App { | |
constructor() { | |
this.notes = []; /*an array of the stored notes*/ | |
this.title = ''; | |
this.text = ''; | |
this.id = ''; | |
// following are references to html elements, noted by $ | |
this.$form = document.querySelector('#form'); | |
this.$noteTitle = document.querySelector('#note-title'); | |
this.$formButtons = document.querySelector('#form-buttons'); | |
this.$noteText = document.querySelector('#note-text'); | |
this.$placeholder = document.querySelector('#placeholder'); | |
this.$notes = document.querySelector('#notes'); | |
this.$formCloseButton = document.querySelector('#form-close-button'); | |
this.$modal = document.querySelector('.modal'); | |
this.$modalTitle = document.querySelector('.modal-title') | |
this.$modalText = document.querySelector('.modal-text') | |
this.addEventListeners(); /*Calls the addEventListener function when the app opens*/ | |
} | |
addEventListeners() { | |
document.body.addEventListener('click', event => { /*add click events to document body*/ | |
this.handleFormClick(event); | |
this.handleFormClose(event); | |
this.selectNote(event); | |
this.openModal(event); | |
}); | |
this.$form.addEventListener('submit', event =>{ /*add submit event to the form*/ | |
this.handleSubmit(event); | |
}) | |
} | |
handleFormClick(event) { /*Will open the form if the click event contains the form or close if outside */ | |
const isFormClicked = this.$form.contains(event.target); | |
if (isFormClicked) { | |
this.openForm(); | |
} else { | |
this.closeForm(); | |
} | |
} | |
handleFormClose(event) { | |
const isCloseClicked = this.$formCloseButton.contains(event.target); | |
if (isCloseClicked) { | |
this.closeForm(); | |
this.clearForm(); | |
} | |
} | |
selectNote(event) { | |
const $selectedNote = event.target.closest('.note'); | |
if(!$selectedNote) return; | |
const [$noteTitle, $noteText] = $selectedNote.children; | |
this.title = $noteTitle.innerText; | |
this.text = $noteText.innerText; | |
this.id = $selectedNote.dataset.id; | |
} | |
openModal(event) { | |
const isNoteClicked = event.target.closest('.note'); | |
if (isNoteClicked) { | |
this.$modal.classList.toggle('open-modal'); | |
this.$modalTitle.value = this.title; | |
this.$modalText.value = this.text; | |
} else return; | |
} | |
openForm() { | |
this.$form.classList.add('form-open'); | |
this.$noteTitle.style.display = 'block'; | |
this.$formButtons.style.display = 'block'; | |
} | |
closeForm() { | |
this.$form.classList.remove('form-open'); | |
this.$noteTitle.style.display = 'none'; | |
this.$formButtons.style.display = 'none'; | |
} | |
handleSubmit(event) { /*Will run if the submit event is called on the form.*/ | |
event.preventDefault(); /*Prevents the default submit function of refreshing the page*/ | |
const title = this.$noteTitle.value /*Stores the value of noteTitle and noteText in separate vars*/ | |
const text = this.$noteText.value | |
const completeNote = title || text; /*Will check to see if the note has a title or text before adding*/ | |
if (completeNote) { /*If there is a note, will call the addNote func*/ | |
this.addNote({ title, text }); /*object shorthand to pass the title and text values to the addNote func*/ | |
} | |
} | |
addNote(note) { | |
const newNote = { /*Creates a new object when called*/ | |
title: note.title, | |
text: note.text, | |
color: 'white', | |
id: this.notes.length > 0 ? this.notes[this.notes.length - 1].id + 1 : 1 | |
}; | |
this.notes = [...this.notes, newNote]; /*Updates the notes array with all the previous notes and adds the newNote*/ | |
this.displayNotes(); /*Call the displayNotes function to display them on the page*/ | |
this.closeForm(); | |
this.clearForm(); | |
} | |
displayNotes() { | |
const hasNotes = this.notes.length > 0; | |
this.$placeholder.style.display = hasNotes ? 'none' : 'flex'; | |
// Below, maps over the notes array, creating the HTML necessary to display each, and adds it to the notes element in the HTML file. | |
this.$notes.innerHTML = this.notes.map(note => ` | |
<div style="background: ${note.color};" class="note" data-id="${note.id}"> | |
<div class="${note.title && 'note-title'}">${note.title}</div> | |
<div class="note-text">${note.text}</div> | |
<div class="toolbar-container"> | |
<div class="toolbar"> | |
<img class="toolbar-color" src="https://icon.now.sh/palette"> | |
<img class="toolbar-delete" src="https://icon.now.sh/delete"> | |
</div> | |
</div> | |
</div> | |
`).join(""); /*Keeps the elements from being separated by a ' on the screen'*/ | |
} | |
clearForm() { | |
this.$noteTitle.value = ''; | |
this.$noteText.value = ''; | |
} | |
} | |
new App() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
* { | |
margin: 0; | |
padding: 0; | |
font-family: "Google Sans", sans-serif; | |
box-sizing: border-box; | |
} | |
html, | |
body { | |
background-color: #fff; | |
color: #202124; | |
font-size: 15px; | |
margin: 0; | |
min-width: 360px; | |
overflow-x: hidden; | |
overflow-y: auto; | |
padding: 0; | |
} | |
main { | |
margin: 0 auto; | |
max-width: 900px; | |
} | |
#placeholder { | |
margin-top: 10vh !important; | |
display: flex; | |
align-items: center; | |
flex-direction: column; | |
} | |
#placeholder-logo { | |
height: 120px; | |
margin: 20px; | |
opacity: 0.1; | |
width: 120px; | |
} | |
#placeholder-text { | |
color: #80868b; | |
cursor: default; | |
font-size: 1.375rem; | |
font-weight: 400; | |
line-height: 1.75rem; | |
} | |
#form-container { | |
border-radius: 8px; | |
border: 0.5px solid #d3d3d3; | |
box-shadow: 0 1px 2px 0 rgba(60, 64, 67, 0.302), | |
0 2px 6px 2px rgba(60, 64, 67, 0.149); | |
margin: 32px auto 16px auto; | |
max-width: 496px; | |
transition-duration: 0.218s; | |
transition-property: background, border, opacity, box-shadow, transform; | |
transition-timing-function: ease-in; | |
} | |
#form { | |
position: relative; | |
border: none; | |
margin: 1px; | |
border-radius: 8px; | |
transition-duration: 0.218s; | |
transition-property: background, border, opacity, box-shadow, transform; | |
transition-timing-function: ease-in; | |
} | |
.form-open { | |
box-shadow: 0 3px 5px rgba(0, 0, 0, 0.2); | |
} | |
header { | |
display: flex; | |
align-items: center; | |
justify-content: center; | |
margin-top: 2em; | |
} | |
.header-logo { | |
height: 4em; | |
} | |
.header-title { | |
color: #5f6368; | |
font-size: 2rem; | |
padding-top: 4px; | |
padding-left: 4px; | |
} | |
#notes { | |
display: flex; | |
flex-wrap: wrap; | |
justify-content: center; | |
} | |
#note-text { | |
width: calc(100% - 10px); | |
min-height: 43px; | |
margin: 0 5px; | |
padding: 10px 10px; | |
font-size: 1rem; | |
font-weight: 500; | |
line-height: 1.5rem; | |
/* box-shadow: 0 3px 5px rgba(0, 0, 0, 0.2); */ | |
letter-spacing: 0.00625em; | |
border: 0; | |
overflow: hidden; | |
position: relative; | |
} | |
.modal-title { | |
width: calc(100% - 10px); | |
min-height: 43px; | |
margin: 0 5px; | |
font-weight: 500; | |
line-height: 1.5rem; | |
letter-spacing: 0.00625em; | |
border: 0; | |
overflow: hidden; | |
position: relative; | |
font-size: 1.375rem; | |
font-weight: 400; | |
line-height: 1.75rem; | |
padding-bottom: 12px; | |
padding-top: 16px; | |
} | |
.modal-text { | |
width: calc(100% - 10px); | |
min-height: 43px; | |
margin: 0 5px; | |
padding: 10px 10px; | |
font-size: 1rem; | |
font-weight: 500; | |
line-height: 1.5rem; | |
letter-spacing: 0.00625em; | |
border: 0; | |
overflow: hidden; | |
position: relative; | |
color: #202124; | |
font-size: 14px; | |
line-height: 19px; | |
min-height: 46px; | |
padding: 4px 16px 12px 0px; | |
} | |
#note-title { | |
display: none; | |
width: calc(100% - 10px); | |
min-height: 43px; | |
margin: 0 5px; | |
padding: 10px 10px; | |
font-size: 1rem; | |
font-weight: 500; | |
line-height: 1.5rem; | |
border: 0; | |
} | |
#form:focus, | |
input:focus { | |
outline: none; | |
} | |
#placeholder { | |
margin-top: 20vh; | |
display: flex; | |
align-items: center; | |
flex-direction: column; | |
} | |
#form-buttons { | |
display: none; | |
text-align: right; | |
} | |
#submit-button, | |
#form-close-button { | |
margin: 0.2em 0; | |
box-sizing: border-box; | |
color: rgba(0, 0, 0, 0.87); | |
overflow: hidden; | |
text-overflow: ellipsis; | |
white-space: nowrap; | |
letter-spacing: 0.01785714em; | |
font-size: 0.875rem; | |
font-weight: 500; | |
line-height: 1.25rem; | |
height: 36px; | |
padding: 8px 24px; | |
border-radius: 4px; | |
border-color: transparent !important; | |
background-color: rgb(251, 251, 251); | |
} | |
#submit-button { | |
color: #fff; | |
background-color: #007bff; | |
} | |
#submit-button:hover, | |
#form-close-button:hover { | |
filter: brightness(95%); | |
} | |
.note { | |
display: flex; | |
flex-direction: column; | |
justify-content: space-between; | |
margin: 16px; | |
width: 250px; | |
background-color: #fff; | |
border-color: #e0e0e0; | |
border: 1px solid #d3d3d3; | |
box-sizing: border-box; | |
overflow: hidden; | |
position: relative; | |
border-radius: 8px; | |
transition-duration: 0.218s; | |
transition-property: background, border, opacity, box-shadow, transform; | |
transition-timing-function: ease-in; | |
} | |
.note:hover { | |
box-shadow: 0 1px 2px 0 rgba(60, 64, 67, 0.302), | |
0 1px 3px 1px rgba(60, 64, 67, 0.149); | |
} | |
.note-title { | |
padding-top: 12px; | |
letter-spacing: 0.00625em; | |
font-size: 1rem; | |
font-weight: 500; | |
line-height: 1.5rem; | |
min-height: 38px; | |
padding: 16px 16px 0 16px; | |
transition: background-color 0.218s ease-in; | |
box-sizing: border-box; | |
font-variant-ligatures: none; | |
outline: none; | |
white-space: pre-wrap; | |
word-wrap: break-word; | |
} | |
.note-text { | |
font-size: 1.125rem; | |
font-weight: 400; | |
line-height: 1.5rem; | |
min-height: 30px; | |
letter-spacing: 0.01428571em; | |
padding: 4px 16px 12px 16px; | |
box-sizing: border-box; | |
font-variant-ligatures: none; | |
outline: none; | |
white-space: pre-wrap; | |
word-wrap: break-word; | |
} | |
.toolbar { | |
opacity: 0; | |
flex-direction: row-reverse; | |
align-items: center; | |
color: rgba(0, 0, 0, 0.54); | |
display: flex; | |
font-size: 12px; | |
line-height: 26px; | |
margin: 4px 0; | |
position: relative; | |
transition: opacity 0.218s ease-in-out, background-color 0.218s ease-in-out, | |
box-shadow 0.218s ease-in-out; | |
} | |
.toolbar:hover { | |
opacity: 1; | |
} | |
.toolbar-color, | |
.toolbar-delete { | |
height: 20px; | |
margin: 0 8px; | |
width: 20px; | |
margin: 0 3px; | |
cursor: pointer; | |
color: #202124; | |
opacity: 0.71; | |
} | |
.toolbar-color-modal { | |
display: none; | |
} | |
#color-tooltip { | |
border-bottom: 2px solid #fff; | |
position: absolute; | |
top: -40; | |
background: #fff; | |
border: 1px solid black; | |
left: 0; | |
z-index: 20; | |
border-radius: 5px; | |
display: none; | |
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.2); | |
padding: 5px 7px 3px; | |
width: 136px; | |
height: 40px; | |
justify-content: space-between; | |
} | |
.color-option { | |
width: 25px; | |
height: 25px; | |
border-radius: 50%; | |
border: 1px solid #dedede; | |
} | |
.color-option:hover { | |
border: 1px solid black; | |
} | |
.modal { | |
position: fixed; | |
left: 0; | |
z-index: 200; | |
top: 0; | |
width: 100%; | |
height: 100%; | |
background-color: rgba(229, 229, 229, 0.5); | |
opacity: 0; | |
visibility: hidden; | |
transform: scale(1.1); | |
transition: visibility 0s linear 0.25s, opacity 0.25s 0s, transform 0.25s; | |
} | |
.modal-content { | |
box-shadow: 0 1px 3px 0 rgba(60, 64, 67, 0.302), | |
0 4px 8px 3px rgba(60, 64, 67, 0.149); | |
position: absolute; | |
top: 50%; | |
left: 50%; | |
transform: translate(-50%, -50%); | |
background-color: white; | |
padding: 1rem 1.5rem; | |
width: 24rem; | |
border-radius: 0.5rem; | |
} | |
.modal-close-button { | |
float: right; | |
margin: 0.2em 0; | |
box-sizing: border-box; | |
color: rgba(0, 0, 0, 0.87); | |
overflow: hidden; | |
text-overflow: ellipsis; | |
white-space: nowrap; | |
letter-spacing: 0.01785714em; | |
font-size: 1rem; | |
font-weight: 500; | |
line-height: 1.25rem; | |
height: 36px; | |
padding: 8px 24px; | |
border-radius: 4px; | |
border-color: transparent !important; | |
background-color: rgb(251, 251, 251); | |
cursor: pointer; | |
} | |
.modal-close-button:hover { | |
background-color: rgba(95, 99, 104, 0.039); | |
} | |
.open-modal { | |
opacity: 1; | |
visibility: visible; | |
transform: scale(1); | |
transition: visibility 0s linear 0s, opacity 0.25s 0s, transform 0.25s; | |
} | |
#white { | |
background: white; | |
} | |
#purple { | |
background: #d7aefb; | |
} | |
#orange { | |
background: #fbbc04; | |
} | |
#teal { | |
background: #a7ffeb; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment