mirror of
https://github.com/phfroidmont/scalive.git
synced 2025-12-25 05:26:59 +01:00
Add all phx html attributes
This commit is contained in:
parent
42f1729745
commit
763788fb89
6 changed files with 171 additions and 47 deletions
|
|
@ -78,6 +78,70 @@ class DomDefsGenerator(baseOutputDirectoryPath: String):
|
|||
format = format
|
||||
).printTrait().getOutput()
|
||||
end generateTagsTrait
|
||||
|
||||
override def generateAttrsTrait(
|
||||
defGroups: List[(String, List[AttrDef])],
|
||||
printDefGroupComments: Boolean,
|
||||
traitCommentLines: List[String],
|
||||
traitModifiers: List[String],
|
||||
traitName: String,
|
||||
keyKind: String,
|
||||
implNameSuffix: String,
|
||||
baseImplDefComments: List[String],
|
||||
baseImplName: String,
|
||||
namespaceImports: List[String],
|
||||
namespaceImpl: String => String,
|
||||
transformAttrDomName: String => String,
|
||||
defType: DefType
|
||||
): String =
|
||||
val (defs, defGroupComments) = defsAndGroupComments(defGroups, printDefGroupComments)
|
||||
|
||||
val tagTypes = defs.foldLeft(List[TagType]())((acc, k) => (acc :+ k.tagType).distinct)
|
||||
if tagTypes.size > 1 then
|
||||
throw new Exception(
|
||||
"Sorry, generateAttrsTrait does not support mixing attrs of different types in one call. You can contribute a PR (please contact us first), or bypass this limitation by calling AttrsTraitGenerator manually."
|
||||
)
|
||||
val tagType = tagTypes.head
|
||||
|
||||
val baseImplDef =
|
||||
if tagType == SvgTagType then
|
||||
List(
|
||||
s"def $baseImplName[V]($keyImplNameArgName: String, encoder: Encoder[V, String], namespace: Option[String]): $keyKind[V] = ${keyKindConstructor(keyKind)}($keyImplNameArgName, encoder, namespace)"
|
||||
)
|
||||
else
|
||||
List(
|
||||
s"def $baseImplName[V]($keyImplNameArgName: String, encoder: Encoder[V, String]): $keyKind[V] = ${keyKindConstructor(keyKind)}($keyImplNameArgName, encoder)"
|
||||
)
|
||||
|
||||
val headerLines = List(
|
||||
s"package $attrDefsPackagePath",
|
||||
"",
|
||||
keyTypeImport(keyKind),
|
||||
codecsImport
|
||||
) ++ namespaceImports ++ List("") ++ standardTraitCommentLines.map("// " + _)
|
||||
|
||||
new AttrsTraitGenerator(
|
||||
defs = defs.map(d => d.copy(domName = transformAttrDomName(d.domName))),
|
||||
defGroupComments = defGroupComments,
|
||||
headerLines = headerLines,
|
||||
traitCommentLines = traitCommentLines,
|
||||
traitModifiers = traitModifiers,
|
||||
traitName = traitName,
|
||||
traitExtends = Nil,
|
||||
traitThisType = None,
|
||||
defType = _ => defType,
|
||||
keyKind = keyKind,
|
||||
keyImplName = attr => attrImplName(attr.codec, implNameSuffix),
|
||||
keyImplNameArgName = keyImplNameArgName,
|
||||
baseImplDefComments = baseImplDefComments,
|
||||
baseImplName = baseImplName,
|
||||
baseImplDef = baseImplDef,
|
||||
transformCodecName = _ + "Encoder",
|
||||
namespaceImpl = namespaceImpl,
|
||||
outputImplDefs = true,
|
||||
format = format
|
||||
).printTrait().getOutput()
|
||||
end generateAttrsTrait
|
||||
end generator
|
||||
|
||||
def generate(): Unit =
|
||||
|
|
@ -134,7 +198,7 @@ class DomDefsGenerator(baseOutputDirectoryPath: String):
|
|||
"Create HTML attribute (Note: for SVG attrs, use L.svg.svgAttr)",
|
||||
"",
|
||||
"@param name - name of the attribute, e.g. \"value\"",
|
||||
"@param codec - used to encode V into String, e.g. StringAsIsCodec",
|
||||
"@param codec - used to encode V into String, e.g. StringAsIsEncoder",
|
||||
"",
|
||||
"@tparam V - value type for this attr in Scala"
|
||||
),
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
||||
// 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", BooleanAsAttrPresenceCodec)
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
30
core/src/scalive/codecs/Encoder.scala
Normal file
30
core/src/scalive/codecs/Encoder.scala
Normal 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")
|
||||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue