Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .git-blame-ignore-revs
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
# Scala Steward: Reformat with scalafmt 3.1.2
fb6cfb8aea15a1b339e3ed69e1e96acd7df4cae6

# Scala Steward: Reformat with scalafmt 3.1.2
624bc98810f9caed05c63c2b0354a49f6db47a87
2 changes: 1 addition & 1 deletion .scalafmt.conf
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
runner.dialect = scala3
version = "3.1.1"
version = "3.1.2"
maxColumn = 80
88 changes: 63 additions & 25 deletions js/src/main/scala/com/thoughtworks/todo/Main.scala
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@ import upickle.default.*

object Models {
val LocalStorageName = "todos-binding.scala"
def load() = Option(window.localStorage.getItem(LocalStorageName)).toSeq.flatMap(read[Seq[Todo]](_))
def save(todos: collection.Seq[Todo]) = window.localStorage.setItem(LocalStorageName, write(todos))
def load() = Option(window.localStorage.getItem(LocalStorageName)).toSeq
.flatMap(read[Seq[Todo]](_))
def save(todos: collection.Seq[Todo]) =
window.localStorage.setItem(LocalStorageName, write(todos))

val allTodos = Vars[Todo](load(): _*)

Expand All @@ -31,8 +33,16 @@ import upickle.default.*
val editingTodo = Var[Option[Todo]](None)

val all = TodoList("All", "#/", allTodos)
val active = TodoList("Active", "#/active", for (todo <- allTodos if !todo.completed) yield todo)
val completed = TodoList("Completed", "#/completed", for (todo <- allTodos if todo.completed) yield todo)
val active = TodoList(
"Active",
"#/active",
for (todo <- allTodos if !todo.completed) yield todo
)
val completed = TodoList(
"Completed",
"#/completed",
for (todo <- allTodos if todo.completed) yield todo
)
val todoLists = Vector(all, active, completed)
val route = Binding {
LatestEvent.hashchange(window).bind
Expand Down Expand Up @@ -71,7 +81,8 @@ import upickle.default.*
case "" =>
allTodos.value.remove(allTodos.value.indexWhere(todo.eq))
case trimmedTitle =>
allTodos.value(allTodos.value.indexWhere(todo.eq)) = Todo(trimmedTitle, todo.completed)
allTodos.value(allTodos.value.indexWhere(todo.eq)) =
Todo(trimmedTitle, todo.completed)
}
}
def keyDownHandler(event: KeyboardEvent) = {
Expand All @@ -84,16 +95,26 @@ import upickle.default.*
case _ =>
}
}
def blurHandler = Binding[Event => Any] { if (suppressOnBlur.bind) Function.const(()) else submit }
def blurHandler = Binding[Event => Any] {
if (suppressOnBlur.bind) Function.const(()) else submit
}
def toggleHandler(event: Event) = {
allTodos.value(allTodos.value.indexWhere(todo.eq)) = Todo(todo.title, event.currentTarget.asInstanceOf[HTMLInputElement].checked)
allTodos.value(allTodos.value.indexWhere(todo.eq)) = Todo(
todo.title,
event.currentTarget.asInstanceOf[HTMLInputElement].checked
)
}
val editInput = html"""<input class="edit" value=${ todo.title } onblur=${ blurHandler.bind } onkeydown=${ keyDownHandler } />"""
html"""<li class=${s"${if (todo.completed) "completed" else ""} ${if (editingTodo.bind.exists(todo.eq)) "editing" else ""}"}>
val editInput =
html"""<input class="edit" value=${todo.title} onblur=${blurHandler.bind} onkeydown=${keyDownHandler} />"""
html"""<li class=${s"${if (todo.completed) "completed" else ""} ${if (editingTodo.bind.exists(todo.eq)) "editing"
else ""}"}>
<div class="view">
<input class="toggle" type="checkbox" checked=${todo.completed} onclick=${toggleHandler}/>
<label ondblclick=${ (_: Event) => editingTodo.value = Some(todo); editInput.value.focus() }>${ todo.title }</label>
<button class="destroy" onclick=${ (_: Event) => allTodos.value.remove(allTodos.value.indexWhere(todo.eq)) }></button>
<label ondblclick=${(_: Event) =>
editingTodo.value = Some(todo); editInput.value.focus()
}>${todo.title}</label>
<button class="destroy" onclick=${(_: Event) =>
allTodos.value.remove(allTodos.value.indexWhere(todo.eq))}></button>
</div>
${editInput}
</li>"""
Expand All @@ -102,35 +123,52 @@ import upickle.default.*
def mainSection: Binding[Node] = {
def toggleAllClickHandler(event: Event) = {
for ((todo, i) <- allTodos.value.zipWithIndex) {
if (todo.completed != event.currentTarget.asInstanceOf[HTMLInputElement].checked) {
allTodos.value(i) = Todo(todo.title, event.currentTarget.asInstanceOf[HTMLInputElement].checked)
if (
todo.completed != event.currentTarget
.asInstanceOf[HTMLInputElement]
.checked
) {
allTodos.value(i) = Todo(
todo.title,
event.currentTarget.asInstanceOf[HTMLInputElement].checked
)
}
}
}
html"""<section class="main" style=${ if (allTodos.length.bind == 0) "display:none" else "" }>
html"""<section class="main" style=${if (allTodos.length.bind == 0)
"display:none"
else ""}>
<input type="checkbox" id="toggle-all" class="toggle-all" checked=${active.items.length.bind == 0} onclick=${toggleAllClickHandler}/>
<label for="toggle-all">Mark all as complete</label>
<ul class="todo-list">${ for (todo <- route.bind.items) yield todoListItem(todo).bind }</ul>
<ul class="todo-list">${for (todo <- route.bind.items)
yield todoListItem(todo).bind}</ul>
</section>"""
}

def footer: Binding[Node] = {
def clearCompletedClickHandler(event: MouseEvent) = {
allTodos.value --= (for (todo <- allTodos.value if todo.completed) yield todo)
allTodos.value --= (for (todo <- allTodos.value if todo.completed)
yield todo)
}
html"""<footer class="footer" style=${ if (allTodos.length.bind == 0) "display:none" else "" }>
html"""<footer class="footer" style=${if (allTodos.length.bind == 0)
"display:none"
else ""}>
<span class="todo-count">
<strong>${ active.items.length.bind.toString }</strong> ${ if (active.items.length.bind == 1) "item" else "items"} left
<strong>${active.items.length.bind.toString}</strong> ${if (
active.items.length.bind == 1
) "item"
else "items"} left
</span>
<ul class="filters">${
for (todoList <- todoLists) yield {
html"""<li>
<a href=${ todoList.hash } class=${ if (todoList eq route.bind) "selected" else "" }>${ todoList.text }</a>
<ul class="filters">${for (todoList <- todoLists) yield {
html"""<li>
<a href=${todoList.hash} class=${if (todoList eq route.bind)
"selected"
else ""}>${todoList.text}</a>
</li>"""
}
}</ul>
}}</ul>
<button class="clear-completed" onclick=$clearCompletedClickHandler
style=${if (completed.items.length.bind == 0) "visibility:hidden" else "visibility:visible"}>
style=${if (completed.items.length.bind == 0) "visibility:hidden"
else "visibility:visible"}>
Clear completed
</button>
</footer>"""
Expand Down