Skip to content

Commit

Permalink
Fixed #273: Nice syntax for custom attributes
Browse files Browse the repository at this point in the history
  • Loading branch information
davesmith00000 committed Aug 24, 2024
1 parent 580bb50 commit 3be5e74
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 41 deletions.
38 changes: 3 additions & 35 deletions project/AttributeGen.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,34 +3,6 @@ import scala.sys.process._

object AttributeGen {

def generateAttributeNameTypes: String =
List("String", "Int", "Double", "Boolean", "Style").map { typ =>
s""" final class AttributeName$typ(name: String):
| def :=(value: $typ): Attribute = Attribute(name.toString, ${
if (typ == "String") "value"
else "value.toString"
})
|
|""".stripMargin
}.mkString

def generatePropertyNameTypes: String =
List("String", "Boolean").map { typ =>
s""" final class PropertyName$typ(name: String):
| def :=(value: $typ): Property$typ = Property$typ(name.toString, value)
|
|""".stripMargin
}.mkString

def genAttributesAndProperties: String =
s""" def attribute(name: String, value: String): Attr[Nothing] = Attribute(name, value)
| def attributes(as: (String, String)*): List[Attr[Nothing]] = as.toList.map(p => Attribute(p._1, p._2))
| def property(name: String, value: Boolean | String): Attr[Nothing] = Property(name, value)
| def properties(ps: (String, Boolean | String)*): List[Attr[Nothing]] = ps.toList.map(p => Property(p._1, p._2))
|
| def onEvent[E <: Tyrian.Event, M](name: String, msg: E => M): Event[E, M] = Event(name, msg)
|""".stripMargin

def genAttr(tag: AttributeType, isAttribute: Boolean): String =
tag match {
case Normal(name, attrName, types) if isAttribute => genNormal(name, attrName, types)
Expand Down Expand Up @@ -77,12 +49,12 @@ object AttributeGen {
}
eventType match {
case Some(evt) =>
s""" def $attrName[M](msg: Tyrian.$evt => M): Event[Tyrian.$evt, M] = onEvent("$attr", msg)
s""" def $attrName[M](msg: Tyrian.$evt => M): Event[Tyrian.$evt, M] = AttributeSyntax.onEvent("$attr", msg)
|
|""".stripMargin

case None =>
s""" def $attrName[M](msg: M): Event[Tyrian.Event, M] = onEvent("$attr", (_: Tyrian.Event) => msg)
s""" def $attrName[M](msg: M): Event[Tyrian.Event, M] = AttributeSyntax.onEvent("$attr", (_: Tyrian.Event) => msg)
|
|""".stripMargin
}
Expand All @@ -91,7 +63,6 @@ object AttributeGen {
def template(moduleName: String, fullyQualifiedPath: String, contents: String): String =
s"""package $fullyQualifiedPath
|
|import tyrian.Html.*
|import scala.annotation.targetName
|
|// GENERATED by AttributeGen.scala - DO NOT EDIT
Expand All @@ -111,10 +82,7 @@ object AttributeGen {
println("Generating Html Attributes")

val contents: String =
generateAttributeNameTypes +
generatePropertyNameTypes +
genAttributesAndProperties +
"\n\n // Attributes\n\n" +
"\n\n // Attributes\n\n" +
attrs.map(a => genAttr(a, true)).mkString +
"\n\n // Properties\n\n" +
props.map(p => genAttr(p, false)).mkString
Expand Down
5 changes: 1 addition & 4 deletions project/HtmxAttributes.scala
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,6 @@ object HtmxAttributes {
s"""package $fullyQualifiedPath
|
|import tyrian.*
|import tyrian.Html.*
|import scala.annotation.targetName
|
|// GENERATED by AttributeGen.scala - DO NOT EDIT
Expand All @@ -99,9 +98,7 @@ object HtmxAttributes {
println("Generating Html Attributes")

val contents: String =
AttributeGen.generateAttributeNameTypes +
AttributeGen.genAttributesAndProperties +
triggerAttributeName +
triggerAttributeName +
"\n\n // Attributes\n\n" +
attrs.map(a => genAttr(a, true)).mkString +
"\n\n // Properties\n\n" +
Expand Down
2 changes: 1 addition & 1 deletion sandbox/src/main/scala/example/Sandbox.scala
Original file line number Diff line number Diff line change
Expand Up @@ -630,7 +630,7 @@ object Sandbox extends TyrianIOApp[Msg, Model]:
)

case Page.Page7 =>
div(
div(attr("custom-a") := "attr", prop("custom-b") := false, attribute("x", "y"))(
button(onClick(Msg.SelectImageFile))("Select an image file"),
button(onClick(Msg.SelectTextFile))("Select a text file"),
button(onClick(Msg.SelectBytesFile))("Select a file as bytes"),
Expand Down
55 changes: 55 additions & 0 deletions tyrian-tags/shared/src/main/scala/tyrian/Attr.scala
Original file line number Diff line number Diff line change
Expand Up @@ -117,3 +117,58 @@ object Event:
stopPropagation = true,
stopImmediatePropagation = true
)

trait AttributeSyntax:

final def attr(name: String): AttributeName = AttributeName(name)
final def prop(name: String): PropertyName = PropertyName(name)

def attribute(name: String, value: String): Attr[Nothing] = AttributeSyntax.attribute(name, value)
def attributes(as: (String, String)*): List[Attr[Nothing]] = AttributeSyntax.attributes(as.toList)
def property(name: String, value: Boolean | String): Attr[Nothing] = AttributeSyntax.property(name, value)
def properties(ps: (String, Boolean | String)*): List[Attr[Nothing]] = AttributeSyntax.properties(ps.toList)
def onEvent[E <: Tyrian.Event, M](name: String, msg: E => M): Event[E, M] = AttributeSyntax.onEvent(name, msg)

object AttributeSyntax:

def attribute(name: String, value: String): Attr[Nothing] = Attribute(name, value)
def attributes(as: List[(String, String)]): List[Attr[Nothing]] = as.map(p => Attribute(p._1, p._2))
def property(name: String, value: Boolean | String): Attr[Nothing] = Property(name, value)
def properties(ps: List[(String, Boolean | String)]): List[Attr[Nothing]] = ps.map(p => Property(p._1, p._2))

def onEvent[E <: Tyrian.Event, M](name: String, msg: E => M): Event[E, M] = Event(name, msg)

final class AttributeName(name: String):
def :=(value: String | Int | Double | Boolean): Attribute =
value match
case x: String => Attribute(name.toString, x)
case x: Int => Attribute(name.toString, x.toString)
case x: Double => Attribute(name.toString, x.toString)
case x: Boolean => Attribute(name.toString, x.toString)

final class PropertyName(name: String):
def :=(value: String | Boolean): Attribute =
value match
case x: String => Attribute(name.toString, x)
case x: Boolean => Attribute(name.toString, x.toString)

final class AttributeNameString(name: String):
def :=(value: String): Attribute = Attribute(name.toString, value)

final class AttributeNameInt(name: String):
def :=(value: Int): Attribute = Attribute(name.toString, value.toString)

final class AttributeNameDouble(name: String):
def :=(value: Double): Attribute = Attribute(name.toString, value.toString)

final class AttributeNameBoolean(name: String):
def :=(value: Boolean): Attribute = Attribute(name.toString, value.toString)

final class AttributeNameStyle(name: String):
def :=(value: Style): Attribute = Attribute(name.toString, value.toString)

final class PropertyNameString(name: String):
def :=(value: String): PropertyString = PropertyString(name.toString, value)

final class PropertyNameBoolean(name: String):
def :=(value: Boolean): PropertyBoolean = PropertyBoolean(name.toString, value)
2 changes: 1 addition & 1 deletion tyrian-tags/shared/src/main/scala/tyrian/Html.scala
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ sealed trait Html[+M] extends Elem[M]:

/** Object used to provide Html syntax `import tyrian.Html.*`
*/
object Html extends HtmlTags with HtmlAttributes:
object Html extends HtmlTags with HtmlAttributes with AttributeSyntax:

def tag[M](name: String)(attributes: Attr[M]*)(children: Elem[M]*): Html[M] =
Tag(name, attributes.toList, children.toList)
Expand Down

0 comments on commit 3be5e74

Please sign in to comment.