mirror of
https://github.com/freeCodeCamp/freeCodeCamp.git
synced 2026-03-07 18:01:40 -05:00
fix(curriculum): Allow more freedom of implementation in Todo App (#59624)
Co-authored-by: Jessica Wilkins <67210629+jdwilkin4@users.noreply.github.com>
This commit is contained in:
@@ -144,140 +144,136 @@
|
||||
"title": "Step 34"
|
||||
},
|
||||
{
|
||||
"id": "64facf6180824876f70a2e86",
|
||||
"id": "64fad07f43a101779cb8692a",
|
||||
"title": "Step 35"
|
||||
},
|
||||
{
|
||||
"id": "64fad07f43a101779cb8692a",
|
||||
"id": "64fad9cd2eeb1e7ca2ca8c8b",
|
||||
"title": "Step 36"
|
||||
},
|
||||
{
|
||||
"id": "64fad9cd2eeb1e7ca2ca8c8b",
|
||||
"id": "64fadae4f2d51b7d5d8b98d8",
|
||||
"title": "Step 37"
|
||||
},
|
||||
{
|
||||
"id": "64fadae4f2d51b7d5d8b98d8",
|
||||
"id": "64fadff23375f27ff06c6d40",
|
||||
"title": "Step 38"
|
||||
},
|
||||
{
|
||||
"id": "64fadff23375f27ff06c6d40",
|
||||
"id": "64faf0418e828c0114a558a7",
|
||||
"title": "Step 39"
|
||||
},
|
||||
{
|
||||
"id": "64faf0418e828c0114a558a7",
|
||||
"id": "64fae068bcdc9c805bd8399e",
|
||||
"title": "Step 40"
|
||||
},
|
||||
{
|
||||
"id": "64fae068bcdc9c805bd8399e",
|
||||
"id": "64faedcd16a1e985c4c2dc94",
|
||||
"title": "Step 41"
|
||||
},
|
||||
{
|
||||
"id": "64faedcd16a1e985c4c2dc94",
|
||||
"id": "64faf65b22ad8d07df9be14d",
|
||||
"title": "Step 42"
|
||||
},
|
||||
{
|
||||
"id": "64faf65b22ad8d07df9be14d",
|
||||
"id": "64faf874364ec308f875f636",
|
||||
"title": "Step 43"
|
||||
},
|
||||
{
|
||||
"id": "64faf874364ec308f875f636",
|
||||
"id": "64fafac95328110a69bcb75f",
|
||||
"title": "Step 44"
|
||||
},
|
||||
{
|
||||
"id": "64fafac95328110a69bcb75f",
|
||||
"id": "64fb0fa0968f2b113b2d90e9",
|
||||
"title": "Step 45"
|
||||
},
|
||||
{
|
||||
"id": "64fb0fa0968f2b113b2d90e9",
|
||||
"id": "64fb1061ca838611ed6a7d6b",
|
||||
"title": "Step 46"
|
||||
},
|
||||
{
|
||||
"id": "64fb1061ca838611ed6a7d6b",
|
||||
"id": "64fb1321e189a6136d200f77",
|
||||
"title": "Step 47"
|
||||
},
|
||||
{
|
||||
"id": "64fb1321e189a6136d200f77",
|
||||
"id": "64fb1436adef3e145b4c3501",
|
||||
"title": "Step 48"
|
||||
},
|
||||
{
|
||||
"id": "64fb1436adef3e145b4c3501",
|
||||
"id": "64fb14d890415c14f93069ce",
|
||||
"title": "Step 49"
|
||||
},
|
||||
{
|
||||
"id": "64fb14d890415c14f93069ce",
|
||||
"id": "64fb154a7c48cd159924bb18",
|
||||
"title": "Step 50"
|
||||
},
|
||||
{
|
||||
"id": "64fb154a7c48cd159924bb18",
|
||||
"id": "64fb1c4dc0feb219149a7c7d",
|
||||
"title": "Step 51"
|
||||
},
|
||||
{
|
||||
"id": "64fb1c4dc0feb219149a7c7d",
|
||||
"id": "64fb285637fa1e1c222033e3",
|
||||
"title": "Step 52"
|
||||
},
|
||||
{
|
||||
"id": "64fb285637fa1e1c222033e3",
|
||||
"id": "64fb29348a60361ccd45c1e2",
|
||||
"title": "Step 53"
|
||||
},
|
||||
{
|
||||
"id": "64fb29348a60361ccd45c1e2",
|
||||
"id": "64fefebad99209211ec30537",
|
||||
"title": "Step 54"
|
||||
},
|
||||
{
|
||||
"id": "64fefebad99209211ec30537",
|
||||
"id": "64ff0313700dad264d19dfe4",
|
||||
"title": "Step 55"
|
||||
},
|
||||
{
|
||||
"id": "64ff0313700dad264d19dfe4",
|
||||
"id": "64ff04cc33779427a6412449",
|
||||
"title": "Step 56"
|
||||
},
|
||||
{
|
||||
"id": "64ff04cc33779427a6412449",
|
||||
"id": "64ff068e0426eb288874ed79",
|
||||
"title": "Step 57"
|
||||
},
|
||||
{
|
||||
"id": "64ff068e0426eb288874ed79",
|
||||
"id": "64ff23daf176a92de95f24dc",
|
||||
"title": "Step 58"
|
||||
},
|
||||
{
|
||||
"id": "64ff23daf176a92de95f24dc",
|
||||
"id": "64ff24b80431f62ec6b93f65",
|
||||
"title": "Step 59"
|
||||
},
|
||||
{
|
||||
"id": "64ff24b80431f62ec6b93f65",
|
||||
"id": "65003986d17d1e1865b269c0",
|
||||
"title": "Step 60"
|
||||
},
|
||||
{
|
||||
"id": "65003986d17d1e1865b269c0",
|
||||
"id": "650046832f92c01a35834bca",
|
||||
"title": "Step 61"
|
||||
},
|
||||
{
|
||||
"id": "650046832f92c01a35834bca",
|
||||
"id": "650048b0764f9c1b798200e2",
|
||||
"title": "Step 62"
|
||||
},
|
||||
{
|
||||
"id": "650048b0764f9c1b798200e2",
|
||||
"id": "65004ba581d03d1d5628b41c",
|
||||
"title": "Step 63"
|
||||
},
|
||||
{
|
||||
"id": "65004ba581d03d1d5628b41c",
|
||||
"id": "6632420f81f3cc554a5e540b",
|
||||
"title": "Step 64"
|
||||
},
|
||||
{
|
||||
"id": "6632420f81f3cc554a5e540b",
|
||||
"id": "66ad0f178ed5791ed898fe56",
|
||||
"title": "Step 65"
|
||||
},
|
||||
{
|
||||
"id": "66ad0f178ed5791ed898fe56",
|
||||
"id": "671682cd6d7aa95f0078f35f",
|
||||
"title": "Step 66"
|
||||
},
|
||||
{
|
||||
"id": "671682cd6d7aa95f0078f35f",
|
||||
"title": "Step 67"
|
||||
},
|
||||
{
|
||||
"id": "67168a7243b6396cb69c1bdf",
|
||||
"title": "Step 68"
|
||||
"title": "Step 67"
|
||||
}
|
||||
],
|
||||
"helpCategory": "JavaScript",
|
||||
|
||||
@@ -145,141 +145,137 @@
|
||||
"title": "Step 34"
|
||||
},
|
||||
{
|
||||
"id": "64facf6180824876f70a2e86",
|
||||
"id": "64fad07f43a101779cb8692a",
|
||||
"title": "Step 35"
|
||||
},
|
||||
{
|
||||
"id": "64fad07f43a101779cb8692a",
|
||||
"id": "64fad9cd2eeb1e7ca2ca8c8b",
|
||||
"title": "Step 36"
|
||||
},
|
||||
{
|
||||
"id": "64fad9cd2eeb1e7ca2ca8c8b",
|
||||
"id": "64fadae4f2d51b7d5d8b98d8",
|
||||
"title": "Step 37"
|
||||
},
|
||||
{
|
||||
"id": "64fadae4f2d51b7d5d8b98d8",
|
||||
"id": "64fadff23375f27ff06c6d40",
|
||||
"title": "Step 38"
|
||||
},
|
||||
{
|
||||
"id": "64fadff23375f27ff06c6d40",
|
||||
"id": "64faf0418e828c0114a558a7",
|
||||
"title": "Step 39"
|
||||
},
|
||||
{
|
||||
"id": "64faf0418e828c0114a558a7",
|
||||
"id": "64fae068bcdc9c805bd8399e",
|
||||
"title": "Step 40"
|
||||
},
|
||||
{
|
||||
"id": "64fae068bcdc9c805bd8399e",
|
||||
"id": "64faedcd16a1e985c4c2dc94",
|
||||
"title": "Step 41"
|
||||
},
|
||||
{
|
||||
"id": "64faedcd16a1e985c4c2dc94",
|
||||
"id": "64faf65b22ad8d07df9be14d",
|
||||
"title": "Step 42"
|
||||
},
|
||||
{
|
||||
"id": "64faf65b22ad8d07df9be14d",
|
||||
"id": "64faf874364ec308f875f636",
|
||||
"title": "Step 43"
|
||||
},
|
||||
{
|
||||
"id": "64faf874364ec308f875f636",
|
||||
"id": "64fafac95328110a69bcb75f",
|
||||
"title": "Step 44"
|
||||
},
|
||||
{
|
||||
"id": "64fafac95328110a69bcb75f",
|
||||
"id": "64fb0fa0968f2b113b2d90e9",
|
||||
"title": "Step 45"
|
||||
},
|
||||
{
|
||||
"id": "64fb0fa0968f2b113b2d90e9",
|
||||
"id": "64fb1061ca838611ed6a7d6b",
|
||||
"title": "Step 46"
|
||||
},
|
||||
{
|
||||
"id": "64fb1061ca838611ed6a7d6b",
|
||||
"id": "64fb1321e189a6136d200f77",
|
||||
"title": "Step 47"
|
||||
},
|
||||
{
|
||||
"id": "64fb1321e189a6136d200f77",
|
||||
"id": "64fb1436adef3e145b4c3501",
|
||||
"title": "Step 48"
|
||||
},
|
||||
{
|
||||
"id": "64fb1436adef3e145b4c3501",
|
||||
"id": "64fb14d890415c14f93069ce",
|
||||
"title": "Step 49"
|
||||
},
|
||||
{
|
||||
"id": "64fb14d890415c14f93069ce",
|
||||
"id": "64fb154a7c48cd159924bb18",
|
||||
"title": "Step 50"
|
||||
},
|
||||
{
|
||||
"id": "64fb154a7c48cd159924bb18",
|
||||
"id": "64fb1c4dc0feb219149a7c7d",
|
||||
"title": "Step 51"
|
||||
},
|
||||
{
|
||||
"id": "64fb1c4dc0feb219149a7c7d",
|
||||
"id": "64fb285637fa1e1c222033e3",
|
||||
"title": "Step 52"
|
||||
},
|
||||
{
|
||||
"id": "64fb285637fa1e1c222033e3",
|
||||
"id": "64fb29348a60361ccd45c1e2",
|
||||
"title": "Step 53"
|
||||
},
|
||||
{
|
||||
"id": "64fb29348a60361ccd45c1e2",
|
||||
"id": "64fefebad99209211ec30537",
|
||||
"title": "Step 54"
|
||||
},
|
||||
{
|
||||
"id": "64fefebad99209211ec30537",
|
||||
"id": "64ff0313700dad264d19dfe4",
|
||||
"title": "Step 55"
|
||||
},
|
||||
{
|
||||
"id": "64ff0313700dad264d19dfe4",
|
||||
"id": "64ff04cc33779427a6412449",
|
||||
"title": "Step 56"
|
||||
},
|
||||
{
|
||||
"id": "64ff04cc33779427a6412449",
|
||||
"id": "64ff068e0426eb288874ed79",
|
||||
"title": "Step 57"
|
||||
},
|
||||
{
|
||||
"id": "64ff068e0426eb288874ed79",
|
||||
"id": "64ff23daf176a92de95f24dc",
|
||||
"title": "Step 58"
|
||||
},
|
||||
{
|
||||
"id": "64ff23daf176a92de95f24dc",
|
||||
"id": "64ff24b80431f62ec6b93f65",
|
||||
"title": "Step 59"
|
||||
},
|
||||
{
|
||||
"id": "64ff24b80431f62ec6b93f65",
|
||||
"id": "65003986d17d1e1865b269c0",
|
||||
"title": "Step 60"
|
||||
},
|
||||
{
|
||||
"id": "65003986d17d1e1865b269c0",
|
||||
"id": "650046832f92c01a35834bca",
|
||||
"title": "Step 61"
|
||||
},
|
||||
{
|
||||
"id": "650046832f92c01a35834bca",
|
||||
"id": "650048b0764f9c1b798200e2",
|
||||
"title": "Step 62"
|
||||
},
|
||||
{
|
||||
"id": "650048b0764f9c1b798200e2",
|
||||
"id": "65004ba581d03d1d5628b41c",
|
||||
"title": "Step 63"
|
||||
},
|
||||
{
|
||||
"id": "65004ba581d03d1d5628b41c",
|
||||
"id": "6632420f81f3cc554a5e540b",
|
||||
"title": "Step 64"
|
||||
},
|
||||
{
|
||||
"id": "6632420f81f3cc554a5e540b",
|
||||
"id": "66ad0f178ed5791ed898fe56",
|
||||
"title": "Step 65"
|
||||
},
|
||||
{
|
||||
"id": "66ad0f178ed5791ed898fe56",
|
||||
"id": "671682cd6d7aa95f0078f35f",
|
||||
"title": "Step 66"
|
||||
},
|
||||
{
|
||||
"id": "671682cd6d7aa95f0078f35f",
|
||||
"title": "Step 67"
|
||||
},
|
||||
{
|
||||
"id": "67168a7243b6396cb69c1bdf",
|
||||
"title": "Step 68"
|
||||
"title": "Step 67"
|
||||
}
|
||||
],
|
||||
"helpCategory": "JavaScript"
|
||||
}
|
||||
}
|
||||
@@ -9,14 +9,55 @@ dashedName: step-34
|
||||
|
||||
You should display the `Cancel` and `Discard` buttons to the user only if there is some text present in the input fields.
|
||||
|
||||
To begin, within the `closeTaskFormBtn` event listener, create a `formInputsContainValues` variable to check if there is a value in the `titleInput` field **or** the `dateInput` field **or** the `descriptionInput` field.
|
||||
Start by removing the `confirmCloseDialog.showModal();` line.
|
||||
|
||||
Then inside the `closeTaskFormBtn` event listener, write some logic to check if there is a value in the `titleInput`, `dateInput` **or** the `descriptionInput` field.
|
||||
|
||||
If any of those input fields has a value, then use the `showModal()` method on `confirmCloseDialog`. Otherwise, if there are no changes, call the `reset()` function to clear the input fields and hide the form modal.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should use `const` to create a variable `formInputsContainValues` with the value `titleInput.value || dateInput.value || descriptionInput.value;`
|
||||
If any of the input fields has a value, then the confirmation dialog should display. Otherwise, the `reset` function should be called.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+formInputsContainValues\s*=\s*(titleInput\.value\s*\|\|\s*dateInput\.value\s*\|\|\s*descriptionInput\.value|titleInput\.value\s*!==\s*null\s*\|\|\s*dateInput\.value\s*!==\s*null\s*\|\|\s*descriptionInput\.value\s*!==\s*null)\s*;?/)
|
||||
const titleInput = document.getElementById("title-input");
|
||||
const dateInput = document.getElementById("date-input");
|
||||
const descriptionInput = document.getElementById("description-input");
|
||||
const closeTaskFormBtn = document.getElementById("close-task-form-btn");
|
||||
const cancelBtn = document.getElementById("cancel-btn");
|
||||
const confirmCloseDialog = document.getElementById("confirm-close-dialog");
|
||||
|
||||
const fields = [
|
||||
{ name: "titleInput", element: titleInput, value: "Test title" },
|
||||
{ name: "dateInput", element: dateInput, value: "2025-04-28" },
|
||||
{ name: "descriptionInput", element: descriptionInput, value: "Test description" },
|
||||
];
|
||||
|
||||
closeTaskFormBtn.click();
|
||||
assert.isFalse(confirmCloseDialog.open);
|
||||
|
||||
fields.forEach(({ name, element, value }) => {
|
||||
titleInput.value = "";
|
||||
dateInput.value = "";
|
||||
descriptionInput.value = "";
|
||||
confirmCloseDialog.open = false;
|
||||
|
||||
element.value = value;
|
||||
|
||||
closeTaskFormBtn.click();
|
||||
|
||||
assert.isTrue(confirmCloseDialog.open);
|
||||
|
||||
cancelBtn.click();
|
||||
});
|
||||
|
||||
// Now test for reset functionality
|
||||
|
||||
titleInput.value = "";
|
||||
dateInput.value = "";
|
||||
descriptionInput.value = "";
|
||||
assert.isTrue(taskForm.classList.toggle("hidden"));
|
||||
assert.isEmpty(currentTask);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
@@ -1,363 +0,0 @@
|
||||
---
|
||||
id: 64facf6180824876f70a2e86
|
||||
title: Step 35
|
||||
challengeType: 0
|
||||
dashedName: step-35
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Create an `if` statement to check if `formInputsContainValues` is true. If `formInputsContainValues` is true, indicating that there are changes, use the `showModal()` method on `confirmCloseDialog`. Otherwise, if there are no changes, call the `reset()` function to clear the input fields and hide the form modal.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should create an `if` statement with the condition `formInputsContainValues`.
|
||||
|
||||
```js
|
||||
assert.match(code, /if\s*\(\s*formInputsContainValues\s*\)\s*\{/)
|
||||
```
|
||||
|
||||
The `if` block of your `if` statement should contain `confirmCloseDialog.showModal();`.
|
||||
|
||||
```js
|
||||
assert.match(code, /if\s*\(\s*formInputsContainValues\s*\)\s*\{\s*confirmCloseDialog\.showModal\(\s*\)\s*;?/)
|
||||
```
|
||||
|
||||
Your `if` statement should have an `else` block.
|
||||
|
||||
```js
|
||||
assert.match(code, /if\s*\(\s*formInputsContainValues\s*\)\s*\{\s*confirmCloseDialog\.showModal\(\s*\)\s*;?\s*\}\s*else\s*\{/)
|
||||
```
|
||||
|
||||
You should call the `reset()` function in the `else` block of your `if` statement.
|
||||
|
||||
```js
|
||||
assert.match(code, /if\s*\(\s*formInputsContainValues\s*\)\s*\{\s*confirmCloseDialog\.showModal\(\s*\)\s*;?\s*\}\s*else\s*\{\s*reset\(\s*\)\s*;?\s*\}/)
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Learn localStorage by Building a Todo App</title>
|
||||
<link rel="stylesheet" href="styles.css" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<main>
|
||||
<h1>Todo App</h1>
|
||||
<div class="todo-app">
|
||||
<button id="open-task-form-btn" class="btn large-btn">
|
||||
Add New Task
|
||||
</button>
|
||||
<form class="task-form hidden" id="task-form">
|
||||
<div class="task-form-header">
|
||||
<button id="close-task-form-btn" class="close-task-form-btn" type="button" aria-label="close">
|
||||
<svg class="close-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="48px" height="48px"><path fill="#F44336" d="M21.5 4.5H26.501V43.5H21.5z" transform="rotate(45.001 24 24)" /><path fill="#F44336" d="M21.5 4.5H26.5V43.501H21.5z" transform="rotate(135.008 24 24)" /></svg>
|
||||
</button>
|
||||
</div>
|
||||
<div class="task-form-body">
|
||||
<label class="task-form-label" for="title-input">Title</label>
|
||||
<input required type="text" class="form-control" id="title-input" value="" />
|
||||
<label class="task-form-label" for="date-input">Date</label>
|
||||
<input type="date" class="form-control" id="date-input" value="" />
|
||||
<label class="task-form-label" for="description-input">Description</label>
|
||||
<textarea class="form-control" id="description-input" cols="30" rows="5"></textarea>
|
||||
</div>
|
||||
<div class="task-form-footer">
|
||||
<button id="add-or-update-task-btn" class="btn large-btn" type="submit">
|
||||
Add Task
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
<dialog id="confirm-close-dialog">
|
||||
<form method="dialog">
|
||||
<p class="discard-message-text">Discard unsaved changes?</p>
|
||||
<div class="confirm-close-dialog-btn-container">
|
||||
<button id="cancel-btn" class="btn">
|
||||
Cancel
|
||||
</button>
|
||||
<button id="discard-btn" class="btn">
|
||||
Discard
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</dialog>
|
||||
<div id="tasks-container"></div>
|
||||
</div>
|
||||
</main>
|
||||
<script src="script.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
:root {
|
||||
--white: #fff;
|
||||
--light-grey: #f5f6f7;
|
||||
--dark-grey: #0a0a23;
|
||||
--yellow: #f1be32;
|
||||
--golden-yellow: #feac32;
|
||||
}
|
||||
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: var(--dark-grey);
|
||||
}
|
||||
|
||||
main {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
h1 {
|
||||
color: var(--light-grey);
|
||||
margin: 20px 0 40px 0;
|
||||
}
|
||||
|
||||
.todo-app {
|
||||
background-color: var(--white);
|
||||
width: 300px;
|
||||
height: 350px;
|
||||
border: 5px solid var(--yellow);
|
||||
border-radius: 8px;
|
||||
padding: 15px;
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.btn {
|
||||
cursor: pointer;
|
||||
width: 100px;
|
||||
margin: 10px;
|
||||
color: var(--dark-grey);
|
||||
background-color: var(--golden-yellow);
|
||||
background-image: linear-gradient(#fecc4c, #ffac33);
|
||||
border-color: var(--golden-yellow);
|
||||
border-width: 3px;
|
||||
}
|
||||
|
||||
.btn:hover {
|
||||
background-image: linear-gradient(#ffcc4c, #f89808);
|
||||
}
|
||||
|
||||
.large-btn {
|
||||
width: 80%;
|
||||
font-size: 1.2rem;
|
||||
align-self: center;
|
||||
justify-self: center;
|
||||
}
|
||||
|
||||
.close-task-form-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.close-icon {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.task-form {
|
||||
display: flex;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
background-color: var(--white);
|
||||
border-radius: 5px;
|
||||
padding: 15px;
|
||||
width: 300px;
|
||||
height: 350px;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.task-form-header {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.task-form-body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
||||
.task-form-footer {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.task-form-label,
|
||||
#title-input,
|
||||
#date-input,
|
||||
#description-input {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.task-form-label {
|
||||
margin-bottom: 5px;
|
||||
font-size: 1.3rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#title-input,
|
||||
#date-input,
|
||||
#description-input {
|
||||
width: 100%;
|
||||
margin-bottom: 10px;
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
#confirm-close-dialog {
|
||||
padding: 10px;
|
||||
margin: 10px auto;
|
||||
border-radius: 15px;
|
||||
}
|
||||
|
||||
.confirm-close-dialog-btn-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.discard-message-text {
|
||||
font-weight: bold;
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
#tasks-container {
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.task {
|
||||
margin: 5px 0;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media (min-width: 576px) {
|
||||
.todo-app,
|
||||
.task-form {
|
||||
width: 400px;
|
||||
height: 450px;
|
||||
}
|
||||
|
||||
.task-form-label {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
#title-input,
|
||||
#date-input {
|
||||
height: 2rem;
|
||||
}
|
||||
|
||||
#title-input,
|
||||
#date-input,
|
||||
#description-input {
|
||||
padding: 5px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
const taskForm = document.getElementById("task-form");
|
||||
const confirmCloseDialog = document.getElementById("confirm-close-dialog");
|
||||
const openTaskFormBtn = document.getElementById("open-task-form-btn");
|
||||
const closeTaskFormBtn = document.getElementById("close-task-form-btn");
|
||||
const addOrUpdateTaskBtn = document.getElementById("add-or-update-task-btn");
|
||||
const cancelBtn = document.getElementById("cancel-btn");
|
||||
const discardBtn = document.getElementById("discard-btn");
|
||||
const tasksContainer = document.getElementById("tasks-container");
|
||||
const titleInput = document.getElementById("title-input");
|
||||
const dateInput = document.getElementById("date-input");
|
||||
const descriptionInput = document.getElementById("description-input");
|
||||
|
||||
const taskData = [];
|
||||
let currentTask = {};
|
||||
|
||||
const reset = () => {
|
||||
titleInput.value = "";
|
||||
dateInput.value = "";
|
||||
descriptionInput.value = "";
|
||||
taskForm.classList.toggle("hidden");
|
||||
currentTask = {};
|
||||
}
|
||||
|
||||
openTaskFormBtn.addEventListener("click", () =>
|
||||
taskForm.classList.toggle("hidden")
|
||||
);
|
||||
|
||||
closeTaskFormBtn.addEventListener("click", () => {
|
||||
const formInputsContainValues = titleInput.value || dateInput.value || descriptionInput.value;
|
||||
--fcc-editable-region--
|
||||
confirmCloseDialog.showModal();
|
||||
--fcc-editable-region--
|
||||
});
|
||||
|
||||
cancelBtn.addEventListener("click", () => confirmCloseDialog.close());
|
||||
|
||||
discardBtn.addEventListener("click", () => {
|
||||
confirmCloseDialog.close();
|
||||
reset()
|
||||
});
|
||||
|
||||
taskForm.addEventListener("submit", (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
const dataArrIndex = taskData.findIndex((item) => item.id === currentTask.id);
|
||||
const taskObj = {
|
||||
id: `${titleInput.value.toLowerCase().split(" ").join("-")}-${Date.now()}`,
|
||||
title: titleInput.value,
|
||||
date: dateInput.value,
|
||||
description: descriptionInput.value,
|
||||
};
|
||||
|
||||
if (dataArrIndex === -1) {
|
||||
taskData.unshift(taskObj);
|
||||
}
|
||||
|
||||
taskData.forEach(
|
||||
({ id, title, date, description }) => {
|
||||
tasksContainer.innerHTML += `
|
||||
<div class="task" id="${id}">
|
||||
<p><strong>Title:</strong> ${title}</p>
|
||||
<p><strong>Date:</strong> ${date}</p>
|
||||
<p><strong>Description:</strong> ${description}</p>
|
||||
<button type="button" class="btn">Edit</button>
|
||||
<button type="button" class="btn">Delete</button>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
);
|
||||
|
||||
reset()
|
||||
});
|
||||
```
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 64fad07f43a101779cb8692a
|
||||
title: Step 36
|
||||
title: Step 35
|
||||
challengeType: 0
|
||||
dashedName: step-36
|
||||
dashedName: step-35
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 64fad9cd2eeb1e7ca2ca8c8b
|
||||
title: Step 37
|
||||
title: Step 36
|
||||
challengeType: 0
|
||||
dashedName: step-37
|
||||
dashedName: step-36
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 64fadae4f2d51b7d5d8b98d8
|
||||
title: Step 38
|
||||
title: Step 37
|
||||
challengeType: 0
|
||||
dashedName: step-38
|
||||
dashedName: step-37
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 64fadff23375f27ff06c6d40
|
||||
title: Step 39
|
||||
title: Step 38
|
||||
challengeType: 0
|
||||
dashedName: step-39
|
||||
dashedName: step-38
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 64fae068bcdc9c805bd8399e
|
||||
title: Step 41
|
||||
title: Step 40
|
||||
challengeType: 0
|
||||
dashedName: step-41
|
||||
dashedName: step-40
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 64faedcd16a1e985c4c2dc94
|
||||
title: Step 42
|
||||
title: Step 41
|
||||
challengeType: 0
|
||||
dashedName: step-42
|
||||
dashedName: step-41
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 64faf0418e828c0114a558a7
|
||||
title: Step 40
|
||||
title: Step 39
|
||||
challengeType: 0
|
||||
dashedName: step-40
|
||||
dashedName: step-39
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 64faf65b22ad8d07df9be14d
|
||||
title: Step 43
|
||||
title: Step 42
|
||||
challengeType: 0
|
||||
dashedName: step-43
|
||||
dashedName: step-42
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 64faf874364ec308f875f636
|
||||
title: Step 44
|
||||
title: Step 43
|
||||
challengeType: 0
|
||||
dashedName: step-44
|
||||
dashedName: step-43
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 64fafac95328110a69bcb75f
|
||||
title: Step 45
|
||||
title: Step 44
|
||||
challengeType: 0
|
||||
dashedName: step-45
|
||||
dashedName: step-44
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 64fb0fa0968f2b113b2d90e9
|
||||
title: Step 46
|
||||
title: Step 45
|
||||
challengeType: 0
|
||||
dashedName: step-46
|
||||
dashedName: step-45
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 64fb1061ca838611ed6a7d6b
|
||||
title: Step 47
|
||||
title: Step 46
|
||||
challengeType: 0
|
||||
dashedName: step-47
|
||||
dashedName: step-46
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 64fb1321e189a6136d200f77
|
||||
title: Step 48
|
||||
title: Step 47
|
||||
challengeType: 0
|
||||
dashedName: step-48
|
||||
dashedName: step-47
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 64fb1436adef3e145b4c3501
|
||||
title: Step 49
|
||||
title: Step 48
|
||||
challengeType: 0
|
||||
dashedName: step-49
|
||||
dashedName: step-48
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 64fb14d890415c14f93069ce
|
||||
title: Step 50
|
||||
title: Step 49
|
||||
challengeType: 0
|
||||
dashedName: step-50
|
||||
dashedName: step-49
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 64fb154a7c48cd159924bb18
|
||||
title: Step 51
|
||||
title: Step 50
|
||||
challengeType: 0
|
||||
dashedName: step-51
|
||||
dashedName: step-50
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 64fb1c4dc0feb219149a7c7d
|
||||
title: Step 52
|
||||
title: Step 51
|
||||
challengeType: 0
|
||||
dashedName: step-52
|
||||
dashedName: step-51
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 64fb285637fa1e1c222033e3
|
||||
title: Step 53
|
||||
title: Step 52
|
||||
challengeType: 0
|
||||
dashedName: step-53
|
||||
dashedName: step-52
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 64fb29348a60361ccd45c1e2
|
||||
title: Step 54
|
||||
title: Step 53
|
||||
challengeType: 0
|
||||
dashedName: step-54
|
||||
dashedName: step-53
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 64fefebad99209211ec30537
|
||||
title: Step 55
|
||||
title: Step 54
|
||||
challengeType: 0
|
||||
dashedName: step-55
|
||||
dashedName: step-54
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 64ff0313700dad264d19dfe4
|
||||
title: Step 56
|
||||
title: Step 55
|
||||
challengeType: 0
|
||||
dashedName: step-56
|
||||
dashedName: step-55
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 64ff04cc33779427a6412449
|
||||
title: Step 57
|
||||
title: Step 56
|
||||
challengeType: 0
|
||||
dashedName: step-57
|
||||
dashedName: step-56
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 64ff068e0426eb288874ed79
|
||||
title: Step 58
|
||||
title: Step 57
|
||||
challengeType: 0
|
||||
dashedName: step-58
|
||||
dashedName: step-57
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 64ff23daf176a92de95f24dc
|
||||
title: Step 59
|
||||
title: Step 58
|
||||
challengeType: 0
|
||||
dashedName: step-59
|
||||
dashedName: step-58
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 64ff24b80431f62ec6b93f65
|
||||
title: Step 60
|
||||
title: Step 59
|
||||
challengeType: 0
|
||||
dashedName: step-60
|
||||
dashedName: step-59
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 65003986d17d1e1865b269c0
|
||||
title: Step 61
|
||||
title: Step 60
|
||||
challengeType: 0
|
||||
dashedName: step-61
|
||||
dashedName: step-60
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 650046832f92c01a35834bca
|
||||
title: Step 62
|
||||
title: Step 61
|
||||
challengeType: 0
|
||||
dashedName: step-62
|
||||
dashedName: step-61
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 650048b0764f9c1b798200e2
|
||||
title: Step 63
|
||||
title: Step 62
|
||||
challengeType: 0
|
||||
dashedName: step-63
|
||||
dashedName: step-62
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 65004ba581d03d1d5628b41c
|
||||
title: Step 64
|
||||
title: Step 63
|
||||
challengeType: 0
|
||||
dashedName: step-64
|
||||
dashedName: step-63
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 6632420f81f3cc554a5e540b
|
||||
title: Step 65
|
||||
title: Step 64
|
||||
challengeType: 0
|
||||
dashedName: step-65
|
||||
dashedName: step-64
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 66ad0f178ed5791ed898fe56
|
||||
title: Step 66
|
||||
title: Step 65
|
||||
challengeType: 0
|
||||
dashedName: step-66
|
||||
dashedName: step-65
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 671682cd6d7aa95f0078f35f
|
||||
title: Step 67
|
||||
title: Step 66
|
||||
challengeType: 0
|
||||
dashedName: step-67
|
||||
dashedName: step-66
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 67168a7243b6396cb69c1bdf
|
||||
title: Step 68
|
||||
title: Step 67
|
||||
challengeType: 0
|
||||
dashedName: step-68
|
||||
dashedName: step-67
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -9,14 +9,55 @@ dashedName: step-34
|
||||
|
||||
You should display the `Cancel` and `Discard` buttons to the user only if there is some text present in the input fields.
|
||||
|
||||
To begin, within the `closeTaskFormBtn` event listener, create a `formInputsContainValues` variable to check if there is a value in the `titleInput` field **or** the `dateInput` field **or** the `descriptionInput` field.
|
||||
Start by removing the `confirmCloseDialog.showModal();` line.
|
||||
|
||||
Then inside the `closeTaskFormBtn` event listener, write some logic to check if there is a value in the `titleInput`, `dateInput` **or** the `descriptionInput` field.
|
||||
|
||||
If any of those input fields has a value, then use the `showModal()` method on `confirmCloseDialog`. Otherwise, if there are no changes, call the `reset()` function to clear the input fields and hide the form modal.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should use `const` to create a variable `formInputsContainValues` with the value `titleInput.value || dateInput.value || descriptionInput.value;`
|
||||
If any of the input fields has a value, then the confirmation dialog should display. Otherwise, the `reset` function should be called.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+formInputsContainValues\s*=\s*(titleInput\.value\s*\|\|\s*dateInput\.value\s*\|\|\s*descriptionInput\.value|titleInput\.value\s*!==\s*null\s*\|\|\s*dateInput\.value\s*!==\s*null\s*\|\|\s*descriptionInput\.value\s*!==\s*null)\s*;?/)
|
||||
const titleInput = document.getElementById("title-input");
|
||||
const dateInput = document.getElementById("date-input");
|
||||
const descriptionInput = document.getElementById("description-input");
|
||||
const closeTaskFormBtn = document.getElementById("close-task-form-btn");
|
||||
const cancelBtn = document.getElementById("cancel-btn");
|
||||
const confirmCloseDialog = document.getElementById("confirm-close-dialog");
|
||||
|
||||
const fields = [
|
||||
{ name: "titleInput", element: titleInput, value: "Test title" },
|
||||
{ name: "dateInput", element: dateInput, value: "2025-04-28" },
|
||||
{ name: "descriptionInput", element: descriptionInput, value: "Test description" },
|
||||
];
|
||||
|
||||
closeTaskFormBtn.click();
|
||||
assert.isFalse(confirmCloseDialog.open);
|
||||
|
||||
fields.forEach(({ name, element, value }) => {
|
||||
titleInput.value = "";
|
||||
dateInput.value = "";
|
||||
descriptionInput.value = "";
|
||||
confirmCloseDialog.open = false;
|
||||
|
||||
element.value = value;
|
||||
|
||||
closeTaskFormBtn.click();
|
||||
|
||||
assert.isTrue(confirmCloseDialog.open);
|
||||
|
||||
cancelBtn.click();
|
||||
});
|
||||
|
||||
// Now test for reset functionality
|
||||
|
||||
titleInput.value = "";
|
||||
dateInput.value = "";
|
||||
descriptionInput.value = "";
|
||||
assert.isTrue(taskForm.classList.toggle("hidden"));
|
||||
assert.isEmpty(currentTask);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
@@ -1,363 +0,0 @@
|
||||
---
|
||||
id: 64facf6180824876f70a2e86
|
||||
title: Step 35
|
||||
challengeType: 0
|
||||
dashedName: step-35
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Create an `if` statement to check if `formInputsContainValues` is true. If `formInputsContainValues` is true, indicating that there are changes, use the `showModal()` method on `confirmCloseDialog`. Otherwise, if there are no changes, call the `reset()` function to clear the input fields and hide the form modal.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should create an `if` statement with the condition `formInputsContainValues`.
|
||||
|
||||
```js
|
||||
assert.match(code, /if\s*\(\s*formInputsContainValues\s*\)\s*\{/)
|
||||
```
|
||||
|
||||
The `if` block of your `if` statement should contain `confirmCloseDialog.showModal();`.
|
||||
|
||||
```js
|
||||
assert.match(code, /if\s*\(\s*formInputsContainValues\s*\)\s*\{\s*confirmCloseDialog\.showModal\(\s*\)\s*;?/)
|
||||
```
|
||||
|
||||
Your `if` statement should have an `else` block.
|
||||
|
||||
```js
|
||||
assert.match(code, /if\s*\(\s*formInputsContainValues\s*\)\s*\{\s*confirmCloseDialog\.showModal\(\s*\)\s*;?\s*\}\s*else\s*\{/)
|
||||
```
|
||||
|
||||
You should call the `reset()` function in the `else` block of your `if` statement.
|
||||
|
||||
```js
|
||||
assert.match(code, /if\s*\(\s*formInputsContainValues\s*\)\s*\{\s*confirmCloseDialog\.showModal\(\s*\)\s*;?\s*\}\s*else\s*\{\s*reset\(\s*\)\s*;?\s*\}/)
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Learn localStorage by Building a Todo App</title>
|
||||
<link rel="stylesheet" href="styles.css" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<main>
|
||||
<h1>Todo App</h1>
|
||||
<div class="todo-app">
|
||||
<button id="open-task-form-btn" class="btn large-btn">
|
||||
Add New Task
|
||||
</button>
|
||||
<form class="task-form hidden" id="task-form">
|
||||
<div class="task-form-header">
|
||||
<button id="close-task-form-btn" class="close-task-form-btn" type="button" aria-label="close">
|
||||
<svg class="close-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="48px" height="48px"><path fill="#F44336" d="M21.5 4.5H26.501V43.5H21.5z" transform="rotate(45.001 24 24)" /><path fill="#F44336" d="M21.5 4.5H26.5V43.501H21.5z" transform="rotate(135.008 24 24)" /></svg>
|
||||
</button>
|
||||
</div>
|
||||
<div class="task-form-body">
|
||||
<label class="task-form-label" for="title-input">Title</label>
|
||||
<input required type="text" class="form-control" id="title-input" value="" />
|
||||
<label class="task-form-label" for="date-input">Date</label>
|
||||
<input type="date" class="form-control" id="date-input" value="" />
|
||||
<label class="task-form-label" for="description-input">Description</label>
|
||||
<textarea class="form-control" id="description-input" cols="30" rows="5"></textarea>
|
||||
</div>
|
||||
<div class="task-form-footer">
|
||||
<button id="add-or-update-task-btn" class="btn large-btn" type="submit">
|
||||
Add Task
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
<dialog id="confirm-close-dialog">
|
||||
<form method="dialog">
|
||||
<p class="discard-message-text">Discard unsaved changes?</p>
|
||||
<div class="confirm-close-dialog-btn-container">
|
||||
<button id="cancel-btn" class="btn">
|
||||
Cancel
|
||||
</button>
|
||||
<button id="discard-btn" class="btn">
|
||||
Discard
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</dialog>
|
||||
<div id="tasks-container"></div>
|
||||
</div>
|
||||
</main>
|
||||
<script src="script.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
:root {
|
||||
--white: #fff;
|
||||
--light-grey: #f5f6f7;
|
||||
--dark-grey: #0a0a23;
|
||||
--yellow: #f1be32;
|
||||
--golden-yellow: #feac32;
|
||||
}
|
||||
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: var(--dark-grey);
|
||||
}
|
||||
|
||||
main {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
h1 {
|
||||
color: var(--light-grey);
|
||||
margin: 20px 0 40px 0;
|
||||
}
|
||||
|
||||
.todo-app {
|
||||
background-color: var(--white);
|
||||
width: 300px;
|
||||
height: 350px;
|
||||
border: 5px solid var(--yellow);
|
||||
border-radius: 8px;
|
||||
padding: 15px;
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.btn {
|
||||
cursor: pointer;
|
||||
width: 100px;
|
||||
margin: 10px;
|
||||
color: var(--dark-grey);
|
||||
background-color: var(--golden-yellow);
|
||||
background-image: linear-gradient(#fecc4c, #ffac33);
|
||||
border-color: var(--golden-yellow);
|
||||
border-width: 3px;
|
||||
}
|
||||
|
||||
.btn:hover {
|
||||
background-image: linear-gradient(#ffcc4c, #f89808);
|
||||
}
|
||||
|
||||
.large-btn {
|
||||
width: 80%;
|
||||
font-size: 1.2rem;
|
||||
align-self: center;
|
||||
justify-self: center;
|
||||
}
|
||||
|
||||
.close-task-form-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.close-icon {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.task-form {
|
||||
display: flex;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
background-color: var(--white);
|
||||
border-radius: 5px;
|
||||
padding: 15px;
|
||||
width: 300px;
|
||||
height: 350px;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.task-form-header {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.task-form-body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
||||
.task-form-footer {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.task-form-label,
|
||||
#title-input,
|
||||
#date-input,
|
||||
#description-input {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.task-form-label {
|
||||
margin-bottom: 5px;
|
||||
font-size: 1.3rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#title-input,
|
||||
#date-input,
|
||||
#description-input {
|
||||
width: 100%;
|
||||
margin-bottom: 10px;
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
#confirm-close-dialog {
|
||||
padding: 10px;
|
||||
margin: 10px auto;
|
||||
border-radius: 15px;
|
||||
}
|
||||
|
||||
.confirm-close-dialog-btn-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.discard-message-text {
|
||||
font-weight: bold;
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
#tasks-container {
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.task {
|
||||
margin: 5px 0;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media (min-width: 576px) {
|
||||
.todo-app,
|
||||
.task-form {
|
||||
width: 400px;
|
||||
height: 450px;
|
||||
}
|
||||
|
||||
.task-form-label {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
#title-input,
|
||||
#date-input {
|
||||
height: 2rem;
|
||||
}
|
||||
|
||||
#title-input,
|
||||
#date-input,
|
||||
#description-input {
|
||||
padding: 5px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
const taskForm = document.getElementById("task-form");
|
||||
const confirmCloseDialog = document.getElementById("confirm-close-dialog");
|
||||
const openTaskFormBtn = document.getElementById("open-task-form-btn");
|
||||
const closeTaskFormBtn = document.getElementById("close-task-form-btn");
|
||||
const addOrUpdateTaskBtn = document.getElementById("add-or-update-task-btn");
|
||||
const cancelBtn = document.getElementById("cancel-btn");
|
||||
const discardBtn = document.getElementById("discard-btn");
|
||||
const tasksContainer = document.getElementById("tasks-container");
|
||||
const titleInput = document.getElementById("title-input");
|
||||
const dateInput = document.getElementById("date-input");
|
||||
const descriptionInput = document.getElementById("description-input");
|
||||
|
||||
const taskData = [];
|
||||
let currentTask = {};
|
||||
|
||||
const reset = () => {
|
||||
titleInput.value = "";
|
||||
dateInput.value = "";
|
||||
descriptionInput.value = "";
|
||||
taskForm.classList.toggle("hidden");
|
||||
currentTask = {};
|
||||
}
|
||||
|
||||
openTaskFormBtn.addEventListener("click", () =>
|
||||
taskForm.classList.toggle("hidden")
|
||||
);
|
||||
|
||||
closeTaskFormBtn.addEventListener("click", () => {
|
||||
const formInputsContainValues = titleInput.value || dateInput.value || descriptionInput.value;
|
||||
--fcc-editable-region--
|
||||
confirmCloseDialog.showModal();
|
||||
--fcc-editable-region--
|
||||
});
|
||||
|
||||
cancelBtn.addEventListener("click", () => confirmCloseDialog.close());
|
||||
|
||||
discardBtn.addEventListener("click", () => {
|
||||
confirmCloseDialog.close();
|
||||
reset()
|
||||
});
|
||||
|
||||
taskForm.addEventListener("submit", (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
const dataArrIndex = taskData.findIndex((item) => item.id === currentTask.id);
|
||||
const taskObj = {
|
||||
id: `${titleInput.value.toLowerCase().split(" ").join("-")}-${Date.now()}`,
|
||||
title: titleInput.value,
|
||||
date: dateInput.value,
|
||||
description: descriptionInput.value,
|
||||
};
|
||||
|
||||
if (dataArrIndex === -1) {
|
||||
taskData.unshift(taskObj);
|
||||
}
|
||||
|
||||
taskData.forEach(
|
||||
({ id, title, date, description }) => {
|
||||
tasksContainer.innerHTML += `
|
||||
<div class="task" id="${id}">
|
||||
<p><strong>Title:</strong> ${title}</p>
|
||||
<p><strong>Date:</strong> ${date}</p>
|
||||
<p><strong>Description:</strong> ${description}</p>
|
||||
<button type="button" class="btn">Edit</button>
|
||||
<button type="button" class="btn">Delete</button>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
);
|
||||
|
||||
reset()
|
||||
});
|
||||
```
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 64fad07f43a101779cb8692a
|
||||
title: Step 36
|
||||
title: Step 35
|
||||
challengeType: 0
|
||||
dashedName: step-36
|
||||
dashedName: step-35
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 64fad9cd2eeb1e7ca2ca8c8b
|
||||
title: Step 37
|
||||
title: Step 36
|
||||
challengeType: 0
|
||||
dashedName: step-37
|
||||
dashedName: step-36
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 64fadae4f2d51b7d5d8b98d8
|
||||
title: Step 38
|
||||
title: Step 37
|
||||
challengeType: 0
|
||||
dashedName: step-38
|
||||
dashedName: step-37
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 64fadff23375f27ff06c6d40
|
||||
title: Step 39
|
||||
title: Step 38
|
||||
challengeType: 0
|
||||
dashedName: step-39
|
||||
dashedName: step-38
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 64fae068bcdc9c805bd8399e
|
||||
title: Step 41
|
||||
title: Step 40
|
||||
challengeType: 0
|
||||
dashedName: step-41
|
||||
dashedName: step-40
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 64faedcd16a1e985c4c2dc94
|
||||
title: Step 42
|
||||
title: Step 41
|
||||
challengeType: 0
|
||||
dashedName: step-42
|
||||
dashedName: step-41
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 64faf0418e828c0114a558a7
|
||||
title: Step 40
|
||||
title: Step 39
|
||||
challengeType: 0
|
||||
dashedName: step-40
|
||||
dashedName: step-39
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 64faf65b22ad8d07df9be14d
|
||||
title: Step 43
|
||||
title: Step 42
|
||||
challengeType: 0
|
||||
dashedName: step-43
|
||||
dashedName: step-42
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 64faf874364ec308f875f636
|
||||
title: Step 44
|
||||
title: Step 43
|
||||
challengeType: 0
|
||||
dashedName: step-44
|
||||
dashedName: step-43
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 64fafac95328110a69bcb75f
|
||||
title: Step 45
|
||||
title: Step 44
|
||||
challengeType: 0
|
||||
dashedName: step-45
|
||||
dashedName: step-44
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 64fb0fa0968f2b113b2d90e9
|
||||
title: Step 46
|
||||
title: Step 45
|
||||
challengeType: 0
|
||||
dashedName: step-46
|
||||
dashedName: step-45
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 64fb1061ca838611ed6a7d6b
|
||||
title: Step 47
|
||||
title: Step 46
|
||||
challengeType: 0
|
||||
dashedName: step-47
|
||||
dashedName: step-46
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 64fb1321e189a6136d200f77
|
||||
title: Step 48
|
||||
title: Step 47
|
||||
challengeType: 0
|
||||
dashedName: step-48
|
||||
dashedName: step-47
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 64fb1436adef3e145b4c3501
|
||||
title: Step 49
|
||||
title: Step 48
|
||||
challengeType: 0
|
||||
dashedName: step-49
|
||||
dashedName: step-48
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 64fb14d890415c14f93069ce
|
||||
title: Step 50
|
||||
title: Step 49
|
||||
challengeType: 0
|
||||
dashedName: step-50
|
||||
dashedName: step-49
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 64fb154a7c48cd159924bb18
|
||||
title: Step 51
|
||||
title: Step 50
|
||||
challengeType: 0
|
||||
dashedName: step-51
|
||||
dashedName: step-50
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 64fb1c4dc0feb219149a7c7d
|
||||
title: Step 52
|
||||
title: Step 51
|
||||
challengeType: 0
|
||||
dashedName: step-52
|
||||
dashedName: step-51
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 64fb285637fa1e1c222033e3
|
||||
title: Step 53
|
||||
title: Step 52
|
||||
challengeType: 0
|
||||
dashedName: step-53
|
||||
dashedName: step-52
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 64fb29348a60361ccd45c1e2
|
||||
title: Step 54
|
||||
title: Step 53
|
||||
challengeType: 0
|
||||
dashedName: step-54
|
||||
dashedName: step-53
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 64fefebad99209211ec30537
|
||||
title: Step 55
|
||||
title: Step 54
|
||||
challengeType: 0
|
||||
dashedName: step-55
|
||||
dashedName: step-54
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 64ff0313700dad264d19dfe4
|
||||
title: Step 56
|
||||
title: Step 55
|
||||
challengeType: 0
|
||||
dashedName: step-56
|
||||
dashedName: step-55
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 64ff04cc33779427a6412449
|
||||
title: Step 57
|
||||
title: Step 56
|
||||
challengeType: 0
|
||||
dashedName: step-57
|
||||
dashedName: step-56
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 64ff068e0426eb288874ed79
|
||||
title: Step 58
|
||||
title: Step 57
|
||||
challengeType: 0
|
||||
dashedName: step-58
|
||||
dashedName: step-57
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 64ff23daf176a92de95f24dc
|
||||
title: Step 59
|
||||
title: Step 58
|
||||
challengeType: 0
|
||||
dashedName: step-59
|
||||
dashedName: step-58
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 64ff24b80431f62ec6b93f65
|
||||
title: Step 60
|
||||
title: Step 59
|
||||
challengeType: 0
|
||||
dashedName: step-60
|
||||
dashedName: step-59
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 65003986d17d1e1865b269c0
|
||||
title: Step 61
|
||||
title: Step 60
|
||||
challengeType: 0
|
||||
dashedName: step-61
|
||||
dashedName: step-60
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 650046832f92c01a35834bca
|
||||
title: Step 62
|
||||
title: Step 61
|
||||
challengeType: 0
|
||||
dashedName: step-62
|
||||
dashedName: step-61
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 650048b0764f9c1b798200e2
|
||||
title: Step 63
|
||||
title: Step 62
|
||||
challengeType: 0
|
||||
dashedName: step-63
|
||||
dashedName: step-62
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 65004ba581d03d1d5628b41c
|
||||
title: Step 64
|
||||
title: Step 63
|
||||
challengeType: 0
|
||||
dashedName: step-64
|
||||
dashedName: step-63
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 6632420f81f3cc554a5e540b
|
||||
title: Step 65
|
||||
title: Step 64
|
||||
challengeType: 0
|
||||
dashedName: step-65
|
||||
dashedName: step-64
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 66ad0f178ed5791ed898fe56
|
||||
title: Step 66
|
||||
title: Step 65
|
||||
challengeType: 0
|
||||
dashedName: step-66
|
||||
dashedName: step-65
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 671682cd6d7aa95f0078f35f
|
||||
title: Step 67
|
||||
title: Step 66
|
||||
challengeType: 0
|
||||
dashedName: step-67
|
||||
dashedName: step-66
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 67168a7243b6396cb69c1bdf
|
||||
title: Step 68
|
||||
title: Step 67
|
||||
challengeType: 0
|
||||
dashedName: step-68
|
||||
dashedName: step-67
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Reference in New Issue
Block a user