diff --git a/project/AttributeGen.scala b/project/AttributeGen.scala index d32df2f8..d06d43b6 100644 --- a/project/AttributeGen.scala +++ b/project/AttributeGen.scala @@ -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) @@ -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 } @@ -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 @@ -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 diff --git a/project/HtmxAttributes.scala b/project/HtmxAttributes.scala index 61621220..22ccead4 100644 --- a/project/HtmxAttributes.scala +++ b/project/HtmxAttributes.scala @@ -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 @@ -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" + diff --git a/sandbox/src/main/scala/example/Sandbox.scala b/sandbox/src/main/scala/example/Sandbox.scala index ada61d23..6be484ae 100644 --- a/sandbox/src/main/scala/example/Sandbox.scala +++ b/sandbox/src/main/scala/example/Sandbox.scala @@ -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"), diff --git a/tyrian-tags/shared/src/main/scala/tyrian/Attr.scala b/tyrian-tags/shared/src/main/scala/tyrian/Attr.scala index d33796fa..728747b8 100644 --- a/tyrian-tags/shared/src/main/scala/tyrian/Attr.scala +++ b/tyrian-tags/shared/src/main/scala/tyrian/Attr.scala @@ -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) diff --git a/tyrian-tags/shared/src/main/scala/tyrian/Html.scala b/tyrian-tags/shared/src/main/scala/tyrian/Html.scala index 1cd4b1e9..e44d60a5 100644 --- a/tyrian-tags/shared/src/main/scala/tyrian/Html.scala +++ b/tyrian-tags/shared/src/main/scala/tyrian/Html.scala @@ -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)