Skip to content

Commit

Permalink
[0300-js-todo] Add a material
Browse files Browse the repository at this point in the history
  • Loading branch information
version-1 committed Apr 23, 2024
1 parent 39329b5 commit 6c96a85
Show file tree
Hide file tree
Showing 18 changed files with 700 additions and 0 deletions.
1 change: 1 addition & 0 deletions 0300-js-todo/.gitignore
@@ -0,0 +1 @@
style.css.map
54 changes: 54 additions & 0 deletions 0300-js-todo/index.html
@@ -0,0 +1,54 @@
<!DOCTYPE html>
<html>
<head>
<title>Todo</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@100;200;300;400;500;600;700;800;900&family=Noto+Sans+JP:wght@100..900&display=swap" rel="stylesheet">
<link rel="stylesheet" type="text/css" href="stylesheets/style.css">
<link rel="icon" type="image/x-icon" href="assets/favicon.ico">
<script src="https://kit.fontawesome.com/51423ebba7.js" crossorigin="anonymous"></script>
</head>
<body class="body">
<main class="main">
<div class="main__container">
<div class="main__header">
<h1 class="main__header-title">Todo</h1>
</div>
<div class="main__body">
<form class="form js-form">
<div class="form__input">
<label class="form__input-label">タスク</label>
<input name="name" type="text" class="form__input-field" placeholder="タスク名を入力">
</div>
<div class="form__input">
<label class="form__input-label">期限日</label>
<input name="deadline" type="date" class="form__input-field" placeholder="期限日を入力">
</div>
<div class="form__input-footer">
<button class="button button--primary">追加</button>
</div>
</form>
<div class="list">
<div class="list__setting">
<label class="list__setting-label">
<input type="checkbox" class="list__setting-input js-show-completed" />
完了タスクを表示
</label>
</div>
<div class="list__header">
<div class="list__header-item">&nbsp;</div>
<div class="list__header-item list__header-item--name">タスク</div>
<div class="list__header-item">期限日</div>
<div class="list__header-item">&nbsp;</div>
</div>
<div class="list__container js-list-container">
</div>
</div>
</div>
</div>
</main>
<script src="js/function.js"></script>
</body>
</html>
215 changes: 215 additions & 0 deletions 0300-js-todo/js/function.js
@@ -0,0 +1,215 @@

class AppDate {
static parse(dateString) {
if (!dateString) {
return
}

const [year, month, day] = dateString.split('-').map((str) => parseInt(str, 10))
return new AppDate(new Date(year, month - 1, day))
}

constructor(date = new Date()) {
this.date = date
}

get cloneDate() {
return new Date(this.date.getTime())
}

toString() {
const month = (this.date.getMonth() + 1).toString().padStart(2, '0')
const day = this.date.getDate().toString().padStart(2, '0')

return `${this.date.getFullYear()}-${month}-${day}`
}

getDateInXMonth(n) {
const date = (this.date.getMonth() + n) % 12
const res = new Date(this.cloneDate.setMonth(date))

return new AppDate(res)
}

getTime() {
return this.date.getTime()
}

isAfter(date) {
return this.date.getTime() > date.getTime()
}
}

let showCompleted = false

const tasks = [
{
name: 'Task 1',
deadline: new AppDate().getDateInXMonth(1),
},
{
name: 'Task 2',
deadline: new AppDate().getDateInXMonth(2),
},
{
name: 'Task 3',
deadline: new AppDate().getDateInXMonth(3),
},
]

function renderTasks(container) {
container.innerHTML = ''
tasks.forEach((task, index) => {
if (!showCompleted && task.completed) {
return
}

renderTask(container, task, {
onEdit: (row, col) => {
},
onUpdate: (row, col) => {
},
onComplete: (row, completed) => {
row.classList.toggle('list__item--completed')
if (!showCompleted && completed) {
row.classList.add('list__item--completed-dismissing')
}
task.completed = completed

setTimeout(() => {
renderTasks(container)
}, 1000)
},
onDelete: () => {
tasks.splice(index, 1)
renderTasks(container)
}
})
})
}

function renderTask(target, task, { onComplete, onDelete }) {
const li = document.createElement('li')
const taskContainer = div('list__item')

const columns = {
checkbox: div('list__item-col list__item-col--checkbox'),
name: div('list__item-col list__item-col--name'),
deadline: div('list__item-col list__item-col--deadline'),
actions: div('list__item-col list__item-col--actions')
}

const checkboxEle = checkbox(task.completed, (checked) => {
onComplete(taskContainer, checked)
})

columns.checkbox.appendChild(checkboxEle)
columns.name.textContent = task.name
columns.deadline.textContent = task.deadline.toString()

columns.actions.appendChild(icon('icon icon--trash fa-solid fa-trash', () => {
if (window.confirm('このタスクを削除しますか?')) {
onDelete()
}
}))

Object.values(columns).forEach((column) => taskContainer.appendChild(column))
li.appendChild(taskContainer)

target.appendChild(li)
}

function submitTask(container) {
const form = document.querySelector('.js-form')
const data = new FormData(form)
const name = data.get('name')
if (!name) {
window.alert('タスク名を入力してください。')
return
}

const deadline = AppDate.parse(data.get('deadline'))
if (!deadline) {
window.alert('期限日を入力してください。')
return
}

tasks.push({
name,
deadline
})

renderTasks(container)
form.reset()
}


function div(klass) {
const div = document.createElement('div')
div.setAttribute('class', klass)

return div
}

function icon(klass, onClick) {
const i = document.createElement('i')
i.setAttribute('class', klass)
i.addEventListener('click', onClick)

return i
}

function button(text, klass, onClick) {
const button = document.createElement('button')
button.setAttribute('class', `button ${klass}`)
button.textContent = text
button.addEventListener('click', onClick)

return button
}

function checkbox(checked, onClick) {
const label = document.createElement('label')
label.setAttribute('class', 'checkbox')
if (checked) {
label.classList.add('checkbox--checked')
}

const checkbox = document.createElement('input')
checkbox.setAttribute('type', 'checkbox')
checkbox.setAttribute('class', 'checkbox__input')
checkbox.checked = checked

label.addEventListener('click', () => {
checkbox.checked = !checkbox.checked
if (checkbox.checked) {
label.classList.add('checkbox--checked')
} else {
label.classList.remove('checkbox--checked')
}

onClick(checkbox.checked)
})
label.appendChild(checkbox)
label.appendChild(icon('icon icon--check fa-solid fa-check', onClick))

return label
}


function main() {
const todoContainer = document.querySelector('.js-list-container')

document.querySelector('.js-form').addEventListener('submit', (e) => {
e.preventDefault()
submitTask(todoContainer)
})

document.querySelector('.js-show-completed').addEventListener('change', (e) => {
showCompleted = e.target.checked
renderTasks(todoContainer)
})
renderTasks(todoContainer)
}

main()

0 comments on commit 6c96a85

Please sign in to comment.