js_1
This commit is contained in:
@@ -9,7 +9,7 @@
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="todo data-js-todo">
|
||||
<div class="todo" data-js-todo>
|
||||
<h1 class="todo_title">To Do List</h1>
|
||||
<form class="todo_form" data-js-todo-new-task-form>
|
||||
<div class="todo_field field">
|
||||
@@ -36,30 +36,7 @@
|
||||
Delete All
|
||||
</button>
|
||||
</div>
|
||||
<ul class="todo_list" data-js-todo-list>
|
||||
<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>
|
||||
<ul class="todo_list" data-js-todo-list></ul>
|
||||
<div class="todo_empty-message" data-js-todo-empty-message>
|
||||
|
||||
</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