Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hetav Desai | FrontEnd Project 1 Submission #1

Open
wants to merge 18 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 16 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 6 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# GetYourMentor Frontend project - Beginner/1

## DEMO LINK
<INSERT_DEMO_LINK_HERE>
https://noteshd.netlify.app/

## Target

Expand All @@ -20,13 +20,8 @@ https://collectui.com/designers/adriengervaix/notes-widget
- Clicking on any note in the left sidebar shows the note in the right side area.
- Left sidebar also has a "New note" button which creates a new note with title as "Untitled Note".

## Development
- Fork this repo.
- Keep all your project files in this repo
- Build the project with with vanilla (no library/framework) HTML, CSS and JavaScript.
- Join our Telegram group for any help, discussion, questions - https://t.me/getyourmentor

## Submitting the project
- Your project source code should be public on Github.
- Host your project on [Netlify](https://netlify.com) or [Vercel](https://vercel.com). And insert your demo link in the README.md file instead of `<INSERT_DEMO_LINK_HERE>`
- To submit your project for review, open a pull request of your repository against this repo -> https://github.com/getYourMentor-org/frontend-project-beginner-1
## Added Specifications
- Delete Button to delete currently open note
- Auto delete notes with empty title and content
- Not to allow creation of new note if last added note is empty
- Designed empty state to display when all notes are deleted
174 changes: 174 additions & 0 deletions app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
var notesList = document.querySelector(".notes-list");
var noteTitle = document.querySelector(".note-title");
var notenote = document.querySelector(".note-note");
var noteItem = document.querySelector(".note-item");
var notenote = document.querySelector(".note-note");
var emptyState = document.querySelector(".empty-state");

var selectedNote = 0;
var notes = [];

toggleSwitch = document.querySelector('.theme-switch input[type="checkbox"]');

function switchTheme(e) {
if (e.target.checked) {
document.documentElement.setAttribute("data-theme", "dark");
localStorage.setItem("theme", "dark");
} else {
document.documentElement.setAttribute("data-theme", "light");
localStorage.setItem("theme", "light");
}
}

if (currentTheme) {
toggleSwitch.checked = true;
}

toggleSwitch.addEventListener("change", switchTheme, false);

function initializeNotes() {
var notesArr = localStorage.getItem(["notes"]);
if (JSON.parse(notesArr) == null || JSON.parse(notesArr).length === 0) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

instead of parsing twice, you can parse once and store it in a variable. Also, on first start notesArr would be undefined, and hence JSON.parse will fail with error, won't it?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup, using variable would be better.

And localStorage.getItem() returns null if the data we want isn't store in local storage. And hence, on first start, notesArr will be null and JSON.parse() will return null as well.

notes = [
{
title: "Welcome to NotesHD!",
note: `This is a sample note, please feel free to edit or delete this note and get started with creating your own notes!`,
},
];
} else {
notes = JSON.parse(notesArr);
}
}

function getNotesCount() {
return Number(notes.length);
}

function isLastEmpty() {
if (
notes[getNotesCount() - 1].title === "" &&
notes[getNotesCount() - 1].note === ""
) {
return true;
}
return false;
}

function titleChangeHandler(idx) {
var noteTitle = document.querySelector(".note-title");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You have already stored this DOM element at the top. You can use that?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Turns out that I was trying to store a few elements like this one even before they were rendered and hence they would return null when trying to store on top. Got rid of such statements on top and used them where they were required, after they had been rendered.

idx = Number(idx);
notes[idx].title = noteTitle.innerHTML;
// if (noteTitle.innerHTML === "") {
// noteTitle.innerHTML = "Untitled";
// }
renderNotesList(false);
}

function noteChangeHandler(idx) {
var notenote = document.querySelector(".note-note");
idx = Number(idx);
notes[idx].note = notenote.value;
renderNotesList(false);
}

function addNote() {
var noOfNotes = notes.length;
if (noOfNotes != 0) {
if (!isLastEmpty()) {
notes.push({
title: "",
note: "",
});
} else {
alert("You already have an empty note!");
}
} else {
notes.push({
title: "",
note: "",
});
}
renderSelectedNote(noOfNotes, false);
}

