From 21309629bc6f5ed1078daed8e8a7c058929c5de3 Mon Sep 17 00:00:00 2001 From: Paul-Henri Froidmont Date: Thu, 25 Sep 2025 03:39:05 +0200 Subject: [PATCH] Bindings must by dynamic mods That way they each get their own event went inside a comprehension --- core/src/scalive/DiffBuilder.scala | 3 +++ core/src/scalive/HtmlBuilder.scala | 4 +++- core/src/scalive/HtmlElement.scala | 33 ++++++++++++++++++---------- core/src/scalive/StaticBuilder.scala | 6 ++--- 4 files changed, 30 insertions(+), 16 deletions(-) diff --git a/core/src/scalive/DiffBuilder.scala b/core/src/scalive/DiffBuilder.scala index 7337757..c40f691 100644 --- a/core/src/scalive/DiffBuilder.scala +++ b/core/src/scalive/DiffBuilder.scala @@ -23,6 +23,9 @@ object DiffBuilder: private def buildDynamic(dynamicMods: Seq[DynamicMod], trackUpdates: Boolean): Seq[Option[Diff]] = dynamicMods.flatMap { + case Attr.Binding(_, id, _) => List(id.render(trackUpdates).map(Diff.Value(_))) + case Attr.JsBinding(_, jsonValue, _) => + List(jsonValue.render(trackUpdates).map(Diff.Value(_))) case Attr.Dyn(name, value, _) => List(value.render(trackUpdates).map(v => Diff.Value(v.toString))) case Attr.DynValueAsPresence(name, value) => diff --git a/core/src/scalive/HtmlBuilder.scala b/core/src/scalive/HtmlBuilder.scala index 1e551b2..eef40b8 100644 --- a/core/src/scalive/HtmlBuilder.scala +++ b/core/src/scalive/HtmlBuilder.scala @@ -20,7 +20,9 @@ object HtmlBuilder: for i <- dynamic.indices do strw.write(static(i)) dynamic(i) match - case Attr.Dyn(name, value, isJson) => + case Attr.Binding(_, id, _) => strw.write(id.render(false).getOrElse("")) + case Attr.JsBinding(_, jsonValue, _) => strw.write(jsonValue.render(false).getOrElse("")) + case Attr.Dyn(name, value, isJson) => strw.write(value.render(false).getOrElse("")) case Attr.DynValueAsPresence(name, value) => strw.write( diff --git a/core/src/scalive/HtmlElement.scala b/core/src/scalive/HtmlElement.scala index a411565..776ee2f 100644 --- a/core/src/scalive/HtmlElement.scala +++ b/core/src/scalive/HtmlElement.scala @@ -70,7 +70,7 @@ class HtmlAttr[V](val name: String, val codec: Encoder[V, String]): class HtmlAttrBinding(val name: String): def apply(cmd: JSCommand): Mod.Attr = - Mod.Attr.JsBinding(name, cmd.toJson, cmd.bindings) + Mod.Attr.JsBinding(name, Var(cmd.toJson), cmd.bindings) def apply[Msg](msg: Msg): Mod.Attr = apply(_ => msg) @@ -78,12 +78,19 @@ class HtmlAttrBinding(val name: String): def apply[Msg](f: Map[String, String] => Msg): Mod.Attr = Mod.Attr.Binding( name, - Base64.getUrlEncoder().withoutPadding().encodeToString(Random().nextBytes(12)), + Var(Base64.getUrlEncoder().withoutPadding().encodeToString(Random().nextBytes(12))), f ) def withValue[Msg](f: String => Msg): Mod.Attr = apply(m => f(m("value"))) + def withBoolValue[Msg](f: Boolean => Msg): Mod.Attr = + apply(m => + f(m("value") match + case "on" | "yes" | "true" => true + case "off" | "no" | "false" => false) + ) + trait BindingAdapter[F, Msg]: def createMessage(f: F): Map[String, String] => Msg object BindingAdapter: @@ -99,12 +106,14 @@ sealed trait DynamicMod extends Mod object Mod: enum Attr extends Mod: - case Static(name: String, value: String) extends Attr with StaticMod - case StaticValueAsPresence(name: String, value: Boolean) extends Attr with StaticMod - case Binding(name: String, id: String, f: Map[String, String] => ?) extends Attr with StaticMod - case JsBinding(name: String, jsonValue: String, bindings: Map[String, ?]) + case Static(name: String, value: String) extends Attr with StaticMod + case StaticValueAsPresence(name: String, value: Boolean) extends Attr with StaticMod + case Binding(name: String, id: scalive.Dyn[String], f: Map[String, String] => ?) extends Attr - with StaticMod + with DynamicMod + case JsBinding(name: String, jsonValue: scalive.Dyn[String], bindings: Map[String, ?]) + extends Attr + with DynamicMod case Dyn(name: String, value: scalive.Dyn[String], isJson: Boolean = false) extends Attr with DynamicMod @@ -124,8 +133,8 @@ extension (mod: Mod) private[scalive] def setAllUnchanged(): Unit = mod match case Attr.Static(_, _) => () - case Attr.Binding(_, _, _) => () - case Attr.JsBinding(_, _, _) => () + case Attr.Binding(_, id, _) => id.setUnchanged() + case Attr.JsBinding(_, json, _) => json.setUnchanged() case Attr.StaticValueAsPresence(_, _) => () case Attr.Dyn(_, value, _) => value.setUnchanged() case Attr.DynValueAsPresence(_, value) => value.setUnchanged() @@ -149,8 +158,8 @@ extension (mod: Mod) mod match case Attr.Static(_, _) => () case Attr.StaticValueAsPresence(_, _) => () - case Attr.Binding(_, _, _) => () - case Attr.JsBinding(_, _, _) => () + case Attr.Binding(_, id, _) => id.sync() + case Attr.JsBinding(_, json, _) => json.sync() case Attr.Dyn(_, value, _) => value.sync() case Attr.DynValueAsPresence(_, value) => value.sync() case Content.Text(text) => () @@ -174,7 +183,7 @@ extension (mod: Mod) case Attr.Static(_, _) => None case Attr.StaticValueAsPresence(_, _) => None case Attr.Binding(_, eventId, f) => - if id == eventId then Some(f.asInstanceOf[Map[String, String] => Msg]) + if id == eventId.currentValue then Some(f.asInstanceOf[Map[String, String] => Msg]) else None case Attr.JsBinding(_, _, bindings) => bindings.get(id).map(msg => _ => msg.asInstanceOf[Msg]) diff --git a/core/src/scalive/StaticBuilder.scala b/core/src/scalive/StaticBuilder.scala index ef90ef5..85c2105 100644 --- a/core/src/scalive/StaticBuilder.scala +++ b/core/src/scalive/StaticBuilder.scala @@ -14,9 +14,9 @@ object StaticBuilder: val attrs = el.attrMods.flatMap { case Attr.Static(name, value) => List(Some(s" $name='$value'")) case Attr.StaticValueAsPresence(name, value) => List(Some(s" $name")) - case Attr.Binding(name, id, _) => List(Some(s""" $name="$id"""")) - case Attr.JsBinding(name, json, _) => List(Some(s" $name='$json'")) - case Attr.Dyn(name, value, isJson) => + case Attr.Binding(name, _, _) => List(Some(s""" $name=""""), None, Some('"'.toString)) + case Attr.JsBinding(name, _, _) => List(Some(s" $name='"), None, Some("'")) + case Attr.Dyn(name, value, isJson) => if isJson then List(Some(s" $name='"), None, Some("'")) else List(Some(s""" $name=""""), None, Some('"'.toString)) case Attr.DynValueAsPresence(_, value) => List(Some(""), None, Some(""))