mirror of
https://github.com/freeCodeCamp/freeCodeCamp.git
synced 2026-02-28 14:01:32 -05:00
chore(i18n,learn): processed translations (#54638)
This commit is contained in:
committed by
GitHub
parent
7214c2eb0c
commit
c53aab1f11
@@ -39,7 +39,13 @@ Your `idToText` function should have an `id` parameter.
|
||||
assert.match(code, /const\s+evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*(\(\s*id\s*\)|id)\s*=>/);
|
||||
```
|
||||
|
||||
You should assign `idToText` the result of calling the `.find()` method on your `cells` array.
|
||||
Your `idToText` function should return the result of calling the `.find()` method on your `cells` array. Your callback function should use an implicit return.
|
||||
|
||||
```js
|
||||
assert.notMatch(code, /const\s+evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*(\(\s*id\s*\)|id)\s*=>\s*cells\.find\(\s*(\(\s*cell\s*\)|cell)\s*=>\s*\{/);
|
||||
```
|
||||
|
||||
Your `idToText` function should use an implicit return.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*(\(\s*id\s*\)|id)\s*=>\s*cells\.find\(/);
|
||||
@@ -57,12 +63,6 @@ Your callback function should have a `cell` parameter.
|
||||
assert.match(code, /const\s+evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*(\(\s*id\s*\)|id)\s*=>\s*cells\.find\(\s*(\(\s*cell\s*\)|cell)\s*=>/);
|
||||
```
|
||||
|
||||
Your callback function should use an implicit return.
|
||||
|
||||
```js
|
||||
assert.notMatch(code, /const\s+evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*(\(\s*id\s*\)|id)\s*=>\s*cells\.find\(\s*(\(\s*cell\s*\)|cell)\s*=>\s*\{/);
|
||||
```
|
||||
|
||||
Your callback function should return whether `cell.id` is strictly equal to `id`.
|
||||
|
||||
```js
|
||||
|
||||
@@ -11,72 +11,48 @@ In your `highPrecedence` function, declare a `regex` variable. Assign it a regul
|
||||
|
||||
Each number, and the operator, should be in separate capture groups.
|
||||
|
||||
Incorporate the regular expression you've defined into your `highPrecedence` function to test if the provided string `str` matches the pattern. Use the `test()` method on your `regex` variable and return the result.
|
||||
|
||||
# --hints--
|
||||
|
||||
|
||||
You should declare a `regex` variable in your `highPrecedence` function.
|
||||
|
||||
```js
|
||||
|
||||
assert.match(code, /const\s+highPrecedence\s*=\s*(\(\s*str\s*\)|str)\s*=>\s*{\s*(?:const|let|var)\s+regex/);
|
||||
|
||||
```
|
||||
|
||||
You should use `const` to declare your `regex` variable.
|
||||
|
||||
```js
|
||||
|
||||
assert.match(code, /const\s+highPrecedence\s*=\s*(\(\s*str\s*\)|str)\s*=>\s*{\s*const\s+regex/);
|
||||
|
||||
```
|
||||
|
||||
Your `regex` variable should be a regular expression.
|
||||
|
||||
```js
|
||||
|
||||
assert.match(code, /const\s+highPrecedence\s*=\s*(\(\s*str\s*\)|str)\s*=>\s*{\s*const\s+regex\s*=\s*\//);
|
||||
|
||||
```
|
||||
|
||||
Your `regex` should use a capture group.
|
||||
Your highPrecedence should return `regex.test(str);`
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+highPrecedence\s*=\s*(\(\s*str\s*\)|str)\s*=>\s*{\s*const\s+regex\s*=\s*\/\(/);
|
||||
assert.match(code, /return\s+regex\.test\(str\);?/);
|
||||
|
||||
```
|
||||
|
||||
Your first capture group should use a character class.
|
||||
Please enter a properly functioning regex.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+highPrecedence\s*=\s*(\(\s*str\s*\)|str)\s*=>\s*{\s*const\s+regex\s*=\s*\/\(\[/);
|
||||
```
|
||||
|
||||
Your first capture group should match any digit or a period. Use the special `\d` character class.
|
||||
assert.strictEqual(highPrecedence("5*3"), true);
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+highPrecedence\s*=\s*(\(\s*str\s*\)|str)\s*=>\s*{\s*const\s+regex\s*=\s*\/\(\[(?:\\d\.|\.\\d)\]/);
|
||||
```
|
||||
|
||||
Your first capture group should match the character class one or more times.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+highPrecedence\s*=\s*(\(\s*str\s*\)|str)\s*=>\s*{\s*const\s+regex\s*=\s*\/\(\[(?:\\d\.|\.\\d)\]\+\)/);
|
||||
```
|
||||
|
||||
Your `regex` should use a second capture group.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+highPrecedence\s*=\s*(\(\s*str\s*\)|str)\s*=>\s*{\s*const\s+regex\s*=\s*\/\(\[(?:\\d\.|\.\\d)\]\+\)\(/);
|
||||
```
|
||||
|
||||
Your second capture group should match a `*` or `/` operator. Use a character class in the capture group.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+highPrecedence\s*=\s*(\(\s*str\s*\)|str)\s*=>\s*{\s*const\s+regex\s*=\s*\/\(\[(?:\\d\.|\.\\d)\]\+\)\(\[(?:\*\\\/|\\\/*)\]\)/);
|
||||
```
|
||||
|
||||
Your `regex` should use a third capture group.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+highPrecedence\s*=\s*(\(\s*str\s*\)|str)\s*=>\s*{\s*const\s+regex\s*=\s*\/\(\[(?:\\d\.|\.\\d)\]\+\)\(\[(?:\*\\\/|\\\/*)\]\)\(/);
|
||||
```
|
||||
|
||||
Your third capture group should be the same as your first capture group.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+highPrecedence\s*=\s*(\(\s*str\s*\)|str)\s*=>\s*{\s*const\s+regex\s*=\s*\/\(\[(?:\\d\.|\.\\d)\]\+\)\(\[(?:\*\\\/|\\\/*)\]\)\(\[(?:\\d\.|\.\\d)\]\+\)/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 646d40c543943ec250039682
|
||||
title: Step 75
|
||||
title: Step 77
|
||||
challengeType: 0
|
||||
dashedName: step-75
|
||||
dashedName: step-77
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 646d40fe4b7b50c30c2b4cd8
|
||||
title: Step 76
|
||||
title: Step 78
|
||||
challengeType: 0
|
||||
dashedName: step-76
|
||||
dashedName: step-78
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 646d41e23b583fc3b8cc4579
|
||||
title: Step 77
|
||||
title: Step 79
|
||||
challengeType: 0
|
||||
dashedName: step-77
|
||||
dashedName: step-79
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 646d423fade4a9c4636acd13
|
||||
title: Step 78
|
||||
title: Step 80
|
||||
challengeType: 0
|
||||
dashedName: step-78
|
||||
dashedName: step-80
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 646d42f58deb2fc52adc6611
|
||||
title: Step 79
|
||||
title: Step 81
|
||||
challengeType: 0
|
||||
dashedName: step-79
|
||||
dashedName: step-81
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 646d43587d926bc5b6cb2e50
|
||||
title: Step 80
|
||||
title: Step 82
|
||||
challengeType: 0
|
||||
dashedName: step-80
|
||||
dashedName: step-82
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 646d448479c8fdc8dcec868c
|
||||
title: Step 81
|
||||
title: Step 83
|
||||
challengeType: 0
|
||||
dashedName: step-81
|
||||
dashedName: step-83
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 646d44da986f2bc9b72f5fe2
|
||||
title: Step 82
|
||||
title: Step 84
|
||||
challengeType: 0
|
||||
dashedName: step-82
|
||||
dashedName: step-84
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 646d451c2e44afca71b67818
|
||||
title: Step 83
|
||||
title: Step 85
|
||||
challengeType: 0
|
||||
dashedName: step-83
|
||||
dashedName: step-85
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 646d4554721d43cb19a68bc4
|
||||
title: Step 84
|
||||
title: Step 86
|
||||
challengeType: 0
|
||||
dashedName: step-84
|
||||
dashedName: step-86
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 646d45b739da5ecbf830c108
|
||||
title: Step 85
|
||||
title: Step 87
|
||||
challengeType: 0
|
||||
dashedName: step-85
|
||||
dashedName: step-87
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 646d45ee725632cca2555146
|
||||
title: Step 86
|
||||
title: Step 88
|
||||
challengeType: 0
|
||||
dashedName: step-86
|
||||
dashedName: step-88
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 646d4626420eeecd51f241c2
|
||||
title: Step 87
|
||||
title: Step 89
|
||||
challengeType: 0
|
||||
dashedName: step-87
|
||||
dashedName: step-89
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 646d467c6994f4ce0dc416a4
|
||||
title: Step 88
|
||||
title: Step 90
|
||||
challengeType: 0
|
||||
dashedName: step-88
|
||||
dashedName: step-90
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 646d46c03e7d02cecb30f021
|
||||
title: Step 89
|
||||
title: Step 91
|
||||
challengeType: 0
|
||||
dashedName: step-89
|
||||
dashedName: step-91
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 646d4717a689e1cfa232e357
|
||||
title: Step 90
|
||||
title: Step 92
|
||||
challengeType: 0
|
||||
dashedName: step-90
|
||||
dashedName: step-92
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 646d4769ba65f1d05ef6b634
|
||||
title: Step 91
|
||||
title: Step 93
|
||||
challengeType: 0
|
||||
dashedName: step-91
|
||||
dashedName: step-93
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 646d47c8f58107d10f1e5106
|
||||
title: Step 92
|
||||
title: Step 94
|
||||
challengeType: 0
|
||||
dashedName: step-92
|
||||
dashedName: step-94
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 646d4813c17b37d1e261a566
|
||||
title: Step 93
|
||||
title: Step 95
|
||||
challengeType: 0
|
||||
dashedName: step-93
|
||||
dashedName: step-95
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 646d486aec20f7d2a581cc36
|
||||
title: Step 94
|
||||
title: Step 96
|
||||
challengeType: 0
|
||||
dashedName: step-94
|
||||
dashedName: step-96
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 646d48b936802fd34c3f05af
|
||||
title: Step 95
|
||||
title: Step 97
|
||||
challengeType: 0
|
||||
dashedName: step-95
|
||||
dashedName: step-97
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 646d498c8ebc31d3f753b22e
|
||||
title: Step 96
|
||||
title: Step 98
|
||||
challengeType: 0
|
||||
dashedName: step-96
|
||||
dashedName: step-98
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 646d49bfff9079d4b38df115
|
||||
title: Step 97
|
||||
title: Step 99
|
||||
challengeType: 0
|
||||
dashedName: step-97
|
||||
dashedName: step-99
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 646d4a07a8fb14d55cd70e09
|
||||
title: Step 98
|
||||
title: Step 100
|
||||
challengeType: 0
|
||||
dashedName: step-98
|
||||
dashedName: step-100
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 646d4a5b32a1cad6165df286
|
||||
title: Step 100
|
||||
title: Step 102
|
||||
challengeType: 0
|
||||
dashedName: step-100
|
||||
dashedName: step-102
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 646d4a8dbc04c6d6bb0001f8
|
||||
title: Step 101
|
||||
title: Step 103
|
||||
challengeType: 0
|
||||
dashedName: step-101
|
||||
dashedName: step-103
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 646d4ab9b3b4c5d74fdd2154
|
||||
title: Step 102
|
||||
title: Step 104
|
||||
challengeType: 0
|
||||
dashedName: step-102
|
||||
dashedName: step-104
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 646d4b3d80ea98d824c8a4f9
|
||||
title: Step 103
|
||||
title: Step 105
|
||||
challengeType: 0
|
||||
dashedName: step-103
|
||||
dashedName: step-105
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 6491d38f5b09a021c4b5d5fe
|
||||
title: Step 99
|
||||
title: Step 101
|
||||
challengeType: 0
|
||||
dashedName: step-99
|
||||
dashedName: step-101
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -0,0 +1,159 @@
|
||||
---
|
||||
id: 661f48f412d7631a1d9c30e6
|
||||
title: Step 75
|
||||
challengeType: 0
|
||||
dashedName: step-75
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
You should use `console.log()` to print the result of calling the `highPrecedence` function with the string `"5*3"`.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should call `console.log()`.
|
||||
|
||||
```js
|
||||
|
||||
assert.match(code, /console\.log\(/);
|
||||
|
||||
```
|
||||
|
||||
You should call your `highPrecedence` function.
|
||||
|
||||
```js
|
||||
|
||||
assert.match(code, /console\.log\(highPrecedence\(/);
|
||||
|
||||
|
||||
```
|
||||
|
||||
Pass `5*3` as the argument
|
||||
|
||||
```js
|
||||
|
||||
assert.match(code, /console\.log\(highPrecedence\(["']5\*3["']\)\);?/);
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
const infixToFunction = {
|
||||
"+": (x, y) => x + y,
|
||||
"-": (x, y) => x - y,
|
||||
"*": (x, y) => x * y,
|
||||
"/": (x, y) => x / y,
|
||||
}
|
||||
|
||||
const infixEval = (str, regex) => str.replace(regex, (_match, arg1, operator, arg2) => infixToFunction[operator](parseFloat(arg1), parseFloat(arg2)));
|
||||
|
||||
--fcc-editable-region--
|
||||
const highPrecedence = str => {
|
||||
const regex = /([\d.]+)([*\/])([\d.]+)/;
|
||||
return regex.test(str);
|
||||
}
|
||||
|
||||
--fcc-editable-region--
|
||||
|
||||
const isEven = num => num % 2 === 0;
|
||||
const sum = nums => nums.reduce((acc, el) => acc + el, 0);
|
||||
const average = nums => sum(nums) / nums.length;
|
||||
|
||||
const median = nums => {
|
||||
const sorted = nums.slice().sort((a, b) => a - b);
|
||||
const length = sorted.length;
|
||||
const middle = length / 2 - 1;
|
||||
return isEven(length)
|
||||
? average([sorted[middle], sorted[middle + 1]])
|
||||
: sorted[Math.ceil(middle)];
|
||||
}
|
||||
|
||||
const spreadsheetFunctions = {
|
||||
sum,
|
||||
average,
|
||||
median
|
||||
}
|
||||
|
||||
const range = (start, end) => Array(end - start + 1).fill(start).map((element, index) => element + index);
|
||||
const charRange = (start, end) => range(start.charCodeAt(0), end.charCodeAt(0)).map(code => String.fromCharCode(code));
|
||||
|
||||
const evalFormula = (x, cells) => {
|
||||
const idToText = id => cells.find(cell => cell.id === id).value;
|
||||
const rangeRegex = /([A-J])([1-9][0-9]?):([A-J])([1-9][0-9]?)/gi;
|
||||
const rangeFromString = (num1, num2) => range(parseInt(num1), parseInt(num2));
|
||||
const elemValue = num => character => idToText(character + num);
|
||||
const addCharacters = character1 => character2 => num => charRange(character1, character2).map(elemValue(num));
|
||||
const rangeExpanded = x.replace(rangeRegex, (_match, char1, num1, char2, num2) => rangeFromString(num1, num2).map(addCharacters(char1)(char2)));
|
||||
const cellRegex = /[A-J][1-9][0-9]?/gi;
|
||||
const cellExpanded = rangeExpanded.replace(cellRegex, match => idToText(match.toUpperCase()));
|
||||
}
|
||||
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
const label = document.createElement("div");
|
||||
label.className = "label";
|
||||
label.textContent = name;
|
||||
container.appendChild(label);
|
||||
}
|
||||
const letters = charRange("A", "J");
|
||||
letters.forEach(createLabel);
|
||||
range(1, 99).forEach(number => {
|
||||
createLabel(number);
|
||||
letters.forEach(letter => {
|
||||
const input = document.createElement("input");
|
||||
input.type = "text";
|
||||
input.id = letter + number;
|
||||
input.ariaLabel = letter + number;
|
||||
input.onchange = update;
|
||||
container.appendChild(input);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const update = event => {
|
||||
const element = event.target;
|
||||
const value = element.value.replace(/\s/g, "");
|
||||
if (!value.includes(element.id) && value.startsWith('=')) {
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,148 @@
|
||||
---
|
||||
id: 661f49650572031c6ebdb8e3
|
||||
title: Step 76
|
||||
challengeType: 0
|
||||
dashedName: step-76
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Remove both the `console.log()` with your `highPrecedence` call, and the `return` statement from your `highPrecedence` function.
|
||||
|
||||
# --hints--
|
||||
|
||||
Your code should not log the result of `highPrecedence("5*3")`.
|
||||
|
||||
```js
|
||||
|
||||
assert.notMatch(code, /console\.log\(highPrecedence\("5\*3"\)\)/);
|
||||
|
||||
```
|
||||
|
||||
Your `highPrecedence` function should not return a value.
|
||||
|
||||
```js
|
||||
|
||||
assert.isUndefined(highPrecedence("5*3"));
|
||||
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
const infixToFunction = {
|
||||
"+": (x, y) => x + y,
|
||||
"-": (x, y) => x - y,
|
||||
"*": (x, y) => x * y,
|
||||
"/": (x, y) => x / y,
|
||||
}
|
||||
|
||||
const infixEval = (str, regex) => str.replace(regex, (_match, arg1, operator, arg2) => infixToFunction[operator](parseFloat(arg1), parseFloat(arg2)));
|
||||
|
||||
--fcc-editable-region--
|
||||
const highPrecedence = str => {
|
||||
const regex = /([\d.]+)([*\/])([\d.]+)/;
|
||||
return regex.test(str);
|
||||
}
|
||||
console.log(highPrecedence("5*3"));
|
||||
--fcc-editable-region--
|
||||
|
||||
const isEven = num => num % 2 === 0;
|
||||
const sum = nums => nums.reduce((acc, el) => acc + el, 0);
|
||||
const average = nums => sum(nums) / nums.length;
|
||||
|
||||
const median = nums => {
|
||||
const sorted = nums.slice().sort((a, b) => a - b);
|
||||
const length = sorted.length;
|
||||
const middle = length / 2 - 1;
|
||||
return isEven(length)
|
||||
? average([sorted[middle], sorted[middle + 1]])
|
||||
: sorted[Math.ceil(middle)];
|
||||
}
|
||||
|
||||
const spreadsheetFunctions = {
|
||||
sum,
|
||||
average,
|
||||
median
|
||||
}
|
||||
|
||||
const range = (start, end) => Array(end - start + 1).fill(start).map((element, index) => element + index);
|
||||
const charRange = (start, end) => range(start.charCodeAt(0), end.charCodeAt(0)).map(code => String.fromCharCode(code));
|
||||
|
||||
const evalFormula = (x, cells) => {
|
||||
const idToText = id => cells.find(cell => cell.id === id).value;
|
||||
const rangeRegex = /([A-J])([1-9][0-9]?):([A-J])([1-9][0-9]?)/gi;
|
||||
const rangeFromString = (num1, num2) => range(parseInt(num1), parseInt(num2));
|
||||
const elemValue = num => character => idToText(character + num);
|
||||
const addCharacters = character1 => character2 => num => charRange(character1, character2).map(elemValue(num));
|
||||
const rangeExpanded = x.replace(rangeRegex, (_match, char1, num1, char2, num2) => rangeFromString(num1, num2).map(addCharacters(char1)(char2)));
|
||||
const cellRegex = /[A-J][1-9][0-9]?/gi;
|
||||
const cellExpanded = rangeExpanded.replace(cellRegex, match => idToText(match.toUpperCase()));
|
||||
}
|
||||
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
const label = document.createElement("div");
|
||||
label.className = "label";
|
||||
label.textContent = name;
|
||||
container.appendChild(label);
|
||||
}
|
||||
const letters = charRange("A", "J");
|
||||
letters.forEach(createLabel);
|
||||
range(1, 99).forEach(number => {
|
||||
createLabel(number);
|
||||
letters.forEach(letter => {
|
||||
const input = document.createElement("input");
|
||||
input.type = "text";
|
||||
input.id = letter + number;
|
||||
input.ariaLabel = letter + number;
|
||||
input.onchange = update;
|
||||
container.appendChild(input);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const update = event => {
|
||||
const element = event.target;
|
||||
const value = element.value.replace(/\s/g, "");
|
||||
if (!value.includes(element.id) && value.startsWith('=')) {
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -14,6 +14,7 @@ For example, this code would assign the number `25` to the second element in the
|
||||
```js
|
||||
let array = [1, 2, 3];
|
||||
array[1] = 25;
|
||||
console.log(array); // prints [1, 25, 3]
|
||||
```
|
||||
|
||||
Update the **third** element of your `rows` array to be the number `10`. Then print the `rows` array to your console.
|
||||
|
||||
@@ -7,7 +7,7 @@ dashedName: step-25
|
||||
|
||||
# --description--
|
||||
|
||||
Create a `p` element and use template strings to set its content to the `title` you destructured. Right before the content of the `p` element, create a `strong` element with the text `"Title:"`.
|
||||
Create a `p` element and use template strings to set its content to the `title` you destructured. Right before the content of the `p` element, create a `strong` element with the text `Title:`.
|
||||
|
||||
# --hints--
|
||||
|
||||
@@ -29,7 +29,7 @@ You should create a `strong` element after the opening tag of your `p` element.
|
||||
assert.match(code, /<p><strong>/)
|
||||
```
|
||||
|
||||
Your `strong` element should have the text `"Title:"`.
|
||||
Your `strong` element should have the text `Title:`.
|
||||
|
||||
```js
|
||||
assert.match(code, /<p><strong>Title:\s*<\/strong>\s*/)
|
||||
|
||||
@@ -7,7 +7,7 @@ dashedName: step-26
|
||||
|
||||
# --description--
|
||||
|
||||
Similarly to the previous step, create another `p` element, and interpolate the `date` you destructured as the text content. Inside this paragraph, create a `strong` element with the text `"Date:"`.
|
||||
Similarly to the previous step, create another `p` element, and interpolate the `date` you destructured as the text content. Inside this paragraph, create a `strong` element with the text `Date:`.
|
||||
|
||||
# --hints--
|
||||
|
||||
@@ -17,7 +17,7 @@ You should create a `p` element and interpolate `${date}` as the text.
|
||||
assert.match(code, /<p>.*\$\{date\}<\/p>/)
|
||||
```
|
||||
|
||||
You should create a `strong` element with the text `"Date:"` after the opening tag of your `p` element.
|
||||
You should create a `strong` element with the text `Date:` after the opening tag of your `p` element.
|
||||
|
||||
```js
|
||||
assert.match(code, /<p><strong>Date:\s*<\/strong>\s*/)
|
||||
|
||||
@@ -9,7 +9,7 @@ dashedName: step-28
|
||||
|
||||
To allow for task management, you need to include both a delete and an edit button for each task.
|
||||
|
||||
Create two `button` elements with the `type` attribute set to `button` and the `class` attribute set to `btn`. Set the text of the first button to `"Edit"` and the text of the second button to `"Delete"`.
|
||||
Create two `button` elements with the `type` attribute set to `button` and the `class` attribute set to `btn`. Set the text of the first button to `Edit` and the text of the second button to `Delete`.
|
||||
|
||||
# --hints--
|
||||
|
||||
@@ -19,7 +19,7 @@ You should create a `button` element of type `button`, a class `btn` and `"Edit"
|
||||
assert.match(code, /<button\s+type=('|")button\1\s+class=('|")btn\2>Edit<\/button/)
|
||||
```
|
||||
|
||||
You should create a `button` element of type `button` a class `btn` and `"Delete"` as the text, in that order.
|
||||
You should create a `button` element of type `button` a class `btn` and `Delete` as the text, in that order.
|
||||
|
||||
```js
|
||||
assert.match(code, /<button\s+type=('|")button\1\s+class=('|")btn\2>Delete<\/button/)
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 64fb1c4dc0feb219149a7c7d
|
||||
title: Step 53
|
||||
title: Step 52
|
||||
challengeType: 0
|
||||
dashedName: step-53
|
||||
dashedName: step-52
|
||||
---
|
||||
|
||||
# --description--
|
||||
@@ -307,7 +307,6 @@ const taskData = [];
|
||||
let currentTask = {};
|
||||
|
||||
const addOrUpdateTask = () => {
|
||||
addOrUpdateTaskBtn.innerText = "Add Task";
|
||||
const dataArrIndex = taskData.findIndex((item) => item.id === currentTask.id);
|
||||
const taskObj = {
|
||||
id: `${titleInput.value.toLowerCase().split(" ").join("-")}-${Date.now()}`,
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 64fb285637fa1e1c222033e3
|
||||
title: Step 54
|
||||
title: Step 53
|
||||
challengeType: 0
|
||||
dashedName: step-54
|
||||
dashedName: step-53
|
||||
---
|
||||
|
||||
# --description--
|
||||
@@ -288,7 +288,6 @@ const taskData = [];
|
||||
let currentTask = {};
|
||||
|
||||
const addOrUpdateTask = () => {
|
||||
addOrUpdateTaskBtn.innerText = "Add Task";
|
||||
const dataArrIndex = taskData.findIndex((item) => item.id === currentTask.id);
|
||||
const taskObj = {
|
||||
id: `${titleInput.value.toLowerCase().split(" ").join("-")}-${Date.now()}`,
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 64fb29348a60361ccd45c1e2
|
||||
title: Step 55
|
||||
title: Step 54
|
||||
challengeType: 0
|
||||
dashedName: step-55
|
||||
dashedName: step-54
|
||||
---
|
||||
|
||||
# --description--
|
||||
@@ -304,7 +304,6 @@ const taskData = [];
|
||||
let currentTask = {};
|
||||
|
||||
const addOrUpdateTask = () => {
|
||||
addOrUpdateTaskBtn.innerText = "Add Task";
|
||||
const dataArrIndex = taskData.findIndex((item) => item.id === currentTask.id);
|
||||
const taskObj = {
|
||||
id: `${titleInput.value.toLowerCase().split(" ").join("-")}-${Date.now()}`,
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 64fefebad99209211ec30537
|
||||
title: Step 56
|
||||
title: Step 55
|
||||
challengeType: 0
|
||||
dashedName: step-56
|
||||
dashedName: step-55
|
||||
---
|
||||
|
||||
# --description--
|
||||
@@ -294,7 +294,6 @@ const taskData = [];
|
||||
let currentTask = {};
|
||||
|
||||
const addOrUpdateTask = () => {
|
||||
addOrUpdateTaskBtn.innerText = "Add Task";
|
||||
const dataArrIndex = taskData.findIndex((item) => item.id === currentTask.id);
|
||||
const taskObj = {
|
||||
id: `${titleInput.value.toLowerCase().split(" ").join("-")}-${Date.now()}`,
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 64ff0313700dad264d19dfe4
|
||||
title: Step 57
|
||||
title: Step 56
|
||||
challengeType: 0
|
||||
dashedName: step-57
|
||||
dashedName: step-56
|
||||
---
|
||||
|
||||
# --description--
|
||||
@@ -294,7 +294,6 @@ const taskData = [];
|
||||
let currentTask = {};
|
||||
|
||||
const addOrUpdateTask = () => {
|
||||
addOrUpdateTaskBtn.innerText = "Add Task";
|
||||
const dataArrIndex = taskData.findIndex((item) => item.id === currentTask.id);
|
||||
const taskObj = {
|
||||
id: `${titleInput.value.toLowerCase().split(" ").join("-")}-${Date.now()}`,
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 64ff04cc33779427a6412449
|
||||
title: Step 58
|
||||
title: Step 57
|
||||
challengeType: 0
|
||||
dashedName: step-58
|
||||
dashedName: step-57
|
||||
---
|
||||
|
||||
# --description--
|
||||
@@ -296,7 +296,6 @@ const taskData = [];
|
||||
let currentTask = {};
|
||||
|
||||
const addOrUpdateTask = () => {
|
||||
addOrUpdateTaskBtn.innerText = "Add Task";
|
||||
const dataArrIndex = taskData.findIndex((item) => item.id === currentTask.id);
|
||||
const taskObj = {
|
||||
id: `${titleInput.value.toLowerCase().split(" ").join("-")}-${Date.now()}`,
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 64ff068e0426eb288874ed79
|
||||
title: Step 59
|
||||
title: Step 58
|
||||
challengeType: 0
|
||||
dashedName: step-59
|
||||
dashedName: step-58
|
||||
---
|
||||
|
||||
# --description--
|
||||
@@ -288,7 +288,6 @@ const taskData = [];
|
||||
let currentTask = {};
|
||||
|
||||
const addOrUpdateTask = () => {
|
||||
addOrUpdateTaskBtn.innerText = "Add Task";
|
||||
const dataArrIndex = taskData.findIndex((item) => item.id === currentTask.id);
|
||||
const taskObj = {
|
||||
id: `${titleInput.value.toLowerCase().split(" ").join("-")}-${Date.now()}`,
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 64ff23daf176a92de95f24dc
|
||||
title: Step 60
|
||||
title: Step 59
|
||||
challengeType: 0
|
||||
dashedName: step-60
|
||||
dashedName: step-59
|
||||
---
|
||||
|
||||
# --description--
|
||||
@@ -294,7 +294,6 @@ const taskData = [];
|
||||
let currentTask = {};
|
||||
|
||||
const addOrUpdateTask = () => {
|
||||
addOrUpdateTaskBtn.innerText = "Add Task";
|
||||
const dataArrIndex = taskData.findIndex((item) => item.id === currentTask.id);
|
||||
const taskObj = {
|
||||
id: `${titleInput.value.toLowerCase().split(" ").join("-")}-${Date.now()}`,
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 64ff24b80431f62ec6b93f65
|
||||
title: Step 61
|
||||
title: Step 60
|
||||
challengeType: 0
|
||||
dashedName: step-61
|
||||
dashedName: step-60
|
||||
---
|
||||
|
||||
# --description--
|
||||
@@ -286,7 +286,6 @@ const taskData = [];
|
||||
let currentTask = {};
|
||||
|
||||
const addOrUpdateTask = () => {
|
||||
addOrUpdateTaskBtn.innerText = "Add Task";
|
||||
const dataArrIndex = taskData.findIndex((item) => item.id === currentTask.id);
|
||||
const taskObj = {
|
||||
id: `${titleInput.value.toLowerCase().split(" ").join("-")}-${Date.now()}`,
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 65003986d17d1e1865b269c0
|
||||
title: Step 62
|
||||
title: Step 61
|
||||
challengeType: 0
|
||||
dashedName: step-62
|
||||
dashedName: step-61
|
||||
---
|
||||
|
||||
# --description--
|
||||
@@ -302,7 +302,6 @@ const taskData = [];
|
||||
let currentTask = {};
|
||||
|
||||
const addOrUpdateTask = () => {
|
||||
addOrUpdateTaskBtn.innerText = "Add Task";
|
||||
const dataArrIndex = taskData.findIndex((item) => item.id === currentTask.id);
|
||||
const taskObj = {
|
||||
id: `${titleInput.value.toLowerCase().split(" ").join("-")}-${Date.now()}`,
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 650046832f92c01a35834bca
|
||||
title: Step 63
|
||||
title: Step 62
|
||||
challengeType: 0
|
||||
dashedName: step-63
|
||||
dashedName: step-62
|
||||
---
|
||||
|
||||
# --description--
|
||||
@@ -303,7 +303,6 @@ const taskData = [];
|
||||
let currentTask = {};
|
||||
|
||||
const addOrUpdateTask = () => {
|
||||
addOrUpdateTaskBtn.innerText = "Add Task";
|
||||
const dataArrIndex = taskData.findIndex((item) => item.id === currentTask.id);
|
||||
const taskObj = {
|
||||
id: `${titleInput.value.toLowerCase().split(" ").join("-")}-${Date.now()}`,
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 650048b0764f9c1b798200e2
|
||||
title: Step 64
|
||||
title: Step 63
|
||||
challengeType: 0
|
||||
dashedName: step-64
|
||||
dashedName: step-63
|
||||
---
|
||||
|
||||
# --description--
|
||||
@@ -290,7 +290,6 @@ const taskData = [];
|
||||
let currentTask = {};
|
||||
|
||||
const addOrUpdateTask = () => {
|
||||
addOrUpdateTaskBtn.innerText = "Add Task";
|
||||
const dataArrIndex = taskData.findIndex((item) => item.id === currentTask.id);
|
||||
const taskObj = {
|
||||
id: `${titleInput.value.toLowerCase().split(" ").join("-")}-${Date.now()}`,
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 65004ba581d03d1d5628b41c
|
||||
title: Step 65
|
||||
title: Step 64
|
||||
challengeType: 0
|
||||
dashedName: step-65
|
||||
dashedName: step-64
|
||||
---
|
||||
|
||||
# --description--
|
||||
@@ -23,8 +23,6 @@ In that example, if `arr` has a length greater than `0`, the code inside the `if
|
||||
|
||||
Check if there's a task inside `taskData`, then call the `updateTaskContainer()` inside the `if` statement block.
|
||||
|
||||
With that, you've completed this project.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should create an `if` statement with `(taskData.length)` as the condition. As a reminder, `0` is a falsy value.
|
||||
@@ -308,7 +306,6 @@ const taskData = JSON.parse(localStorage.getItem("data")) || [];
|
||||
let currentTask = {};
|
||||
|
||||
const addOrUpdateTask = () => {
|
||||
addOrUpdateTaskBtn.innerText = "Add Task";
|
||||
const dataArrIndex = taskData.findIndex((item) => item.id === currentTask.id);
|
||||
const taskObj = {
|
||||
id: `${titleInput.value.toLowerCase().split(" ").join("-")}-${Date.now()}`,
|
||||
@@ -413,377 +410,3 @@ taskForm.addEventListener("submit", (e) => {
|
||||
addOrUpdateTask();
|
||||
});
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```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 = JSON.parse(localStorage.getItem("data")) || [];
|
||||
let currentTask = {};
|
||||
|
||||
const addOrUpdateTask = () => {
|
||||
addOrUpdateTaskBtn.innerText = "Add Task";
|
||||
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);
|
||||
} else {
|
||||
taskData[dataArrIndex] = taskObj;
|
||||
}
|
||||
|
||||
localStorage.setItem("data", JSON.stringify(taskData));
|
||||
updateTaskContainer()
|
||||
reset()
|
||||
};
|
||||
|
||||
const updateTaskContainer = () => {
|
||||
tasksContainer.innerHTML = "";
|
||||
|
||||
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 onclick="editTask(this)" type="button" class="btn">Edit</button>
|
||||
<button onclick="deleteTask(this)" type="button" class="btn">Delete</button>
|
||||
</div>
|
||||
`)
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
const deleteTask = (buttonEl) => {
|
||||
const dataArrIndex = taskData.findIndex(
|
||||
(item) => item.id === buttonEl.parentElement.id
|
||||
);
|
||||
|
||||
buttonEl.parentElement.remove();
|
||||
taskData.splice(dataArrIndex, 1);
|
||||
localStorage.setItem("data", JSON.stringify(taskData));
|
||||
}
|
||||
|
||||
const editTask = (buttonEl) => {
|
||||
const dataArrIndex = taskData.findIndex(
|
||||
(item) => item.id === buttonEl.parentElement.id
|
||||
);
|
||||
|
||||
currentTask = taskData[dataArrIndex];
|
||||
|
||||
titleInput.value = currentTask.title;
|
||||
dateInput.value = currentTask.date;
|
||||
descriptionInput.value = currentTask.description;
|
||||
|
||||
addOrUpdateTaskBtn.innerText = "Update Task";
|
||||
|
||||
|
||||
taskForm.classList.toggle("hidden");
|
||||
}
|
||||
|
||||
const reset = () => {
|
||||
titleInput.value = "";
|
||||
dateInput.value = "";
|
||||
descriptionInput.value = "";
|
||||
taskForm.classList.toggle("hidden");
|
||||
currentTask = {};
|
||||
}
|
||||
|
||||
if (taskData.length) {
|
||||
updateTaskContainer();
|
||||
}
|
||||
|
||||
openTaskFormBtn.addEventListener("click", () =>
|
||||
taskForm.classList.toggle("hidden")
|
||||
);
|
||||
|
||||
closeTaskFormBtn.addEventListener("click", () => {
|
||||
const formInputsContainValues = titleInput.value || dateInput.value || descriptionInput.value;
|
||||
const formInputValuesUpdated = titleInput.value !== currentTask.title || dateInput.value !== currentTask.date || descriptionInput.value !== currentTask.description;
|
||||
|
||||
if (formInputsContainValues && formInputValuesUpdated) {
|
||||
confirmCloseDialog.showModal();
|
||||
} else {
|
||||
reset();
|
||||
}
|
||||
});
|
||||
|
||||
cancelBtn.addEventListener("click", () => confirmCloseDialog.close());
|
||||
|
||||
discardBtn.addEventListener("click", () => {
|
||||
confirmCloseDialog.close();
|
||||
reset()
|
||||
});
|
||||
|
||||
taskForm.addEventListener("submit", (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
addOrUpdateTask();
|
||||
});
|
||||
```
|
||||
|
||||
@@ -7,7 +7,7 @@ dashedName: step-27
|
||||
|
||||
# --description--
|
||||
|
||||
Create one more `p` element and interpolate the `description` you destructured as the text. Also, create a `strong` element inside the paragraph with the text `"Description:"`.
|
||||
Create one more `p` element and interpolate the `description` you destructured as the text. Also, create a `strong` element inside the paragraph with the text `Description:`.
|
||||
|
||||
# --hints--
|
||||
|
||||
@@ -17,7 +17,7 @@ You should create a `p` element with `${description}` as the text.
|
||||
assert.match(code, /<p>.*\$\{description\}<\/p>/)
|
||||
```
|
||||
|
||||
You should create a `strong` element with the text `"Description:"` after the opening tag of your `p` element.
|
||||
You should create a `strong` element with the text `Description:` after the opening tag of your `p` element.
|
||||
|
||||
```js
|
||||
assert.match(code, /<p><strong>Description:\s*<\/strong>\s*/)
|
||||
|
||||
@@ -0,0 +1,774 @@
|
||||
---
|
||||
id: 6632420f81f3cc554a5e540b
|
||||
title: Step 65
|
||||
challengeType: 0
|
||||
dashedName: step-65
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
If you try to add a new task, edit that task, and then click on the `Add New Task` button, you will notice a bug.
|
||||
|
||||
The form button will display the incorrect text of `"Update Task"` instead of `"Add Task"`. To fix this, you will need to assign the string `"Add Task"` to `addOrUpdateTaskBtn.innerText` inside your `reset` function.
|
||||
|
||||
With that, you've completed this project.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should assign the string `"Add Task"` to `addOrUpdateTaskBtn.innerText`.
|
||||
|
||||
```js
|
||||
assert.match(code, /addOrUpdateTaskBtn\.innerText\s*=\s*('|")Add Task\1\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 = JSON.parse(localStorage.getItem("data")) || [];
|
||||
let currentTask = {};
|
||||
|
||||
const addOrUpdateTask = () => {
|
||||
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);
|
||||
} else {
|
||||
taskData[dataArrIndex] = taskObj;
|
||||
}
|
||||
|
||||
localStorage.setItem("data", JSON.stringify(taskData));
|
||||
updateTaskContainer()
|
||||
reset()
|
||||
};
|
||||
|
||||
const updateTaskContainer = () => {
|
||||
tasksContainer.innerHTML = "";
|
||||
|
||||
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 onclick="editTask(this)" type="button" class="btn">Edit</button>
|
||||
<button onclick="deleteTask(this)" type="button" class="btn">Delete</button>
|
||||
</div>
|
||||
`)
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
const deleteTask = (buttonEl) => {
|
||||
const dataArrIndex = taskData.findIndex(
|
||||
(item) => item.id === buttonEl.parentElement.id
|
||||
);
|
||||
|
||||
buttonEl.parentElement.remove();
|
||||
taskData.splice(dataArrIndex, 1);
|
||||
localStorage.setItem("data", JSON.stringify(taskData));
|
||||
}
|
||||
|
||||
const editTask = (buttonEl) => {
|
||||
const dataArrIndex = taskData.findIndex(
|
||||
(item) => item.id === buttonEl.parentElement.id
|
||||
);
|
||||
|
||||
currentTask = taskData[dataArrIndex];
|
||||
|
||||
titleInput.value = currentTask.title;
|
||||
dateInput.value = currentTask.date;
|
||||
descriptionInput.value = currentTask.description;
|
||||
|
||||
addOrUpdateTaskBtn.innerText = "Update Task";
|
||||
|
||||
taskForm.classList.toggle("hidden");
|
||||
}
|
||||
|
||||
const reset = () => {
|
||||
--fcc-editable-region--
|
||||
|
||||
--fcc-editable-region--
|
||||
titleInput.value = "";
|
||||
dateInput.value = "";
|
||||
descriptionInput.value = "";
|
||||
taskForm.classList.toggle("hidden");
|
||||
currentTask = {};
|
||||
}
|
||||
|
||||
if (taskData.length) {
|
||||
updateTaskContainer();
|
||||
}
|
||||
|
||||
openTaskFormBtn.addEventListener("click", () =>
|
||||
taskForm.classList.toggle("hidden")
|
||||
);
|
||||
|
||||
closeTaskFormBtn.addEventListener("click", () => {
|
||||
const formInputsContainValues = titleInput.value || dateInput.value || descriptionInput.value;
|
||||
const formInputValuesUpdated = titleInput.value !== currentTask.title || dateInput.value !== currentTask.date || descriptionInput.value !== currentTask.description;
|
||||
|
||||
if (formInputsContainValues && formInputValuesUpdated) {
|
||||
confirmCloseDialog.showModal();
|
||||
} else {
|
||||
reset();
|
||||
}
|
||||
});
|
||||
|
||||
cancelBtn.addEventListener("click", () => confirmCloseDialog.close());
|
||||
|
||||
discardBtn.addEventListener("click", () => {
|
||||
confirmCloseDialog.close();
|
||||
reset()
|
||||
});
|
||||
|
||||
taskForm.addEventListener("submit", (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
addOrUpdateTask();
|
||||
});
|
||||
```
|
||||
|
||||
|
||||
# --solutions--
|
||||
|
||||
```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 = JSON.parse(localStorage.getItem("data")) || [];
|
||||
let currentTask = {};
|
||||
|
||||
const addOrUpdateTask = () => {
|
||||
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);
|
||||
} else {
|
||||
taskData[dataArrIndex] = taskObj;
|
||||
}
|
||||
|
||||
localStorage.setItem("data", JSON.stringify(taskData));
|
||||
updateTaskContainer()
|
||||
reset()
|
||||
};
|
||||
|
||||
const updateTaskContainer = () => {
|
||||
tasksContainer.innerHTML = "";
|
||||
|
||||
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 onclick="editTask(this)" type="button" class="btn">Edit</button>
|
||||
<button onclick="deleteTask(this)" type="button" class="btn">Delete</button>
|
||||
</div>
|
||||
`)
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
const deleteTask = (buttonEl) => {
|
||||
const dataArrIndex = taskData.findIndex(
|
||||
(item) => item.id === buttonEl.parentElement.id
|
||||
);
|
||||
|
||||
buttonEl.parentElement.remove();
|
||||
taskData.splice(dataArrIndex, 1);
|
||||
localStorage.setItem("data", JSON.stringify(taskData));
|
||||
}
|
||||
|
||||
const editTask = (buttonEl) => {
|
||||
const dataArrIndex = taskData.findIndex(
|
||||
(item) => item.id === buttonEl.parentElement.id
|
||||
);
|
||||
|
||||
currentTask = taskData[dataArrIndex];
|
||||
|
||||
titleInput.value = currentTask.title;
|
||||
dateInput.value = currentTask.date;
|
||||
descriptionInput.value = currentTask.description;
|
||||
|
||||
addOrUpdateTaskBtn.innerText = "Update Task";
|
||||
|
||||
|
||||
taskForm.classList.toggle("hidden");
|
||||
}
|
||||
|
||||
const reset = () => {
|
||||
addOrUpdateTaskBtn.innerText = "Add Task";
|
||||
titleInput.value = "";
|
||||
dateInput.value = "";
|
||||
descriptionInput.value = "";
|
||||
taskForm.classList.toggle("hidden");
|
||||
currentTask = {};
|
||||
}
|
||||
|
||||
if (taskData.length) {
|
||||
updateTaskContainer();
|
||||
}
|
||||
|
||||
openTaskFormBtn.addEventListener("click", () =>
|
||||
taskForm.classList.toggle("hidden")
|
||||
);
|
||||
|
||||
closeTaskFormBtn.addEventListener("click", () => {
|
||||
const formInputsContainValues = titleInput.value || dateInput.value || descriptionInput.value;
|
||||
const formInputValuesUpdated = titleInput.value !== currentTask.title || dateInput.value !== currentTask.date || descriptionInput.value !== currentTask.description;
|
||||
|
||||
if (formInputsContainValues && formInputValuesUpdated) {
|
||||
confirmCloseDialog.showModal();
|
||||
} else {
|
||||
reset();
|
||||
}
|
||||
});
|
||||
|
||||
cancelBtn.addEventListener("click", () => confirmCloseDialog.close());
|
||||
|
||||
discardBtn.addEventListener("click", () => {
|
||||
confirmCloseDialog.close();
|
||||
reset()
|
||||
});
|
||||
|
||||
taskForm.addEventListener("submit", (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
addOrUpdateTask();
|
||||
});
|
||||
```
|
||||
@@ -0,0 +1,61 @@
|
||||
---
|
||||
id: 6619240f46cec8e04d77e03a
|
||||
title: Basic Functions Exercise A
|
||||
challengeType: 1
|
||||
dashedName: top-basic-functions-exercise-a
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Create a function that takes in an integer. This function should return the given `integer + 7` if the integer is less than `10`. If the integer is greater than or equal to `10`, it should return the given `integer - 3`.
|
||||
|
||||
The name of the function should be `addOrSubtract`.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should have a function called `addOrSubtract`.
|
||||
|
||||
```js
|
||||
assert.isFunction(addOrSubtract);
|
||||
```
|
||||
|
||||
Your function should take in an integer as an argument.
|
||||
|
||||
```js
|
||||
assert.match(addOrSubtract.toString(), /\s*addOrSubtract\(\s*\w+\s*\)/);
|
||||
```
|
||||
|
||||
You should return the given integer + 7 if the integer is less than 10.
|
||||
|
||||
```js
|
||||
assert.strictEqual(addOrSubtract(5), 12);
|
||||
```
|
||||
|
||||
You should return the given integer - 3 if the integer is greater than or equal to 10.
|
||||
|
||||
```js
|
||||
assert.strictEqual(addOrSubtract(10), 7);
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```js
|
||||
|
||||
```
|
||||
|
||||
## --solutions--
|
||||
|
||||
```js
|
||||
function addOrSubtract(num) {
|
||||
if (num < 10) {
|
||||
return num + 7;
|
||||
} else {
|
||||
return num - 3;
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,47 @@
|
||||
---
|
||||
id: 661e131f068359c3ccf2f4d6
|
||||
title: Basic Functions Exercise B
|
||||
challengeType: 1
|
||||
dashedName: top-basic-functions-exercise-b
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Write a function, named `multiply`, that takes two parameters and returns their product.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should have a function named `multiply`.
|
||||
|
||||
```js
|
||||
assert.isFunction(multiply);
|
||||
```
|
||||
|
||||
Your function should take in two integers as arguments.
|
||||
|
||||
```js
|
||||
assert.match(multiply.toString(), /\s*multiply\(\s*\w+\s*,\s*\w+\s*\)/);
|
||||
```
|
||||
|
||||
You should return the product of the two integers.
|
||||
|
||||
```js
|
||||
assert.strictEqual(multiply(10, 10), 100);
|
||||
```
|
||||
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```js
|
||||
|
||||
```
|
||||
|
||||
## --solutions--
|
||||
|
||||
```js
|
||||
function multiply(a, b) {
|
||||
return a * b;
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,47 @@
|
||||
---
|
||||
id: 661e151f068359c3ccf2f4d7
|
||||
title: Basic Functions Exercise C
|
||||
challengeType: 1
|
||||
dashedName: top-basic-functions-exercise-c
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Write a function, named `capitalize`, that takes a string as an parameter and returns a new string with the first letter capitalized.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should have a function named `capitalize`.
|
||||
|
||||
```js
|
||||
assert.isFunction(capitalize);
|
||||
```
|
||||
|
||||
Your function should take in a string as a parameter.
|
||||
|
||||
```js
|
||||
assert.match(capitalize.toString(), /\s*capitalize\(\s*\w+\s*\)/);
|
||||
```
|
||||
|
||||
Your function should return a new string with the first letter capitalized.
|
||||
|
||||
```js
|
||||
assert.strictEqual(capitalize('sem'), 'Sem');
|
||||
```
|
||||
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```js
|
||||
|
||||
```
|
||||
|
||||
## --solutions--
|
||||
|
||||
```js
|
||||
function capitalize(str) {
|
||||
return str[0].toUpperCase() + str.slice(1);
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,47 @@
|
||||
---
|
||||
id: 661e17c6068359c3ccf2f4d8
|
||||
title: Basic Functions Exercise D
|
||||
challengeType: 1
|
||||
dashedName: top-basic-functions-exercise-d
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Write a function, named `lastLetter`, that takes a string as a parameter and returns the last letter of the string.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should have a function named `lastLetter`.
|
||||
|
||||
```js
|
||||
assert.isFunction(lastLetter);
|
||||
```
|
||||
|
||||
Your function should take in a string as a parameter.
|
||||
|
||||
```js
|
||||
assert.match(lastLetter.toString(), /\s*lastLetter\(\s*\w+\s*\)/);
|
||||
```
|
||||
|
||||
You should return the last letter of the string.
|
||||
|
||||
```js
|
||||
assert.strictEqual(lastLetter('Sem'), 'm');
|
||||
```
|
||||
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```js
|
||||
|
||||
```
|
||||
|
||||
## --solutions--
|
||||
|
||||
```js
|
||||
function lastLetter(str) {
|
||||
return str[str.length - 1];
|
||||
}
|
||||
```
|
||||
@@ -39,7 +39,13 @@ Your `idToText` function should have an `id` parameter.
|
||||
assert.match(code, /const\s+evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*(\(\s*id\s*\)|id)\s*=>/);
|
||||
```
|
||||
|
||||
You should assign `idToText` the result of calling the `.find()` method on your `cells` array.
|
||||
Your `idToText` function should return the result of calling the `.find()` method on your `cells` array. Your callback function should use an implicit return.
|
||||
|
||||
```js
|
||||
assert.notMatch(code, /const\s+evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*(\(\s*id\s*\)|id)\s*=>\s*cells\.find\(\s*(\(\s*cell\s*\)|cell)\s*=>\s*\{/);
|
||||
```
|
||||
|
||||
Your `idToText` function should use an implicit return.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*(\(\s*id\s*\)|id)\s*=>\s*cells\.find\(/);
|
||||
@@ -57,12 +63,6 @@ Your callback function should have a `cell` parameter.
|
||||
assert.match(code, /const\s+evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*(\(\s*id\s*\)|id)\s*=>\s*cells\.find\(\s*(\(\s*cell\s*\)|cell)\s*=>/);
|
||||
```
|
||||
|
||||
Your callback function should use an implicit return.
|
||||
|
||||
```js
|
||||
assert.notMatch(code, /const\s+evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*(\(\s*id\s*\)|id)\s*=>\s*cells\.find\(\s*(\(\s*cell\s*\)|cell)\s*=>\s*\{/);
|
||||
```
|
||||
|
||||
Your callback function should return whether `cell.id` is strictly equal to `id`.
|
||||
|
||||
```js
|
||||
|
||||
@@ -11,72 +11,48 @@ In your `highPrecedence` function, declare a `regex` variable. Assign it a regul
|
||||
|
||||
Each number, and the operator, should be in separate capture groups.
|
||||
|
||||
Incorporate the regular expression you've defined into your `highPrecedence` function to test if the provided string `str` matches the pattern. Use the `test()` method on your `regex` variable and return the result.
|
||||
|
||||
# --hints--
|
||||
|
||||
|
||||
You should declare a `regex` variable in your `highPrecedence` function.
|
||||
|
||||
```js
|
||||
|
||||
assert.match(code, /const\s+highPrecedence\s*=\s*(\(\s*str\s*\)|str)\s*=>\s*{\s*(?:const|let|var)\s+regex/);
|
||||
|
||||
```
|
||||
|
||||
You should use `const` to declare your `regex` variable.
|
||||
|
||||
```js
|
||||
|
||||
assert.match(code, /const\s+highPrecedence\s*=\s*(\(\s*str\s*\)|str)\s*=>\s*{\s*const\s+regex/);
|
||||
|
||||
```
|
||||
|
||||
Your `regex` variable should be a regular expression.
|
||||
|
||||
```js
|
||||
|
||||
assert.match(code, /const\s+highPrecedence\s*=\s*(\(\s*str\s*\)|str)\s*=>\s*{\s*const\s+regex\s*=\s*\//);
|
||||
|
||||
```
|
||||
|
||||
Your `regex` should use a capture group.
|
||||
Your highPrecedence should return `regex.test(str);`
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+highPrecedence\s*=\s*(\(\s*str\s*\)|str)\s*=>\s*{\s*const\s+regex\s*=\s*\/\(/);
|
||||
assert.match(code, /return\s+regex\.test\(str\);?/);
|
||||
|
||||
```
|
||||
|
||||
Your first capture group should use a character class.
|
||||
Please enter a properly functioning regex.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+highPrecedence\s*=\s*(\(\s*str\s*\)|str)\s*=>\s*{\s*const\s+regex\s*=\s*\/\(\[/);
|
||||
```
|
||||
|
||||
Your first capture group should match any digit or a period. Use the special `\d` character class.
|
||||
assert.strictEqual(highPrecedence("5*3"), true);
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+highPrecedence\s*=\s*(\(\s*str\s*\)|str)\s*=>\s*{\s*const\s+regex\s*=\s*\/\(\[(?:\\d\.|\.\\d)\]/);
|
||||
```
|
||||
|
||||
Your first capture group should match the character class one or more times.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+highPrecedence\s*=\s*(\(\s*str\s*\)|str)\s*=>\s*{\s*const\s+regex\s*=\s*\/\(\[(?:\\d\.|\.\\d)\]\+\)/);
|
||||
```
|
||||
|
||||
Your `regex` should use a second capture group.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+highPrecedence\s*=\s*(\(\s*str\s*\)|str)\s*=>\s*{\s*const\s+regex\s*=\s*\/\(\[(?:\\d\.|\.\\d)\]\+\)\(/);
|
||||
```
|
||||
|
||||
Your second capture group should match a `*` or `/` operator. Use a character class in the capture group.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+highPrecedence\s*=\s*(\(\s*str\s*\)|str)\s*=>\s*{\s*const\s+regex\s*=\s*\/\(\[(?:\\d\.|\.\\d)\]\+\)\(\[(?:\*\\\/|\\\/*)\]\)/);
|
||||
```
|
||||
|
||||
Your `regex` should use a third capture group.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+highPrecedence\s*=\s*(\(\s*str\s*\)|str)\s*=>\s*{\s*const\s+regex\s*=\s*\/\(\[(?:\\d\.|\.\\d)\]\+\)\(\[(?:\*\\\/|\\\/*)\]\)\(/);
|
||||
```
|
||||
|
||||
Your third capture group should be the same as your first capture group.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+highPrecedence\s*=\s*(\(\s*str\s*\)|str)\s*=>\s*{\s*const\s+regex\s*=\s*\/\(\[(?:\\d\.|\.\\d)\]\+\)\(\[(?:\*\\\/|\\\/*)\]\)\(\[(?:\\d\.|\.\\d)\]\+\)/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 646d40c543943ec250039682
|
||||
title: Step 75
|
||||
title: Step 77
|
||||
challengeType: 0
|
||||
dashedName: step-75
|
||||
dashedName: step-77
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 646d40fe4b7b50c30c2b4cd8
|
||||
title: Step 76
|
||||
title: Step 78
|
||||
challengeType: 0
|
||||
dashedName: step-76
|
||||
dashedName: step-78
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 646d41e23b583fc3b8cc4579
|
||||
title: Step 77
|
||||
title: Step 79
|
||||
challengeType: 0
|
||||
dashedName: step-77
|
||||
dashedName: step-79
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 646d423fade4a9c4636acd13
|
||||
title: Step 78
|
||||
title: Step 80
|
||||
challengeType: 0
|
||||
dashedName: step-78
|
||||
dashedName: step-80
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 646d42f58deb2fc52adc6611
|
||||
title: Step 79
|
||||
title: Step 81
|
||||
challengeType: 0
|
||||
dashedName: step-79
|
||||
dashedName: step-81
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 646d43587d926bc5b6cb2e50
|
||||
title: Step 80
|
||||
title: Step 82
|
||||
challengeType: 0
|
||||
dashedName: step-80
|
||||
dashedName: step-82
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 646d448479c8fdc8dcec868c
|
||||
title: Step 81
|
||||
title: Step 83
|
||||
challengeType: 0
|
||||
dashedName: step-81
|
||||
dashedName: step-83
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 646d44da986f2bc9b72f5fe2
|
||||
title: Step 82
|
||||
title: Step 84
|
||||
challengeType: 0
|
||||
dashedName: step-82
|
||||
dashedName: step-84
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 646d451c2e44afca71b67818
|
||||
title: Step 83
|
||||
title: Step 85
|
||||
challengeType: 0
|
||||
dashedName: step-83
|
||||
dashedName: step-85
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 646d4554721d43cb19a68bc4
|
||||
title: Step 84
|
||||
title: Step 86
|
||||
challengeType: 0
|
||||
dashedName: step-84
|
||||
dashedName: step-86
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 646d45b739da5ecbf830c108
|
||||
title: Step 85
|
||||
title: Step 87
|
||||
challengeType: 0
|
||||
dashedName: step-85
|
||||
dashedName: step-87
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 646d45ee725632cca2555146
|
||||
title: Step 86
|
||||
title: Step 88
|
||||
challengeType: 0
|
||||
dashedName: step-86
|
||||
dashedName: step-88
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 646d4626420eeecd51f241c2
|
||||
title: Step 87
|
||||
title: Step 89
|
||||
challengeType: 0
|
||||
dashedName: step-87
|
||||
dashedName: step-89
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 646d467c6994f4ce0dc416a4
|
||||
title: Step 88
|
||||
title: Step 90
|
||||
challengeType: 0
|
||||
dashedName: step-88
|
||||
dashedName: step-90
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 646d46c03e7d02cecb30f021
|
||||
title: Step 89
|
||||
title: Step 91
|
||||
challengeType: 0
|
||||
dashedName: step-89
|
||||
dashedName: step-91
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 646d4717a689e1cfa232e357
|
||||
title: Step 90
|
||||
title: Step 92
|
||||
challengeType: 0
|
||||
dashedName: step-90
|
||||
dashedName: step-92
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 646d4769ba65f1d05ef6b634
|
||||
title: Step 91
|
||||
title: Step 93
|
||||
challengeType: 0
|
||||
dashedName: step-91
|
||||
dashedName: step-93
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 646d47c8f58107d10f1e5106
|
||||
title: Step 92
|
||||
title: Step 94
|
||||
challengeType: 0
|
||||
dashedName: step-92
|
||||
dashedName: step-94
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 646d4813c17b37d1e261a566
|
||||
title: Step 93
|
||||
title: Step 95
|
||||
challengeType: 0
|
||||
dashedName: step-93
|
||||
dashedName: step-95
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 646d486aec20f7d2a581cc36
|
||||
title: Step 94
|
||||
title: Step 96
|
||||
challengeType: 0
|
||||
dashedName: step-94
|
||||
dashedName: step-96
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 646d48b936802fd34c3f05af
|
||||
title: Step 95
|
||||
title: Step 97
|
||||
challengeType: 0
|
||||
dashedName: step-95
|
||||
dashedName: step-97
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 646d498c8ebc31d3f753b22e
|
||||
title: Step 96
|
||||
title: Step 98
|
||||
challengeType: 0
|
||||
dashedName: step-96
|
||||
dashedName: step-98
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 646d49bfff9079d4b38df115
|
||||
title: Step 97
|
||||
title: Step 99
|
||||
challengeType: 0
|
||||
dashedName: step-97
|
||||
dashedName: step-99
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 646d4a07a8fb14d55cd70e09
|
||||
title: Step 98
|
||||
title: Step 100
|
||||
challengeType: 0
|
||||
dashedName: step-98
|
||||
dashedName: step-100
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 646d4a5b32a1cad6165df286
|
||||
title: Step 100
|
||||
title: Step 102
|
||||
challengeType: 0
|
||||
dashedName: step-100
|
||||
dashedName: step-102
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 646d4a8dbc04c6d6bb0001f8
|
||||
title: Step 101
|
||||
title: Step 103
|
||||
challengeType: 0
|
||||
dashedName: step-101
|
||||
dashedName: step-103
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 646d4ab9b3b4c5d74fdd2154
|
||||
title: Step 102
|
||||
title: Step 104
|
||||
challengeType: 0
|
||||
dashedName: step-102
|
||||
dashedName: step-104
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 646d4b3d80ea98d824c8a4f9
|
||||
title: Step 103
|
||||
title: Step 105
|
||||
challengeType: 0
|
||||
dashedName: step-103
|
||||
dashedName: step-105
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 6491d38f5b09a021c4b5d5fe
|
||||
title: Step 99
|
||||
title: Step 101
|
||||
challengeType: 0
|
||||
dashedName: step-99
|
||||
dashedName: step-101
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -0,0 +1,159 @@
|
||||
---
|
||||
id: 661f48f412d7631a1d9c30e6
|
||||
title: Step 75
|
||||
challengeType: 0
|
||||
dashedName: step-75
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
You should use `console.log()` to print the result of calling the `highPrecedence` function with the string `"5*3"`.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should call `console.log()`.
|
||||
|
||||
```js
|
||||
|
||||
assert.match(code, /console\.log\(/);
|
||||
|
||||
```
|
||||
|
||||
You should call your `highPrecedence` function.
|
||||
|
||||
```js
|
||||
|
||||
assert.match(code, /console\.log\(highPrecedence\(/);
|
||||
|
||||
|
||||
```
|
||||
|
||||
Pass `5*3` as the argument
|
||||
|
||||
```js
|
||||
|
||||
assert.match(code, /console\.log\(highPrecedence\(["']5\*3["']\)\);?/);
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
const infixToFunction = {
|
||||
"+": (x, y) => x + y,
|
||||
"-": (x, y) => x - y,
|
||||
"*": (x, y) => x * y,
|
||||
"/": (x, y) => x / y,
|
||||
}
|
||||
|
||||
const infixEval = (str, regex) => str.replace(regex, (_match, arg1, operator, arg2) => infixToFunction[operator](parseFloat(arg1), parseFloat(arg2)));
|
||||
|
||||
--fcc-editable-region--
|
||||
const highPrecedence = str => {
|
||||
const regex = /([\d.]+)([*\/])([\d.]+)/;
|
||||
return regex.test(str);
|
||||
}
|
||||
|
||||
--fcc-editable-region--
|
||||
|
||||
const isEven = num => num % 2 === 0;
|
||||
const sum = nums => nums.reduce((acc, el) => acc + el, 0);
|
||||
const average = nums => sum(nums) / nums.length;
|
||||
|
||||
const median = nums => {
|
||||
const sorted = nums.slice().sort((a, b) => a - b);
|
||||
const length = sorted.length;
|
||||
const middle = length / 2 - 1;
|
||||
return isEven(length)
|
||||
? average([sorted[middle], sorted[middle + 1]])
|
||||
: sorted[Math.ceil(middle)];
|
||||
}
|
||||
|
||||
const spreadsheetFunctions = {
|
||||
sum,
|
||||
average,
|
||||
median
|
||||
}
|
||||
|
||||
const range = (start, end) => Array(end - start + 1).fill(start).map((element, index) => element + index);
|
||||
const charRange = (start, end) => range(start.charCodeAt(0), end.charCodeAt(0)).map(code => String.fromCharCode(code));
|
||||
|
||||
const evalFormula = (x, cells) => {
|
||||
const idToText = id => cells.find(cell => cell.id === id).value;
|
||||
const rangeRegex = /([A-J])([1-9][0-9]?):([A-J])([1-9][0-9]?)/gi;
|
||||
const rangeFromString = (num1, num2) => range(parseInt(num1), parseInt(num2));
|
||||
const elemValue = num => character => idToText(character + num);
|
||||
const addCharacters = character1 => character2 => num => charRange(character1, character2).map(elemValue(num));
|
||||
const rangeExpanded = x.replace(rangeRegex, (_match, char1, num1, char2, num2) => rangeFromString(num1, num2).map(addCharacters(char1)(char2)));
|
||||
const cellRegex = /[A-J][1-9][0-9]?/gi;
|
||||
const cellExpanded = rangeExpanded.replace(cellRegex, match => idToText(match.toUpperCase()));
|
||||
}
|
||||
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
const label = document.createElement("div");
|
||||
label.className = "label";
|
||||
label.textContent = name;
|
||||
container.appendChild(label);
|
||||
}
|
||||
const letters = charRange("A", "J");
|
||||
letters.forEach(createLabel);
|
||||
range(1, 99).forEach(number => {
|
||||
createLabel(number);
|
||||
letters.forEach(letter => {
|
||||
const input = document.createElement("input");
|
||||
input.type = "text";
|
||||
input.id = letter + number;
|
||||
input.ariaLabel = letter + number;
|
||||
input.onchange = update;
|
||||
container.appendChild(input);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const update = event => {
|
||||
const element = event.target;
|
||||
const value = element.value.replace(/\s/g, "");
|
||||
if (!value.includes(element.id) && value.startsWith('=')) {
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,148 @@
|
||||
---
|
||||
id: 661f49650572031c6ebdb8e3
|
||||
title: Step 76
|
||||
challengeType: 0
|
||||
dashedName: step-76
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Remove both the `console.log()` with your `highPrecedence` call, and the `return` statement from your `highPrecedence` function.
|
||||
|
||||
# --hints--
|
||||
|
||||
Your code should not log the result of `highPrecedence("5*3")`.
|
||||
|
||||
```js
|
||||
|
||||
assert.notMatch(code, /console\.log\(highPrecedence\("5\*3"\)\)/);
|
||||
|
||||
```
|
||||
|
||||
Your `highPrecedence` function should not return a value.
|
||||
|
||||
```js
|
||||
|
||||
assert.isUndefined(highPrecedence("5*3"));
|
||||
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
const infixToFunction = {
|
||||
"+": (x, y) => x + y,
|
||||
"-": (x, y) => x - y,
|
||||
"*": (x, y) => x * y,
|
||||
"/": (x, y) => x / y,
|
||||
}
|
||||
|
||||
const infixEval = (str, regex) => str.replace(regex, (_match, arg1, operator, arg2) => infixToFunction[operator](parseFloat(arg1), parseFloat(arg2)));
|
||||
|
||||
--fcc-editable-region--
|
||||
const highPrecedence = str => {
|
||||
const regex = /([\d.]+)([*\/])([\d.]+)/;
|
||||
return regex.test(str);
|
||||
}
|
||||
console.log(highPrecedence("5*3"));
|
||||
--fcc-editable-region--
|
||||
|
||||
const isEven = num => num % 2 === 0;
|
||||
const sum = nums => nums.reduce((acc, el) => acc + el, 0);
|
||||
const average = nums => sum(nums) / nums.length;
|
||||
|
||||
const median = nums => {
|
||||
const sorted = nums.slice().sort((a, b) => a - b);
|
||||
const length = sorted.length;
|
||||
const middle = length / 2 - 1;
|
||||
return isEven(length)
|
||||
? average([sorted[middle], sorted[middle + 1]])
|
||||
: sorted[Math.ceil(middle)];
|
||||
}
|
||||
|
||||
const spreadsheetFunctions = {
|
||||
sum,
|
||||
average,
|
||||
median
|
||||
}
|
||||
|
||||
const range = (start, end) => Array(end - start + 1).fill(start).map((element, index) => element + index);
|
||||
const charRange = (start, end) => range(start.charCodeAt(0), end.charCodeAt(0)).map(code => String.fromCharCode(code));
|
||||
|
||||
const evalFormula = (x, cells) => {
|
||||
const idToText = id => cells.find(cell => cell.id === id).value;
|
||||
const rangeRegex = /([A-J])([1-9][0-9]?):([A-J])([1-9][0-9]?)/gi;
|
||||
const rangeFromString = (num1, num2) => range(parseInt(num1), parseInt(num2));
|
||||
const elemValue = num => character => idToText(character + num);
|
||||
const addCharacters = character1 => character2 => num => charRange(character1, character2).map(elemValue(num));
|
||||
const rangeExpanded = x.replace(rangeRegex, (_match, char1, num1, char2, num2) => rangeFromString(num1, num2).map(addCharacters(char1)(char2)));
|
||||
const cellRegex = /[A-J][1-9][0-9]?/gi;
|
||||
const cellExpanded = rangeExpanded.replace(cellRegex, match => idToText(match.toUpperCase()));
|
||||
}
|
||||
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
const label = document.createElement("div");
|
||||
label.className = "label";
|
||||
label.textContent = name;
|
||||
container.appendChild(label);
|
||||
}
|
||||
const letters = charRange("A", "J");
|
||||
letters.forEach(createLabel);
|
||||
range(1, 99).forEach(number => {
|
||||
createLabel(number);
|
||||
letters.forEach(letter => {
|
||||
const input = document.createElement("input");
|
||||
input.type = "text";
|
||||
input.id = letter + number;
|
||||
input.ariaLabel = letter + number;
|
||||
input.onchange = update;
|
||||
container.appendChild(input);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const update = event => {
|
||||
const element = event.target;
|
||||
const value = element.value.replace(/\s/g, "");
|
||||
if (!value.includes(element.id) && value.startsWith('=')) {
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -14,6 +14,7 @@ For example, this code would assign the number `25` to the second element in the
|
||||
```js
|
||||
let array = [1, 2, 3];
|
||||
array[1] = 25;
|
||||
console.log(array); // prints [1, 25, 3]
|
||||
```
|
||||
|
||||
Update the **third** element of your `rows` array to be the number `10`. Then print the `rows` array to your console.
|
||||
|
||||
@@ -7,7 +7,7 @@ dashedName: step-25
|
||||
|
||||
# --description--
|
||||
|
||||
Create a `p` element and use template strings to set its content to the `title` you destructured. Right before the content of the `p` element, create a `strong` element with the text `"Title:"`.
|
||||
Create a `p` element and use template strings to set its content to the `title` you destructured. Right before the content of the `p` element, create a `strong` element with the text `Title:`.
|
||||
|
||||
# --hints--
|
||||
|
||||
@@ -29,7 +29,7 @@ You should create a `strong` element after the opening tag of your `p` element.
|
||||
assert.match(code, /<p><strong>/)
|
||||
```
|
||||
|
||||
Your `strong` element should have the text `"Title:"`.
|
||||
Your `strong` element should have the text `Title:`.
|
||||
|
||||
```js
|
||||
assert.match(code, /<p><strong>Title:\s*<\/strong>\s*/)
|
||||
|
||||
@@ -7,7 +7,7 @@ dashedName: step-26
|
||||
|
||||
# --description--
|
||||
|
||||
Similarly to the previous step, create another `p` element, and interpolate the `date` you destructured as the text content. Inside this paragraph, create a `strong` element with the text `"Date:"`.
|
||||
Similarly to the previous step, create another `p` element, and interpolate the `date` you destructured as the text content. Inside this paragraph, create a `strong` element with the text `Date:`.
|
||||
|
||||
# --hints--
|
||||
|
||||
@@ -17,7 +17,7 @@ You should create a `p` element and interpolate `${date}` as the text.
|
||||
assert.match(code, /<p>.*\$\{date\}<\/p>/)
|
||||
```
|
||||
|
||||
You should create a `strong` element with the text `"Date:"` after the opening tag of your `p` element.
|
||||
You should create a `strong` element with the text `Date:` after the opening tag of your `p` element.
|
||||
|
||||
```js
|
||||
assert.match(code, /<p><strong>Date:\s*<\/strong>\s*/)
|
||||
|
||||
@@ -9,7 +9,7 @@ dashedName: step-28
|
||||
|
||||
To allow for task management, you need to include both a delete and an edit button for each task.
|
||||
|
||||
Create two `button` elements with the `type` attribute set to `button` and the `class` attribute set to `btn`. Set the text of the first button to `"Edit"` and the text of the second button to `"Delete"`.
|
||||
Create two `button` elements with the `type` attribute set to `button` and the `class` attribute set to `btn`. Set the text of the first button to `Edit` and the text of the second button to `Delete`.
|
||||
|
||||
# --hints--
|
||||
|
||||
@@ -19,7 +19,7 @@ You should create a `button` element of type `button`, a class `btn` and `"Edit"
|
||||
assert.match(code, /<button\s+type=('|")button\1\s+class=('|")btn\2>Edit<\/button/)
|
||||
```
|
||||
|
||||
You should create a `button` element of type `button` a class `btn` and `"Delete"` as the text, in that order.
|
||||
You should create a `button` element of type `button` a class `btn` and `Delete` as the text, in that order.
|
||||
|
||||
```js
|
||||
assert.match(code, /<button\s+type=('|")button\1\s+class=('|")btn\2>Delete<\/button/)
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 64fb1c4dc0feb219149a7c7d
|
||||
title: Step 53
|
||||
title: Step 52
|
||||
challengeType: 0
|
||||
dashedName: step-53
|
||||
dashedName: step-52
|
||||
---
|
||||
|
||||
# --description--
|
||||
@@ -307,7 +307,6 @@ const taskData = [];
|
||||
let currentTask = {};
|
||||
|
||||
const addOrUpdateTask = () => {
|
||||
addOrUpdateTaskBtn.innerText = "Add Task";
|
||||
const dataArrIndex = taskData.findIndex((item) => item.id === currentTask.id);
|
||||
const taskObj = {
|
||||
id: `${titleInput.value.toLowerCase().split(" ").join("-")}-${Date.now()}`,
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 64fb285637fa1e1c222033e3
|
||||
title: Step 54
|
||||
title: Step 53
|
||||
challengeType: 0
|
||||
dashedName: step-54
|
||||
dashedName: step-53
|
||||
---
|
||||
|
||||
# --description--
|
||||
@@ -288,7 +288,6 @@ const taskData = [];
|
||||
let currentTask = {};
|
||||
|
||||
const addOrUpdateTask = () => {
|
||||
addOrUpdateTaskBtn.innerText = "Add Task";
|
||||
const dataArrIndex = taskData.findIndex((item) => item.id === currentTask.id);
|
||||
const taskObj = {
|
||||
id: `${titleInput.value.toLowerCase().split(" ").join("-")}-${Date.now()}`,
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 64fb29348a60361ccd45c1e2
|
||||
title: Step 55
|
||||
title: Step 54
|
||||
challengeType: 0
|
||||
dashedName: step-55
|
||||
dashedName: step-54
|
||||
---
|
||||
|
||||
# --description--
|
||||
@@ -304,7 +304,6 @@ const taskData = [];
|
||||
let currentTask = {};
|
||||
|
||||
const addOrUpdateTask = () => {
|
||||
addOrUpdateTaskBtn.innerText = "Add Task";
|
||||
const dataArrIndex = taskData.findIndex((item) => item.id === currentTask.id);
|
||||
const taskObj = {
|
||||
id: `${titleInput.value.toLowerCase().split(" ").join("-")}-${Date.now()}`,
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 64fefebad99209211ec30537
|
||||
title: Step 56
|
||||
title: Step 55
|
||||
challengeType: 0
|
||||
dashedName: step-56
|
||||
dashedName: step-55
|
||||
---
|
||||
|
||||
# --description--
|
||||
@@ -294,7 +294,6 @@ const taskData = [];
|
||||
let currentTask = {};
|
||||
|
||||
const addOrUpdateTask = () => {
|
||||
addOrUpdateTaskBtn.innerText = "Add Task";
|
||||
const dataArrIndex = taskData.findIndex((item) => item.id === currentTask.id);
|
||||
const taskObj = {
|
||||
id: `${titleInput.value.toLowerCase().split(" ").join("-")}-${Date.now()}`,
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 64ff0313700dad264d19dfe4
|
||||
title: Step 57
|
||||
title: Step 56
|
||||
challengeType: 0
|
||||
dashedName: step-57
|
||||
dashedName: step-56
|
||||
---
|
||||
|
||||
# --description--
|
||||
@@ -294,7 +294,6 @@ const taskData = [];
|
||||
let currentTask = {};
|
||||
|
||||
const addOrUpdateTask = () => {
|
||||
addOrUpdateTaskBtn.innerText = "Add Task";
|
||||
const dataArrIndex = taskData.findIndex((item) => item.id === currentTask.id);
|
||||
const taskObj = {
|
||||
id: `${titleInput.value.toLowerCase().split(" ").join("-")}-${Date.now()}`,
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 64ff04cc33779427a6412449
|
||||
title: Step 58
|
||||
title: Step 57
|
||||
challengeType: 0
|
||||
dashedName: step-58
|
||||
dashedName: step-57
|
||||
---
|
||||
|
||||
# --description--
|
||||
@@ -296,7 +296,6 @@ const taskData = [];
|
||||
let currentTask = {};
|
||||
|
||||
const addOrUpdateTask = () => {
|
||||
addOrUpdateTaskBtn.innerText = "Add Task";
|
||||
const dataArrIndex = taskData.findIndex((item) => item.id === currentTask.id);
|
||||
const taskObj = {
|
||||
id: `${titleInput.value.toLowerCase().split(" ").join("-")}-${Date.now()}`,
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: 64ff068e0426eb288874ed79
|
||||
title: Step 59
|
||||
title: Step 58
|
||||
challengeType: 0
|
||||
dashedName: step-59
|
||||
dashedName: step-58
|
||||
---
|
||||
|
||||
# --description--
|
||||
@@ -288,7 +288,6 @@ const taskData = [];
|
||||
let currentTask = {};
|
||||
|
||||
const addOrUpdateTask = () => {
|
||||
addOrUpdateTaskBtn.innerText = "Add Task";
|
||||
const dataArrIndex = taskData.findIndex((item) => item.id === currentTask.id);
|
||||
const taskObj = {
|
||||
id: `${titleInput.value.toLowerCase().split(" ").join("-")}-${Date.now()}`,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user