Initial support for JS commands

This commit is contained in:
Paul-Henri Froidmont 2025-09-16 03:16:50 +02:00
parent d42472061b
commit 6f012b6796
Signed by: phfroidmont
GPG key ID: BE948AFD7E7873BE
4 changed files with 62 additions and 5 deletions

View file

@ -64,10 +64,10 @@ class HtmlAttr[V](val name: String, val codec: Codec[V, String]):
class HtmlAttrJsonValue(val name: String):
def :=[V: JsonCodec](value: V): Mod.Attr =
def :=[V: JsonEncoder](value: V): Mod.Attr =
Mod.Attr.Static(name, value.toJson, isJson = true)
def :=[V: JsonCodec](value: Dyn[V]): Mod.Attr =
def :=[V: JsonEncoder](value: Dyn[V]): Mod.Attr =
Mod.Attr.Dyn(name, value(_.toJson), isJson = true)
sealed trait Mod

53
core/src/scalive/JS.scala Normal file
View file

@ -0,0 +1,53 @@
package scalive
import zio.json.*
import zio.json.ast.Json
val JS: JSCommands.JSCommand = JSCommands.empty
object JSCommands:
opaque type JSCommand = List[Json]
def empty: JSCommand = List.empty
object JSCommand:
given JsonEncoder[JSCommand] =
JsonEncoder[Json].contramap(ops => Json.Arr(ops.reverse*))
private def classNames(names: String) = names.split("\\s+")
extension (ops: JSCommand)
private def addOp[A: JsonEncoder](kind: String, args: A) =
(kind, args).toJsonAST.getOrElse(throw new IllegalArgumentException()) :: ops
def toggleClass(
names: String,
to: String = "",
transition: String | (String, String, String) = "",
time: Int = 200,
blocking: Boolean = true
) =
ops.addOp(
"toggle_class",
Args.ToggleClass(
classNames(names),
Some(to).filterNot(_.isBlank),
transition match
case "" => None
case names: String => Some(classNames(names))
case t: (String, String, String) => Some(t.toList),
Some(time).filterNot(_ == 200),
Some(blocking).filterNot(_ == true)
)
)
private object Args:
final case class ToggleClass(
names: Seq[String],
to: Option[String],
transition: Option[Seq[String]],
time: Option[Int],
blocking: Option[Boolean])
derives JsonEncoder
end JSCommands

View file

@ -5,7 +5,7 @@ import zio.stream.ZStream
class HomeLiveView() extends LiveView[String, Unit]:
val links = List(
"/counter" -> "Counter",
"/list" -> "List"
"/list?q=test" -> "List"
)
def init = ZIO.succeed(())

View file

@ -54,7 +54,11 @@ class ListLiveView(someParam: String) extends LiveView[Msg, Model]:
phx.click := Msg.IncAge(1),
"Inc age"
),
span(cls := "grow")
span(cls := "grow"),
button(
phx.click := JS.toggleClass("bg-red-500 border-5"),
"Toggle color"
)
)
)