functional
This commit is contained in:
parent
d25e8506ab
commit
5ecdd2febe
14 changed files with 2140 additions and 0 deletions
1
.gitignore
vendored
Executable file
1
.gitignore
vendored
Executable file
|
|
@ -0,0 +1 @@
|
||||||
|
node_modules/
|
||||||
33
dist/index.html
vendored
Executable file
33
dist/index.html
vendored
Executable file
|
|
@ -0,0 +1,33 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||||
|
<meta name="Description" content="Enter your description here"/>
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/5.1.0/css/bootstrap.min.css">
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css">
|
||||||
|
<link rel="stylesheet" href="../src/style.css">
|
||||||
|
<title>To-Do List</title>
|
||||||
|
</head>
|
||||||
|
<body class="d-flex h-100 text-center bg-dark">
|
||||||
|
<div class="cover-container d-flex w-100 h-100 px-3 flex-column">
|
||||||
|
<header class="d-flex flex-wrap justify-content-between py-3 border-bottom align-items-center">
|
||||||
|
<div class="dropdown">
|
||||||
|
<button class="btn btn-primary btn-lg dropdown-toggle" type="button" id="dropdownMenuButton1" data-bs-toggle="dropdown" aria-expanded="false">
|
||||||
|
</button>
|
||||||
|
<ul class="dropdown-menu" aria-labelledby="dropdownMenuButton1">
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<ul class="nav nav-pills">
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
<main class="col-sm-6 py-3"></main>
|
||||||
|
</div>
|
||||||
|
<script type="module" src="main.js"></script>
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/2.9.2/umd/popper.min.js"></script>
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/5.1.0/js/bootstrap.min.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
146
dist/main.js
vendored
Executable file
146
dist/main.js
vendored
Executable file
File diff suppressed because one or more lines are too long
1331
package-lock.json
generated
Executable file
1331
package-lock.json
generated
Executable file
File diff suppressed because it is too large
Load diff
23
package.json
Executable file
23
package.json
Executable file
|
|
@ -0,0 +1,23 @@
|
||||||
|
{
|
||||||
|
"name": "js-todolist",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "to-do list implemented in ES6",
|
||||||
|
"main": "js/index.js",
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1",
|
||||||
|
"build": "webpack"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"js",
|
||||||
|
"javascript",
|
||||||
|
"odin",
|
||||||
|
"todo",
|
||||||
|
"list"
|
||||||
|
],
|
||||||
|
"author": "ak",
|
||||||
|
"license": "AGPL-3.0-or-later",
|
||||||
|
"devDependencies": {
|
||||||
|
"webpack": "^5.77.0",
|
||||||
|
"webpack-cli": "^5.0.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
429
src/domifier.js
Executable file
429
src/domifier.js
Executable file
|
|
@ -0,0 +1,429 @@
|
||||||
|
import { todo } from "./todo.js";
|
||||||
|
import { project } from "./project.js";
|
||||||
|
import { getArray, setArray } from "./projects.js"
|
||||||
|
|
||||||
|
const CURRENTPROJECT = document.getElementById("dropdownMenuButton1");
|
||||||
|
|
||||||
|
// shim for setting proper prototype when pulling from JSON
|
||||||
|
const PROJECT = project("shim");
|
||||||
|
|
||||||
|
// initializes certain dom objects
|
||||||
|
export const initDOM = () => {
|
||||||
|
// creates New Todo button
|
||||||
|
const newTodo = document.createElement("button");
|
||||||
|
newTodo.className = "btn btn-warning";
|
||||||
|
newTodo.textContent = "+ New Todo";
|
||||||
|
// event handlers
|
||||||
|
newTodo.onclick = () => createTodo();
|
||||||
|
// appends
|
||||||
|
document.querySelector(".nav-pills").appendChild(newTodo)
|
||||||
|
|
||||||
|
// creates New Project button
|
||||||
|
const newProject = document.createElement("button");
|
||||||
|
newProject.className = "btn btn-success";
|
||||||
|
newProject.textContent = "+ New Project";
|
||||||
|
// event handlers
|
||||||
|
newProject.onclick = () => {
|
||||||
|
// asks for a project name
|
||||||
|
const projectName = prompt("What will your new project be called?", "Default Project");
|
||||||
|
const dummyProjects = getArray();
|
||||||
|
// checks for blanks or default values
|
||||||
|
if (projectName === null || projectName === "" || projectName === "Default Project") {
|
||||||
|
alert("Please enter a valid project name");
|
||||||
|
}
|
||||||
|
|
||||||
|
// checks if any projects are named the same way
|
||||||
|
else if (dummyProjects.find (item => item.name === projectName)) {
|
||||||
|
alert("Please enter an unused project name");
|
||||||
|
}
|
||||||
|
// if valid, create new project
|
||||||
|
else {
|
||||||
|
createProject(projectName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// appends
|
||||||
|
document.querySelector(".nav-pills").appendChild(newProject)
|
||||||
|
|
||||||
|
// creates Delete Project button
|
||||||
|
const delProject = document.createElement("button");
|
||||||
|
delProject.className = "btn btn-danger";
|
||||||
|
delProject.textContent = "x Delete Project";
|
||||||
|
// event handlers
|
||||||
|
delProject.onclick = () => deleteProject();
|
||||||
|
// appends
|
||||||
|
document.querySelector(".nav-pills").appendChild(delProject);
|
||||||
|
|
||||||
|
displayProjects();
|
||||||
|
}
|
||||||
|
|
||||||
|
export const changeCurrentProject = projectName => {
|
||||||
|
// changes dropdown button to main project
|
||||||
|
CURRENTPROJECT.textContent = projectName;
|
||||||
|
// clears the board
|
||||||
|
document.querySelector("main").innerHTML = "";
|
||||||
|
// finds all todos in the project and displays them (if any)
|
||||||
|
const dummyProjects = getArray();
|
||||||
|
const projectInArray = dummyProjects.find (item => item.name === CURRENTPROJECT.textContent);
|
||||||
|
if (projectInArray.todos.length > -1) {
|
||||||
|
projectInArray.todos.forEach(todo => displayTodo(todo));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const displayTodo = todo => {
|
||||||
|
// sets up the necessary containers
|
||||||
|
const card = document.createElement('div');
|
||||||
|
card.className = "card";
|
||||||
|
const cardBody = document.createElement('div');
|
||||||
|
cardBody.className = "card-body";
|
||||||
|
// appends title of current todo to DOM
|
||||||
|
const title = document.createElement('h5');
|
||||||
|
title.className = "card-title mb-2";
|
||||||
|
title.textContent = todo.title;
|
||||||
|
const dueDate = document.createElement('h6');
|
||||||
|
dueDate.className = "card-subtitle text-muted mb-1";
|
||||||
|
// bit of a rigamarole due to localStorage
|
||||||
|
const date = new Date(Date.parse(todo.dueDate));
|
||||||
|
dueDate.textContent = "Due: " + date.toDateString();
|
||||||
|
//
|
||||||
|
const priority = document.createElement('h6');
|
||||||
|
priority.className = "mb-3 btn prioritybtn";
|
||||||
|
const determinePriority = () => {
|
||||||
|
if (todo.priority === 0) {
|
||||||
|
priority.className = priority.className + " btn-success";
|
||||||
|
priority.textContent = "Low";
|
||||||
|
}
|
||||||
|
else if (todo.priority == 1) {
|
||||||
|
priority.className = priority.className + " btn-warning";
|
||||||
|
priority.textContent = "Medium";
|
||||||
|
}
|
||||||
|
else if (todo.priority == 2) {
|
||||||
|
priority.className = priority.className + " btn-danger";
|
||||||
|
priority.textContent = "High";
|
||||||
|
}
|
||||||
|
return priority;
|
||||||
|
}
|
||||||
|
priority.textContent = "Priority: " + determinePriority().textContent;
|
||||||
|
|
||||||
|
const comment = document.createElement('p');
|
||||||
|
comment.className = "card-text text-muted";
|
||||||
|
comment.textContent = todo.description;
|
||||||
|
|
||||||
|
const editButton = document.createElement('button');
|
||||||
|
editButton.className = "btn btn-primary me-1";
|
||||||
|
const editButtonText = document.createElement('div');
|
||||||
|
editButtonText.textContent = "Edit";
|
||||||
|
editButton.onclick = () => editTodo(cardBody, todo);
|
||||||
|
|
||||||
|
const deleteButton = document.createElement('button');
|
||||||
|
deleteButton.className = "btn btn-danger ms-1";
|
||||||
|
const deleteButtonText = document.createElement('div');
|
||||||
|
deleteButtonText.textContent = "Delete";
|
||||||
|
deleteButton.onclick = () => deleteTodo(todo, card);
|
||||||
|
|
||||||
|
// append to DOM
|
||||||
|
document.querySelector("main").appendChild(card);
|
||||||
|
card.appendChild(cardBody);
|
||||||
|
cardBody.appendChild(title);
|
||||||
|
cardBody.appendChild(dueDate);
|
||||||
|
cardBody.appendChild(priority);
|
||||||
|
cardBody.appendChild(comment);
|
||||||
|
cardBody.appendChild(editButton);
|
||||||
|
editButton.appendChild(editButtonText);
|
||||||
|
cardBody.appendChild(deleteButton);
|
||||||
|
deleteButton.appendChild(deleteButtonText);
|
||||||
|
}
|
||||||
|
|
||||||
|
const deleteProject = () => {
|
||||||
|
// removes the todos from the project
|
||||||
|
const dummyProjects = getArray();
|
||||||
|
const projectInArray = dummyProjects.find (item => item.name === CURRENTPROJECT.textContent);
|
||||||
|
projectInArray.todos = [];
|
||||||
|
|
||||||
|
// finds the current project in the dropdown menu
|
||||||
|
// gets array of dropdown objects in dom and casts it to a "real" array;
|
||||||
|
const dropdownArray = Array.prototype.slice.call(document.getElementsByClassName("dropdown-item"));
|
||||||
|
// finds the project which matches current project
|
||||||
|
const currentProjectDropdown = dropdownArray.find(element => element.textContent === CURRENTPROJECT.textContent);
|
||||||
|
// finds the next project
|
||||||
|
const nextProjectDropdown = dropdownArray[dropdownArray.indexOf(currentProjectDropdown) + 1];
|
||||||
|
const prevProjectDropdown = dropdownArray[dropdownArray.indexOf(currentProjectDropdown) - 1];
|
||||||
|
|
||||||
|
// removes the current project from dropdowns
|
||||||
|
const li = currentProjectDropdown.parentElement;
|
||||||
|
li.parentElement.removeChild(li);
|
||||||
|
|
||||||
|
// removes from projects array
|
||||||
|
const projectNumber = dummyProjects.indexOf(projectInArray);
|
||||||
|
dummyProjects.splice(projectNumber, 1);
|
||||||
|
setArray(dummyProjects);
|
||||||
|
|
||||||
|
// changes current project
|
||||||
|
if (nextProjectDropdown) {
|
||||||
|
changeCurrentProject(nextProjectDropdown.textContent);
|
||||||
|
}
|
||||||
|
else if (prevProjectDropdown) {
|
||||||
|
changeCurrentProject(prevProjectDropdown.textContent);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
CURRENTPROJECT.textContent = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const createProjectDOM = projectName => {
|
||||||
|
// adds to dropdown
|
||||||
|
const li = document.createElement("li");
|
||||||
|
document.querySelector(".dropdown-menu").appendChild(li);
|
||||||
|
const dropdownOption = document.createElement("a");
|
||||||
|
dropdownOption.className = "dropdown-item";
|
||||||
|
dropdownOption.textContent = projectName;
|
||||||
|
li.appendChild(dropdownOption);
|
||||||
|
// event handler for when user clicks the project in dropdowns
|
||||||
|
li.onclick = () => changeCurrentProject(projectName);
|
||||||
|
|
||||||
|
// changes to new project
|
||||||
|
changeCurrentProject(projectName);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const createTodo = () => {
|
||||||
|
// sets up the necessary containers
|
||||||
|
const card = document.createElement('div');
|
||||||
|
card.className = "card";
|
||||||
|
const cardBody = document.createElement('div');
|
||||||
|
cardBody.className = "card-body";
|
||||||
|
|
||||||
|
// creates input fields to get user input for todo info
|
||||||
|
const title = document.createElement('input');
|
||||||
|
title.className = "card-title mb-2 form-control";
|
||||||
|
title.type = "text";
|
||||||
|
title.placeholder = "Title";
|
||||||
|
|
||||||
|
const dueDate = document.createElement('input');
|
||||||
|
dueDate.className = "card-subtitle text-muted mb-2 form-control";
|
||||||
|
dueDate.type = "date";
|
||||||
|
dueDate.oninput = () => {
|
||||||
|
const dueDateSplit = dueDate.value.split('-');
|
||||||
|
if (dueDateSplit[0] && dueDateSplit[0].length > 4) {
|
||||||
|
dueDateSplit[0]=dueDateSplit[0].slice(0,4);
|
||||||
|
dueDate.value = dueDateSplit.join('-');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const priority = document.createElement('h6');
|
||||||
|
const oldClassName = "mb-3 btn btn-primary";
|
||||||
|
priority.className = oldClassName;
|
||||||
|
priority.textContent = "Priority"
|
||||||
|
let priorityLevel;
|
||||||
|
let counter = 0;
|
||||||
|
priority.onclick = () => {
|
||||||
|
if (counter === 0) {
|
||||||
|
priority.className = oldClassName + " btn-success";
|
||||||
|
priority.textContent = "Priority: Low";
|
||||||
|
priorityLevel = counter;
|
||||||
|
counter++;
|
||||||
|
}
|
||||||
|
else if (counter === 1) {
|
||||||
|
priority.className = oldClassName + " btn-warning";
|
||||||
|
priority.textContent = "Priority: Medium";
|
||||||
|
priorityLevel = counter;
|
||||||
|
counter++;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
priority.className = oldClassName + " btn-danger";
|
||||||
|
priority.textContent = "Priority: High";
|
||||||
|
priorityLevel = counter;
|
||||||
|
counter = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const comment = document.createElement('input');
|
||||||
|
comment.className = "card-text text-muted form-control mb-3";
|
||||||
|
comment.type = "text";
|
||||||
|
comment.placeholder = "Description"
|
||||||
|
|
||||||
|
const createButton = document.createElement('button');
|
||||||
|
createButton.className = "btn btn-success me-1";
|
||||||
|
const createButtonText = document.createElement('div');
|
||||||
|
createButtonText.textContent = "Create";
|
||||||
|
createButton.onclick = () => finalizeTodo();
|
||||||
|
|
||||||
|
const deleteButton = document.createElement('button');
|
||||||
|
deleteButton.className = "btn btn-danger ms-1";
|
||||||
|
const deleteButtonText = document.createElement('div');
|
||||||
|
deleteButtonText.textContent = "Cancel";
|
||||||
|
|
||||||
|
deleteButton.onclick = () => document.querySelector("main").removeChild(card);
|
||||||
|
|
||||||
|
document.querySelector('main').appendChild(card);
|
||||||
|
card.appendChild(cardBody);
|
||||||
|
cardBody.appendChild(title);
|
||||||
|
cardBody.appendChild(dueDate);
|
||||||
|
cardBody.appendChild(priority);
|
||||||
|
cardBody.appendChild(comment);
|
||||||
|
cardBody.appendChild(createButton);
|
||||||
|
createButton.appendChild(createButtonText);
|
||||||
|
cardBody.appendChild(deleteButton);
|
||||||
|
deleteButton.appendChild(deleteButtonText);
|
||||||
|
|
||||||
|
function finalizeTodo() {
|
||||||
|
// some validation
|
||||||
|
if (title.value === "Title" || title.value === "") {
|
||||||
|
alert("Please enter a name for your to-do");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (priorityLevel || priorityLevel === 0) {
|
||||||
|
// adds todo to project
|
||||||
|
const finalizedTodo = todo(title.value, comment.value, dueDate.valueAsDate, priorityLevel);
|
||||||
|
const dummyProjects = getArray();
|
||||||
|
let foundProject = dummyProjects.find (item => item.name === CURRENTPROJECT.textContent);
|
||||||
|
|
||||||
|
foundProject.todos[foundProject.todos.length] = finalizedTodo;
|
||||||
|
setArray(dummyProjects);
|
||||||
|
|
||||||
|
// wipes prompt window
|
||||||
|
document.querySelector("main").removeChild(card);
|
||||||
|
displayTodo(finalizedTodo);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
alert("Please select a priority for your to-do by clicking the Priority button");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const editTodo = (cardBody, todo) => {
|
||||||
|
// creates title field and fills with existing title
|
||||||
|
const title = document.createElement('input');
|
||||||
|
title.className = "card-title mb-2 form-control";
|
||||||
|
title.type = "text";
|
||||||
|
// gets title out of existing title (title is the first child of cardBody)
|
||||||
|
title.value = cardBody.firstChild.textContent;
|
||||||
|
|
||||||
|
|
||||||
|
const dueDate = document.createElement('input');
|
||||||
|
dueDate.maxLength = 8;
|
||||||
|
dueDate.className = "card-subtitle text-muted mb-2 form-control";
|
||||||
|
dueDate.type = "date";
|
||||||
|
if (typeof todo.dueDate === "object") {
|
||||||
|
dueDate.value = todo.dueDate.toISOString().slice(0,10);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
dueDate.value = todo.dueDate.slice(0,10);
|
||||||
|
}
|
||||||
|
dueDate.oninput = () => {
|
||||||
|
const dueDateSplit = dueDate.value.split('-');
|
||||||
|
if (dueDateSplit[0] && dueDateSplit[0].length > 4) {
|
||||||
|
dueDateSplit[0]=dueDateSplit[0].slice(0,4);
|
||||||
|
dueDate.value = dueDateSplit.join('-');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const priority = document.createElement('h6');
|
||||||
|
const oldClassName = "mb-3 btn btn-primary";
|
||||||
|
priority.className = oldClassName;
|
||||||
|
priority.textContent = "Priority";
|
||||||
|
let counter;
|
||||||
|
let priorityLevel = todo.priority;
|
||||||
|
if (todo.priority === 0) {
|
||||||
|
priority.className = oldClassName + " btn-success";
|
||||||
|
priority.textContent = "Priority: Low";
|
||||||
|
counter = 1;
|
||||||
|
}
|
||||||
|
else if (todo.priority === 1) {
|
||||||
|
priority.className = oldClassName + " btn-warning";
|
||||||
|
priority.textContent = "Priority: Medium";
|
||||||
|
counter = 2;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
priority.className = oldClassName + " btn-danger";
|
||||||
|
priority.textContent = "Priority: High";
|
||||||
|
counter = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
priority.onclick = () => {
|
||||||
|
if (counter === 0) {
|
||||||
|
priority.className = oldClassName + " btn-success";
|
||||||
|
priority.textContent = "Priority: Low";
|
||||||
|
priorityLevel = counter;
|
||||||
|
counter++;
|
||||||
|
}
|
||||||
|
else if (counter === 1) {
|
||||||
|
priority.className = oldClassName + " btn-warning";
|
||||||
|
priority.textContent = "Priority: Medium";
|
||||||
|
priorityLevel = counter;
|
||||||
|
counter++;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
priority.className = oldClassName + " btn-danger";
|
||||||
|
priority.textContent = "Priority: High";
|
||||||
|
priorityLevel = counter;
|
||||||
|
counter = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const comment = document.createElement('input');
|
||||||
|
comment.className = "card-text text-muted form-control mb-3";
|
||||||
|
comment.type = "text";
|
||||||
|
comment.value = cardBody.children[3].textContent;
|
||||||
|
|
||||||
|
for (let i = 0; i < 4; i++) {
|
||||||
|
cardBody.removeChild(cardBody.firstChild);
|
||||||
|
}
|
||||||
|
|
||||||
|
cardBody.firstChild.textContent = "Finish";
|
||||||
|
cardBody.firstChild.className = "btn btn-success me-1";
|
||||||
|
cardBody.firstChild.onclick = () => finalizeTodo();
|
||||||
|
|
||||||
|
cardBody.prepend(comment);
|
||||||
|
cardBody.prepend(priority);
|
||||||
|
cardBody.prepend(dueDate);
|
||||||
|
cardBody.prepend(title);
|
||||||
|
|
||||||
|
function finalizeTodo() {
|
||||||
|
// some validation
|
||||||
|
if (title.value === "") {
|
||||||
|
alert("Please enter a name for your to-do");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (priorityLevel || priorityLevel === 0) {
|
||||||
|
todo.title = title.value;
|
||||||
|
todo.description = comment.value;
|
||||||
|
todo.dueDate = dueDate.valueAsDate;
|
||||||
|
todo.priority = priorityLevel;
|
||||||
|
|
||||||
|
// proper handling
|
||||||
|
const dummyProjects = getArray();
|
||||||
|
const foundProject = dummyProjects.find (item => item.name === CURRENTPROJECT.textContent);
|
||||||
|
setArray(dummyProjects);
|
||||||
|
|
||||||
|
// wipes prompt window
|
||||||
|
document.querySelector("main").removeChild(cardBody.parentElement);
|
||||||
|
displayTodo(todo);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
alert("Please select a priority for your to-do by clicking the Priority button")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const deleteTodo = (todo, card) => {
|
||||||
|
document.querySelector("main").removeChild(card);
|
||||||
|
const dummyProjects = getArray();
|
||||||
|
let foundProject = dummyProjects.find (item => item.name === CURRENTPROJECT.textContent);
|
||||||
|
const todoIndex = foundProject.todos.indexOf(todo);
|
||||||
|
if (todoIndex) {
|
||||||
|
foundProject.todos.splice(todoIndex, 1);
|
||||||
|
}
|
||||||
|
setArray(dummyProjects);
|
||||||
|
}
|
||||||
|
|
||||||
|
const createProject = (projectName) => {
|
||||||
|
const dummyProjects = getArray();
|
||||||
|
dummyProjects[dummyProjects.length] = project(projectName);
|
||||||
|
setArray(dummyProjects);
|
||||||
|
createProjectDOM(projectName);
|
||||||
|
}
|
||||||
|
|
||||||
|
const displayProjects = () => {
|
||||||
|
const dummyProjects = getArray();
|
||||||
|
dummyProjects.forEach(item => createProjectDOM(item.name));
|
||||||
|
}
|
||||||
5
src/incrementer.js
Executable file
5
src/incrementer.js
Executable file
|
|
@ -0,0 +1,5 @@
|
||||||
|
// increments date based from today's date on number of days specified
|
||||||
|
export const incrementer = (date, days) => {
|
||||||
|
date.setDate(date.getDate() + days);
|
||||||
|
return date;
|
||||||
|
}
|
||||||
25
src/index.js
Executable file
25
src/index.js
Executable file
|
|
@ -0,0 +1,25 @@
|
||||||
|
import { todo } from "./todo.js"
|
||||||
|
import { incrementer } from "./incrementer.js";
|
||||||
|
import { changeCurrentProject, createProjectDOM } from "./domifier.js";
|
||||||
|
import { initDOM } from "./domifier.js";
|
||||||
|
import { getArray, setArray } from "./projects.js";
|
||||||
|
import { project } from "./project.js";
|
||||||
|
|
||||||
|
const TODAY = new Date();
|
||||||
|
let dummyProjects = getArray();
|
||||||
|
|
||||||
|
dummyProjects[0] = project("Default Project");
|
||||||
|
|
||||||
|
const defaultTodo = todo(
|
||||||
|
"Default Todo",
|
||||||
|
"This is the default to-do for the to-do list. Feel free to add some more!",
|
||||||
|
incrementer(TODAY, 7),
|
||||||
|
0
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
dummyProjects[0].todos[dummyProjects[0].todos.length] = defaultTodo;
|
||||||
|
setArray(dummyProjects);
|
||||||
|
|
||||||
|
initDOM();
|
||||||
|
changeCurrentProject(dummyProjects[0].name);
|
||||||
9
src/project.js
Executable file
9
src/project.js
Executable file
|
|
@ -0,0 +1,9 @@
|
||||||
|
export const project = (name) => {
|
||||||
|
// sets up array of todo objects
|
||||||
|
let todos = []
|
||||||
|
|
||||||
|
return {
|
||||||
|
name,
|
||||||
|
todos
|
||||||
|
}
|
||||||
|
}
|
||||||
52
src/projects.js
Executable file
52
src/projects.js
Executable file
|
|
@ -0,0 +1,52 @@
|
||||||
|
// logic for the projects array itself
|
||||||
|
import { storageAvailable } from "./storageAvailable.js";
|
||||||
|
|
||||||
|
// get and set arrays - get from local storage if available, if not then get from a global var
|
||||||
|
// set it here too, as well as extend the regular array methods
|
||||||
|
// everything will be here regards getting and setting this stuff, without a need to expose the actual array out to other modules
|
||||||
|
|
||||||
|
// master projects array
|
||||||
|
let projectsArray = [];
|
||||||
|
|
||||||
|
// tests if a "projects" item exists in local storage
|
||||||
|
const projectsExists = localStorage.getItem("projects") ? true : false;
|
||||||
|
|
||||||
|
// returns the proper array depending on where it is
|
||||||
|
export const getArray = () => {
|
||||||
|
// localStorage exists
|
||||||
|
if (storageAvailable) {
|
||||||
|
// "projects" exists
|
||||||
|
if (projectsExists) {
|
||||||
|
// set projects array to local storage and return
|
||||||
|
projectsArray = JSON.parse(localStorage.projects);
|
||||||
|
return projectsArray;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return projectsArray;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// otherwise return the projects array here
|
||||||
|
else {
|
||||||
|
return projectsArray;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// easiest workaround to getting too in-depth - passed array is set as the main, depending on if storage exists, etc.
|
||||||
|
export const setArray = (array) => {
|
||||||
|
// set the master array to passed array - regardless of localStorage
|
||||||
|
projectsArray = array;
|
||||||
|
// check for localStorage
|
||||||
|
if (storageAvailable) {
|
||||||
|
// and existing "projects"
|
||||||
|
if (projectsExists) {
|
||||||
|
// remove existing array from localStorage
|
||||||
|
localStorage.removeItem("projects");
|
||||||
|
// add the modified array
|
||||||
|
localStorage.projects = JSON.stringify(projectsArray);
|
||||||
|
}
|
||||||
|
// if "projects" doesn't exist, add it
|
||||||
|
else {
|
||||||
|
localStorage.projects = JSON.stringify(projectsArray);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
29
src/storageAvailable.js
Executable file
29
src/storageAvailable.js
Executable file
|
|
@ -0,0 +1,29 @@
|
||||||
|
function storageAvailableFunc(type) {
|
||||||
|
let storage;
|
||||||
|
try {
|
||||||
|
storage = window[type];
|
||||||
|
const x = "__storage_test__";
|
||||||
|
storage.setItem(x, x);
|
||||||
|
storage.removeItem(x);
|
||||||
|
return true;
|
||||||
|
} catch (e) {
|
||||||
|
return (
|
||||||
|
e instanceof DOMException &&
|
||||||
|
// everything except Firefox
|
||||||
|
(e.code === 22 ||
|
||||||
|
// Firefox
|
||||||
|
e.code === 1014 ||
|
||||||
|
// test name field too, because code might not be present
|
||||||
|
// everything except Firefox
|
||||||
|
e.name === "QuotaExceededError" ||
|
||||||
|
// Firefox
|
||||||
|
e.name === "NS_ERROR_DOM_QUOTA_REACHED") &&
|
||||||
|
// acknowledge QuotaExceededError only if there's something already stored
|
||||||
|
storage &&
|
||||||
|
storage.length !== 0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// copyright MDN https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API/Using_the_Web_Storage_API
|
||||||
|
|
||||||
|
export const storageAvailable = storageAvailableFunc("localStorage");
|
||||||
45
src/style.css
Executable file
45
src/style.css
Executable file
|
|
@ -0,0 +1,45 @@
|
||||||
|
.nav-pills {
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-toggle::after {
|
||||||
|
margin-left: .3em;
|
||||||
|
vertical-align: .15em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
width: 18rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.col-sm-6 {
|
||||||
|
flex: 1;
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
box-shadow: 0.5px 0.5px 5px black;
|
||||||
|
margin: 0rem 1.5rem 1.5rem 0rem;
|
||||||
|
flex-grow: 0;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.prioritybtn {
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
|
main {
|
||||||
|
display: inline-flex;
|
||||||
|
flex-flow: row wrap;
|
||||||
|
overflow: scroll;
|
||||||
|
justify-content: flex-start
|
||||||
|
}
|
||||||
|
|
||||||
|
/* hides scrollbar*/
|
||||||
|
main::-webkit-scrollbar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
main {
|
||||||
|
-ms-overflow-style: none;
|
||||||
|
scrollbar-width: none;
|
||||||
|
}
|
||||||
9
src/todo.js
Executable file
9
src/todo.js
Executable file
|
|
@ -0,0 +1,9 @@
|
||||||
|
// todo factory function
|
||||||
|
export const todo = (title, description, dueDate, priority) => {
|
||||||
|
return {
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
dueDate,
|
||||||
|
priority
|
||||||
|
}
|
||||||
|
}
|
||||||
3
webpack.config.js
Executable file
3
webpack.config.js
Executable file
|
|
@ -0,0 +1,3 @@
|
||||||
|
module.exports = {
|
||||||
|
mode: 'development'
|
||||||
|
};
|
||||||
Loading…
Add table
Reference in a new issue