Skip to content

Instantly share code, notes, and snippets.

@nguyenyou
Created February 22, 2025 09:26
Show Gist options
  • Save nguyenyou/77607cb2ab7516d53b80a28a802d3e6e to your computer and use it in GitHub Desktop.
Save nguyenyou/77607cb2ab7516d53b80a28a802d3e6e to your computer and use it in GitHub Desktop.
laminar-todoapp
//> using scala 3.6.3
//> using platform scala-js
//> using dep "org.scala-js::scalajs-dom::2.8.0"
//> using dep "com.raquo::laminar::17.2.0"
//> using jsModuleKind es
//> using jsModuleSplitStyleStr fewestmodules
package myapp
import org.scalajs.dom
import com.raquo.laminar.api.L.*
case class Todo(id: Int, text: String, completed: Boolean)
@main
def main(): Unit = {
val container = dom.document.getElementById("app")
// State management
val todosVar = Var(Vector.empty[Todo])
val inputVar = Var("")
def addTodo(text: String): Unit =
if text.trim.nonEmpty then
val newTodo = Todo(
id = System.currentTimeMillis().toInt,
text = text.trim,
completed = false
)
todosVar.update(_ :+ newTodo)
inputVar.set("")
def toggleTodo(id: Int): Unit =
todosVar.update(_.map { todo =>
if todo.id == id then todo.copy(completed = !todo.completed)
else todo
})
def deleteTodo(id: Int): Unit =
todosVar.update(_.filterNot(_.id == id))
render(
container,
div(
className := "max-w-md mx-auto mt-8 p-4",
// Input form
form(
className := "flex gap-2 mb-4",
onSubmit.preventDefault --> { _ => addTodo(inputVar.now()) },
input(
className := "flex-1 px-4 py-2 border rounded focus:outline-none focus:border-blue-500",
placeholder := "Add a new todo...",
controlled(
value <-- inputVar,
onInput.mapToValue --> inputVar
)
),
button(
className := "px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600",
"Add"
)
),
// Todo list
div(
className := "space-y-2",
children <-- todosVar.signal.map { todos =>
todos.map { todo =>
div(
className := "flex items-center gap-2 p-2 border rounded",
input(
typ := "checkbox",
checked := todo.completed,
className := "h-5 w-5",
onClick --> { _ => toggleTodo(todo.id) }
),
span(
className := (if todo.completed then "line-through text-gray-500" else ""),
todo.text
),
button(
className := "ml-auto px-2 py-1 text-red-500 hover:bg-red-100 rounded",
onClick --> { _ => deleteTodo(todo.id) },
"×"
)
)
}
}
)
)
)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment