Add all phx html attributes

This commit is contained in:
Paul-Henri Froidmont 2025-09-18 21:32:02 +02:00
parent 42f1729745
commit 763788fb89
Signed by: phfroidmont
GPG key ID: BE948AFD7E7873BE
6 changed files with 171 additions and 47 deletions

View file

@ -2,8 +2,8 @@ package scalive
import scalive.Mod.Attr
import scalive.Mod.Content
import scalive.codecs.BooleanAsAttrPresenceCodec
import scalive.codecs.Codec
import scalive.codecs.BooleanAsAttrPresenceEncoder
import scalive.codecs.Encoder
import zio.json.*
class HtmlElement(val tag: HtmlTag, val mods: Vector[Mod]):
@ -43,8 +43,8 @@ class HtmlTag(val name: String, val void: Boolean = false):
}
)
class HtmlAttr[V](val name: String, val codec: Codec[V, String]):
private inline def isBooleanAsAttrPresence = codec == BooleanAsAttrPresenceCodec
class HtmlAttr[V](val name: String, val codec: Encoder[V, String]):
private inline def isBooleanAsAttrPresence = codec == BooleanAsAttrPresenceEncoder
def :=(value: V): Mod.Attr =
if isBooleanAsAttrPresence then

View file

@ -1,12 +1,15 @@
import scalive.codecs.BooleanAsAttrPresenceCodec
import scalive.codecs.StringAsIsCodec
import scalive.codecs.BooleanAsAttrPresenceEncoder
import scalive.codecs.BooleanAsTrueFalseStringEncoder
import scalive.codecs.Encoder
import scalive.codecs.IntAsStringEncoder
import scalive.codecs.StringAsIsEncoder
import scalive.defs.attrs.HtmlAttrs
import scalive.defs.complex.ComplexHtmlKeys
import scalive.defs.tags.HtmlTags
package object scalive extends HtmlTags with HtmlAttrs with ComplexHtmlKeys:
lazy val defer = htmlAttr("defer", codecs.BooleanAsAttrPresenceCodec)
lazy val defer = htmlAttr("defer", codecs.BooleanAsAttrPresenceEncoder)
object link:
def navigate(path: String, mods: Mod*): HtmlElement =
@ -14,20 +17,76 @@ package object scalive extends HtmlTags with HtmlAttrs with ComplexHtmlKeys:
object phx:
private def phxAttr(suffix: String): HtmlAttr[String] =
new HtmlAttr(s"phx-$suffix", StringAsIsCodec)
new HtmlAttr(s"phx-$suffix", StringAsIsEncoder)
private def phxAttrBool(suffix: String): HtmlAttr[Boolean] =
new HtmlAttr(s"phx-$suffix", BooleanAsTrueFalseStringEncoder)
private def phxAttrInt(suffix: String): HtmlAttr[Int] =
new HtmlAttr(s"phx-$suffix", IntAsStringEncoder)
private def phxAttrJson(suffix: String): HtmlAttrJsonValue =
new HtmlAttrJsonValue(s"phx-$suffix")
private def dataPhxAttr(suffix: String): HtmlAttr[String] =
dataAttr(s"phx-$suffix")
private[scalive] lazy val session = dataPhxAttr("session")
private[scalive] lazy val main = htmlAttr("data-phx-main", BooleanAsAttrPresenceCodec)
private[scalive] lazy val main = htmlAttr("data-phx-main", BooleanAsAttrPresenceEncoder)
private[scalive] lazy val link = dataPhxAttr("link")
private[scalive] lazy val linkState = dataPhxAttr("link-state")
lazy val click = phxAttrJson("click")
def value(key: String) = phxAttr(s"value-$key")
lazy val trackStatic = htmlAttr("phx-track-static", BooleanAsAttrPresenceCodec)
// Click
lazy val click = phxAttrJson("click")
lazy val clickAway = phxAttrJson("click-away")
// Focus
lazy val blur = phxAttrJson("blur")
lazy val focus = phxAttrJson("focus")
lazy val windowBlur = phxAttrJson("window-blur")
// Keyboard
lazy val keydown = phxAttrJson("keydown")
lazy val keyup = phxAttrJson("keyup")
lazy val windowKeydown = phxAttrJson("window-keydown")
lazy val windowKeyup = phxAttrJson("window-keyup")
lazy val key = phxAttr("key")
// Scroll
lazy val viewportTop = phxAttrJson("viewport-top")
lazy val viewportBottom = phxAttrJson("viewport-bottom")
// Form
lazy val change = phxAttrJson("change")
lazy val submit = phxAttrJson("submit")
lazy val autoRecover = phxAttrJson("auto-recover")
lazy val triggerAction = phxAttrBool("trigger-action")
// Button
lazy val disableWith = phxAttr("disable-with")
// Socket connection lifecycle
lazy val connected = phxAttrJson("connected")
lazy val disconnected = phxAttrJson("disconnected")
// DOM element lifecycle
lazy val mounted = phxAttrJson("mounted")
lazy val remove = phxAttrJson("remove")
lazy val update = new HtmlAttr["update" | "stream" | "ignore"](s"phx-update", Encoder(identity))
// Client hooks
lazy val hook = phxAttr("hook")
// Rate limiting
lazy val debounce = new HtmlAttr["blur" | Int](
s"phx-debounce",
Encoder {
case _: "blur" => "blur"
case value: Int => value.toString
}
)
lazy val throttle = phxAttrInt("throttle")
def value(key: String) = phxAttr(s"value-$key")
lazy val trackStatic = htmlAttr("phx-track-static", BooleanAsAttrPresenceEncoder)
end phx
implicit def stringToMod(v: String): Mod = Mod.Content.Text(v)
implicit def htmlElementToMod(el: HtmlElement): Mod = Mod.Content.Tag(el)
implicit def dynStringToMod(d: Dyn[String]): Mod = Mod.Content.DynText(d)
end scalive

View file

@ -1,29 +0,0 @@
package scalive.codecs
class Codec[ScalaType, DomType](val encode: ScalaType => DomType, val decode: DomType => ScalaType)
def AsIsCodec[V](): Codec[V, V] = Codec(identity, identity)
val StringAsIsCodec: Codec[String, String] = AsIsCodec()
val IntAsIsCodec: Codec[Int, Int] = AsIsCodec()
lazy val IntAsStringCodec: Codec[Int, String] = Codec[Int, String](_.toString, _.toInt)
lazy val DoubleAsIsCodec: Codec[Double, Double] = AsIsCodec()
lazy val DoubleAsStringCodec: Codec[Double, String] = Codec[Double, String](_.toString, _.toDouble)
val BooleanAsIsCodec: Codec[Boolean, Boolean] = AsIsCodec()
lazy val BooleanAsAttrPresenceCodec: Codec[Boolean, String] =
Codec[Boolean, String](if _ then "" else null, _ != null)
lazy val BooleanAsTrueFalseStringCodec: Codec[Boolean, String] =
Codec[Boolean, String](if _ then "true" else "false", _ == "true")
lazy val BooleanAsYesNoStringCodec: Codec[Boolean, String] =
Codec[Boolean, String](if _ then "yes" else "no", _ == "yes")
lazy val BooleanAsOnOffStringCodec: Codec[Boolean, String] =
Codec[Boolean, String](if _ then "on" else "off", _ == "on")

View file

@ -0,0 +1,30 @@
package scalive.codecs
class Encoder[ScalaType, DomType](val encode: ScalaType => DomType)
def AsIsEncoder[V](): Encoder[V, V] = Encoder(identity)
val StringAsIsEncoder: Encoder[String, String] = AsIsEncoder()
val IntAsIsEncoder: Encoder[Int, Int] = AsIsEncoder()
lazy val IntAsStringEncoder: Encoder[Int, String] = Encoder[Int, String](_.toString)
lazy val DoubleAsIsEncoder: Encoder[Double, Double] = AsIsEncoder()
lazy val DoubleAsStringEncoder: Encoder[Double, String] =
Encoder[Double, String](_.toString)
val BooleanAsIsEncoder: Encoder[Boolean, Boolean] = AsIsEncoder()
lazy val BooleanAsAttrPresenceEncoder: Encoder[Boolean, String] =
Encoder[Boolean, String](if _ then "" else null)
lazy val BooleanAsTrueFalseStringEncoder: Encoder[Boolean, String] =
Encoder[Boolean, String](if _ then "true" else "false")
lazy val BooleanAsYesNoStringEncoder: Encoder[Boolean, String] =
Encoder[Boolean, String](if _ then "yes" else "no")
lazy val BooleanAsOnOffStringEncoder: Encoder[Boolean, String] =
Encoder[Boolean, String](if _ then "on" else "off")

View file

@ -12,7 +12,7 @@ trait ComplexHtmlKeys:
* select and access specific elements via the class selectors or functions like the DOM method
* document.getElementsByClassName
*/
val className: HtmlAttr[String] = new HtmlAttr("class", StringAsIsCodec)
val className: HtmlAttr[String] = new HtmlAttr("class", StringAsIsEncoder)
val cls: HtmlAttr[String] = className
@ -22,7 +22,7 @@ trait ComplexHtmlKeys:
* stylesheet, and the href attribute is set to the URL of an external style sheet to format the
* page.
*/
lazy val rel: HtmlAttr[String] = new HtmlAttr("rel", StringAsIsCodec)
lazy val rel: HtmlAttr[String] = new HtmlAttr("rel", StringAsIsEncoder)
/** The attribute describes the role(s) the current element plays in the context of the document.
* This can be used, for example, by applications and assistive technologies to determine the
@ -35,7 +35,7 @@ trait ComplexHtmlKeys:
*
* See: [[http://www.w3.org/TR/role-attribute/#s_role_module_attributes]]
*/
lazy val role: HtmlAttr[String] = new HtmlAttr("role", StringAsIsCodec)
lazy val role: HtmlAttr[String] = new HtmlAttr("role", StringAsIsEncoder)
/** This class of attributes, called custom data attributes, allows proprietary information to be
* exchanged between the HTML and its DOM representation that may be used by scripts. All such
@ -51,13 +51,13 @@ trait ComplexHtmlKeys:
* attribute data-test-value will be accessible via HTMLElement.dataset.testValue as any dash
* (U+002D) is replaced by the capitalization of the next letter (camelcase).
*/
def dataAttr(suffix: String): HtmlAttr[String] = new HtmlAttr(s"data-$suffix", StringAsIsCodec)
def dataAttr(suffix: String): HtmlAttr[String] = new HtmlAttr(s"data-$suffix", StringAsIsEncoder)
/** This attribute contains CSS styling declarations to be applied to the element. Note that it is
* recommended for styles to be defined in a separate file or files. This attribute and the style
* element have mainly the purpose of allowing for quick styling, for example for testing
* purposes.
*/
lazy val styleAttr: HtmlAttr[String] = new HtmlAttr("style", StringAsIsCodec)
lazy val styleAttr: HtmlAttr[String] = new HtmlAttr("style", StringAsIsEncoder)
end ComplexHtmlKeys