function deleteNote(idx) {
idx = Number(idx);
notes.splice(idx, 1);
renderSelectedNote(0);
}

function deleteEmpty() {
notes.map((item, idx) => {
if (item.title === "" && item.note === "") {
notes.splice(idx, 1);
}
});
}

function renderNotesList() {
if (notes.length != 0) {
var notesListInner = "";
notes.map((item, idx) => {
notesListInner += `
<div id=${idx} class="note-list-item${
selectedNote === idx ? "-selected" : ""
}" onClick="renderSelectedNote(this.id)">
<h3 class="note-list-item-title">${
item.title === "" ? "Untitled" : item.title
}</h3>
<p class="note-list-item-note">${
item.note === "" ? "Note" : item.note
}</p>
</div>
`;
});
} else {
var notesListInner = `No notes to display`;
}
notesList.innerHTML = notesListInner;
localStorage.setItem(["notes"], JSON.stringify(notes));
}

function renderSelectedNote(idx, clearEmpty = true) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't seems like it renders selectednote, it renders the note whose index is passed. So may be renderNote or renderNoteWithIndex.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So here I'm always passing the index of selected note. But yeah, renderNoteWithIndex makes more sense.
Also, I could get rid of accepting index in this function and just read the selectedNoteIndex from the global scope.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also, a convention to name booleans is that they start with is, should, has etc...to sound like a question.
Eg. hasItems, shouldOpen, isEmpty....
@desaihetav now you suggest me a better name for clearEmpty

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, got the point. So this can be better named as shouldClearEmpty

idx = Number(idx);
selectedNote = idx;

clearEmpty && deleteEmpty();

var noteItemInner;
if (notes.length != 0) {
noteItemInner = `
<button id=${idx} class="btn delete" onclick="deleteNote(this.id)">
<img src="/images/trash.svg" class="btn-icon" />
<p>Delete</p>
</button>
<div contenteditable="true"
id=${idx}
type="text"
placeholder="Untitled"
class="note-title div-edit"
onInput="titleChangeHandler(this.id)">${notes[idx].title}</div>
<textarea
id=${idx}
type="text"
placeholder="Note"
class="note-note"
onInput="noteChangeHandler(this.id)"
>${notes[idx].note}</textarea>
`;
} else {
noteItemInner = `
<div class="empty-state">
<img src="images/file.svg" class="image" />
<h2>No Notes Created</h2>
<p>Quickly jot that thought down, before it's gone!</p>
</div>
`;
}
noteItem.innerHTML = noteItemInner;

renderNotesList();
}

initializeNotes();
renderSelectedNote(0);
1 change: 1 addition & 0 deletions images/file.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions images/plus.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions images/trash.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
51 changes: 51 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="./styles.css" />
<title>NotesHD | Hetav Desai</title>
</head>
<body>
<script src="theme.js" type="text/javascript"></script>
<div class="container">
<section class="left">
<div class="header">
<h2 class="notes-list-heading">Notes HD</h2>
<div class="theme-switch-wrapper">
<label class="theme-switch" for="checkbox">
<input type="checkbox" id="checkbox" />
<div class="slider round"></div>
</label>
</div>
</div>
<div class="notes-list">
<!-- <div class="note-list-item">
<h3 class="title">Title</h3>
<p class="note">note</p>
</div>
<div class="note-list-item-selected">
<h3 class="note-list-item-title">Title</h3>
<p class="note-list-item-note">note</p>
</div> -->
</div>
<button class="btn add" onclick="addNote()">
<img src="/images/plus.svg" class="btn-icon" />
<p>New Note</p>
</button>
</section>
<section class="right">
<div class="note-item">
<!-- <input type="text" placeholder="Title" class="note-title"></input> -->
<!-- <textarea
type="text"
placeholder="Note note"
class="note-note"
></textarea>
<small class="small-text">Last modified at 06:00 p.m.</small> -->
</div>
</section>
</div>
<script src="app.js" type="text/javascript"></script>
</body>
</html>
Loading