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
|
format = format
|
||||||
).printTrait().getOutput()
|
).printTrait().getOutput()
|
||||||
end generateTagsTrait
|
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
|
end generator
|
||||||
|
|
||||||
def generate(): Unit =
|
def generate(): Unit =
|
||||||
|
|
@ -134,7 +198,7 @@ class DomDefsGenerator(baseOutputDirectoryPath: String):
|
||||||
"Create HTML attribute (Note: for SVG attrs, use L.svg.svgAttr)",
|
"Create HTML attribute (Note: for SVG attrs, use L.svg.svgAttr)",
|
||||||
"",
|
"",
|
||||||
"@param name - name of the attribute, e.g. \"value\"",
|
"@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"
|
"@tparam V - value type for this attr in Scala"
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,8 @@ package scalive
|
||||||
|
|
||||||
import scalive.Mod.Attr
|
import scalive.Mod.Attr
|
||||||
import scalive.Mod.Content
|
import scalive.Mod.Content
|
||||||
import scalive.codecs.BooleanAsAttrPresenceCodec
|
import scalive.codecs.BooleanAsAttrPresenceEncoder
|
||||||
import scalive.codecs.Codec
|
import scalive.codecs.Encoder
|
||||||
import zio.json.*
|
import zio.json.*
|
||||||
|
|
||||||
class HtmlElement(val tag: HtmlTag, val mods: Vector[Mod]):
|
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]):
|
class HtmlAttr[V](val name: String, val codec: Encoder[V, String]):
|
||||||
private inline def isBooleanAsAttrPresence = codec == BooleanAsAttrPresenceCodec
|
private inline def isBooleanAsAttrPresence = codec == BooleanAsAttrPresenceEncoder
|
||||||
|
|
||||||
def :=(value: V): Mod.Attr =
|
def :=(value: V): Mod.Attr =
|
||||||
if isBooleanAsAttrPresence then
|
if isBooleanAsAttrPresence then
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,15 @@
|
||||||
import scalive.codecs.BooleanAsAttrPresenceCodec
|
import scalive.codecs.BooleanAsAttrPresenceEncoder
|
||||||
import scalive.codecs.StringAsIsCodec
|
import scalive.codecs.BooleanAsTrueFalseStringEncoder
|
||||||
|
import scalive.codecs.Encoder
|
||||||
|
import scalive.codecs.IntAsStringEncoder
|
||||||
|
import scalive.codecs.StringAsIsEncoder
|
||||||
import scalive.defs.attrs.HtmlAttrs
|
import scalive.defs.attrs.HtmlAttrs
|
||||||
import scalive.defs.complex.ComplexHtmlKeys
|
import scalive.defs.complex.ComplexHtmlKeys
|
||||||
import scalive.defs.tags.HtmlTags
|
import scalive.defs.tags.HtmlTags
|
||||||
|
|
||||||
package object scalive extends HtmlTags with HtmlAttrs with ComplexHtmlKeys:
|
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:
|
object link:
|
||||||
def navigate(path: String, mods: Mod*): HtmlElement =
|
def navigate(path: String, mods: Mod*): HtmlElement =
|
||||||
|
|
@ -14,20 +17,76 @@ package object scalive extends HtmlTags with HtmlAttrs with ComplexHtmlKeys:
|
||||||
|
|
||||||
object phx:
|
object phx:
|
||||||
private def phxAttr(suffix: String): HtmlAttr[String] =
|
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 =
|
private def phxAttrJson(suffix: String): HtmlAttrJsonValue =
|
||||||
new HtmlAttrJsonValue(s"phx-$suffix")
|
new HtmlAttrJsonValue(s"phx-$suffix")
|
||||||
private def dataPhxAttr(suffix: String): HtmlAttr[String] =
|
private def dataPhxAttr(suffix: String): HtmlAttr[String] =
|
||||||
dataAttr(s"phx-$suffix")
|
dataAttr(s"phx-$suffix")
|
||||||
|
|
||||||
private[scalive] lazy val session = dataPhxAttr("session")
|
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 link = dataPhxAttr("link")
|
||||||
private[scalive] lazy val linkState = dataPhxAttr("link-state")
|
private[scalive] lazy val linkState = dataPhxAttr("link-state")
|
||||||
lazy val click = phxAttrJson("click")
|
|
||||||
def value(key: String) = phxAttr(s"value-$key")
|
// Click
|
||||||
lazy val trackStatic = htmlAttr("phx-track-static", BooleanAsAttrPresenceCodec)
|
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 stringToMod(v: String): Mod = Mod.Content.Text(v)
|
||||||
implicit def htmlElementToMod(el: HtmlElement): Mod = Mod.Content.Tag(el)
|
implicit def htmlElementToMod(el: HtmlElement): Mod = Mod.Content.Tag(el)
|
||||||
implicit def dynStringToMod(d: Dyn[String]): Mod = Mod.Content.DynText(d)
|
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
|
* select and access specific elements via the class selectors or functions like the DOM method
|
||||||
* document.getElementsByClassName
|
* document.getElementsByClassName
|
||||||
*/
|
*/
|
||||||
val className: HtmlAttr[String] = new HtmlAttr("class", StringAsIsCodec)
|
val className: HtmlAttr[String] = new HtmlAttr("class", StringAsIsEncoder)
|
||||||
|
|
||||||
val cls: HtmlAttr[String] = className
|
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
|
* stylesheet, and the href attribute is set to the URL of an external style sheet to format the
|
||||||
* page.
|
* 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.
|
/** 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
|
* 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]]
|
* 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
|
/** 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
|
* 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
|
* 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).
|
* (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
|
/** 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
|
* 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
|
* element have mainly the purpose of allowing for quick styling, for example for testing
|
||||||
* purposes.
|
* purposes.
|
||||||
*/
|
*/
|
||||||
lazy val styleAttr: HtmlAttr[String] = new HtmlAttr("style", StringAsIsCodec)
|
lazy val styleAttr: HtmlAttr[String] = new HtmlAttr("style", StringAsIsEncoder)
|
||||||
|
|
||||||
end ComplexHtmlKeys
|
end ComplexHtmlKeys
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue