js_1
This commit is contained in:
@@ -9,7 +9,7 @@
|
|||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<div class="todo data-js-todo">
|
<div class="todo" data-js-todo>
|
||||||
<h1 class="todo_title">To Do List</h1>
|
<h1 class="todo_title">To Do List</h1>
|
||||||
<form class="todo_form" data-js-todo-new-task-form>
|
<form class="todo_form" data-js-todo-new-task-form>
|
||||||
<div class="todo_field field">
|
<div class="todo_field field">
|
||||||
@@ -36,30 +36,7 @@
|
|||||||
Delete All
|
Delete All
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<ul class="todo_list" data-js-todo-list>
|
<ul class="todo_list" data-js-todo-list></ul>
|
||||||
<li class="todo_item todo-item" data-js-todo-item>
|
|
||||||
<input class="todo_item_checkbox" type="checkbox" id="todo-1" data-js-todo-item-checkbox>
|
|
||||||
<label class="todo-item_label" for="todo-1" data-js-todo-item-label>Todo 1</label>
|
|
||||||
<button class="todo_item_delete-button" type="button" aria-label="Delete" title="Delete" data-js-todo-item-delete-button>
|
|
||||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<path d="M15 5L5 15M5 5L15 15" stroke="#757575" stroke-width="2" stroke-linecap="round"
|
|
||||||
stroke-linejoin="round" />
|
|
||||||
</svg>
|
|
||||||
|
|
||||||
</button>
|
|
||||||
</li>
|
|
||||||
<li class="todo_item todo-item" data-js-todo-item>
|
|
||||||
<input class="todo_item_checkbox" type="checkbox" id="todo-1" data-js-todo-item-checkbox>
|
|
||||||
<label class="todo-item_label" for="todo-1" data-js-todo-item-label>Todo 1</label>
|
|
||||||
<button class="todo_item_delete-button" type="button" aria-label="Delete" title="Delete" data-js-todo-item-delete-button>
|
|
||||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<path d="M15 5L5 15M5 5L15 15" stroke="#757575" stroke-width="2" stroke-linecap="round"
|
|
||||||
stroke-linejoin="round" />
|
|
||||||
</svg>
|
|
||||||
|
|
||||||
</button>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<div class="todo_empty-message" data-js-todo-empty-message>
|
<div class="todo_empty-message" data-js-todo-empty-message>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
154
src/main.js
154
src/main.js
@@ -0,0 +1,154 @@
|
|||||||
|
class Todo {
|
||||||
|
selectors = {
|
||||||
|
root: '[data-js-todo]',
|
||||||
|
newTaskForm: '[data-js-todo-new-task-form]',
|
||||||
|
newTaskInput: '[data-js-todo-new-task-input]',
|
||||||
|
searchTaskForm: '[data-js-todo-search-task-form]',
|
||||||
|
searchTaskInput: '[data-js-todo-search-task-input]',
|
||||||
|
totalTasks: '[data-js-todo-total-tasks]',
|
||||||
|
deleteAllButton: '[data-js-todo-delete-all-button]',
|
||||||
|
list: '[data-js-todo-list]',
|
||||||
|
item: '[data-js-todo-item]',
|
||||||
|
itemCheckbox: '[data-js-todo-item-checkbox]',
|
||||||
|
itemLabel: '[data-js-todo-item-label]',
|
||||||
|
itemDeleteButton: '[data-js-todo-item-delete-button]',
|
||||||
|
emptyMessage: '[data-js-todo-empty-message]',
|
||||||
|
}
|
||||||
|
|
||||||
|
stateClasses = {
|
||||||
|
isVisible: 'is-visible',
|
||||||
|
isDisappearing: 'is-disappearing',
|
||||||
|
}
|
||||||
|
|
||||||
|
localStorageKey = 'todo-items'
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.rootElement = document.querySelector(this.selectors.root)
|
||||||
|
this.newTaskFormElement = this.rootElement.querySelector.querySelector(this.selectors.newTaskForm)
|
||||||
|
this.newTaskInputElement = this.rootElement.querySelector.querySelector(this.selectors.newTaskInput)
|
||||||
|
this.searchTaskFormElement = this.rootElement.querySelector.querySelector(this.selectors.searchTaskForm)
|
||||||
|
this.searchTaskInputElement = this.rootElement.querySelector.querySelector(this.selectors.searchTaskInput)
|
||||||
|
this.totalTasksElement = this.rootElement.querySelector.querySelector(this.selectors.totalTasks)
|
||||||
|
this.deleteAllButtonElement = this.rootElement.querySelector.querySelector(this.selectors.deleteAllButton)
|
||||||
|
this.listElement = this.rootElement.querySelector.querySelector(this.selectors.list)
|
||||||
|
this.emptyMessageElement = this.rootElement.querySelector.querySelector(this.selectors.emptyMessage)
|
||||||
|
this.state = {
|
||||||
|
items: this.getItemsFromLocalStorage(),
|
||||||
|
filteredItems: null,
|
||||||
|
searchQuery: "",
|
||||||
|
}
|
||||||
|
this.render()
|
||||||
|
}
|
||||||
|
|
||||||
|
getItemsFromLocalStorage() {
|
||||||
|
const rawData = localStorage.getItem(this.localStorageKey)
|
||||||
|
|
||||||
|
if (!rawData) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const parsedData = JSON.parse(rawData)
|
||||||
|
|
||||||
|
return Array.isArray(parsedData) ? parsedData : []
|
||||||
|
|
||||||
|
} catch {
|
||||||
|
console.error("Todo items parse error")
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
saveItemsToLocalStorage() {
|
||||||
|
localStorage.setItem(
|
||||||
|
this.localStorageKey,
|
||||||
|
JSON.stringify(this.state.items)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
this.totalTasksElement.textContent = this.state.items.length
|
||||||
|
|
||||||
|
this.deleteAllButtonElement.classList.toggle(
|
||||||
|
this.stateClasses.isVisible,
|
||||||
|
rhis.state.items.length > 0
|
||||||
|
)
|
||||||
|
|
||||||
|
const items = this.state.filteredItems ?? this.state.items
|
||||||
|
|
||||||
|
this.listElement.innerHTML = items.map(({ id, title, isChecked }) => `
|
||||||
|
<li class="todo_item todo-item" data-js-todo-item>
|
||||||
|
<input class="todo_item_checkbox" type="checkbox" ${isChecked ? 'checked' : ''} id="${id}" data-js-todo-item-checkbox>
|
||||||
|
<label class="todo-item_label" for="${id}" data-js-todo-item-label>${title}</label>
|
||||||
|
<button class="todo_item_delete-button" type="button" aria-label="Delete" title="Delete" data-js-todo-item-delete-button>
|
||||||
|
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M15 5L5 15M5 5L15 15" stroke="#757575" stroke-width="2" stroke-linecap="round"
|
||||||
|
stroke-linejoin="round" />
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
`).join('')
|
||||||
|
|
||||||
|
const isEmptyFilteredItems = this.state.filteredItems?.length === 0
|
||||||
|
const isEmptyItems = this.state.items.lenght === 0
|
||||||
|
|
||||||
|
this.emptyMessageElement.textContent =
|
||||||
|
isEmptyFilteredItems ? 'Tasks not found'
|
||||||
|
: isEmptyItems ? 'There are no tasks yet'
|
||||||
|
: ''
|
||||||
|
}
|
||||||
|
|
||||||
|
addItem(title) {
|
||||||
|
this.state.items.push({
|
||||||
|
id: crypto?.randomUUID() ?? Date.now().toString(),
|
||||||
|
title,
|
||||||
|
isChecked: false,
|
||||||
|
})
|
||||||
|
this.saveItemsToLocalStorage()
|
||||||
|
this.render()
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteItem(id) {
|
||||||
|
this.state.items = this.state.items.filter((item) => item.id !== id)
|
||||||
|
this.saveItemsToLocalStorage()
|
||||||
|
this.render()
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleCheckedState(id) {
|
||||||
|
this.state.items = this.state.map((item) => {
|
||||||
|
if (item.id === id) {
|
||||||
|
return {
|
||||||
|
...item,
|
||||||
|
isChecked: !item.isChecked,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return item
|
||||||
|
})
|
||||||
|
this.saveItemsToLocalStorage()
|
||||||
|
this.render()
|
||||||
|
}
|
||||||
|
|
||||||
|
filter() {
|
||||||
|
const queryFormatted = this.state.searchQuery.toLowerCase()
|
||||||
|
|
||||||
|
this.state.filteredItems = this.state.items.filter(({ title }) => {
|
||||||
|
const titleFormatted = title.toLowerCase()
|
||||||
|
|
||||||
|
return titleFormatted.includes(queryFormatted)
|
||||||
|
})
|
||||||
|
this.render()
|
||||||
|
}
|
||||||
|
|
||||||
|
resetFilter() {
|
||||||
|
this.state.filteredItems = null
|
||||||
|
this.state.searchQuery = ''
|
||||||
|
this.render()
|
||||||
|
}
|
||||||
|
|
||||||
|
bindEvents() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
new Todo()
|
||||||
Reference in New Issue
Block a user