diff --git a/dom/src/main/scala/com/thoughtworks/binding/dom.scala b/dom/src/main/scala/com/thoughtworks/binding/dom.scala index b5d38488..9a01e459 100644 --- a/dom/src/main/scala/com/thoughtworks/binding/dom.scala +++ b/dom/src/main/scala/com/thoughtworks/binding/dom.scala @@ -27,7 +27,7 @@ package com.thoughtworks.binding import Binding.{BindingSeq, Constants, MultiMountPoint, SingletonBindingSeq} import dom.Runtime.NodeSeqMountPoint import com.thoughtworks.Extractor._ -import com.thoughtworks.binding.XmlExtractor.{PrefixedName, UnprefixedName} +import com.thoughtworks.binding.XmlExtractor.{PrefixedName, QName, UnprefixedName} import com.thoughtworks.sde.core.Preprocessor import macrocompat.bundle import org.scalajs.dom.raw._ @@ -39,6 +39,8 @@ import scala.language.experimental.macros import scalatags.JsDom import scalatags.jsdom import org.scalajs.dom.document +import scalatags.JsDom.TypedTag +import scalatags.generic.Namespace import scala.collection.immutable.Queue import scala.reflect.NameTransformer @@ -140,7 +142,21 @@ object dom { } - object TagsAndTags2 extends JsDom.Cap with jsdom.Tags with jsdom.Tags2 + object TagsAndTags2 extends JsDom.Cap with jsdom.Tags with jsdom.Tags2 { + + import scala.language.dynamics + + final class DynamicDataTag private[TagsAndTags2] () + extends TypedTag[HTMLElement]("data", Nil, false, Namespace.htmlNamespaceConfig) + with Dynamic { + final def selectDynamic(tagName: String): ConcreteHtmlTag[Element] = { + TagsAndTags2.tag(tagName) + } + } + + override lazy val data = new DynamicDataTag() + + } @inline def domBindingSeq(bindingSeq: BindingSeq[Node]) = bindingSeq @@ -308,7 +324,7 @@ object dom { private def transformedWithValDefs: PartialFunction[Tree, (Queue[ValDef], Tree)] = { case tree @ NodeBuffer(children) => nodeSeq(children) - case tree @ Elem(UnprefixedName(label), attributes, _, children) => + case tree @ Elem(tag, attributes, _, children) => val idOption = findTextAttribute("local-id", attributes).orElse(findTextAttribute("id", attributes)) val elementName = idOption match { case None => TermName(c.freshName("element")) @@ -323,16 +339,7 @@ object dom { } } } yield { - val attributeAccess = key match { - case UnprefixedName(localPart) => - val keyName = TermName(NameTransformer.encode(localPart)) - q"""$elementName.$keyName""" - case PrefixedName(prefix, localPart) => - localPart.split(':').foldLeft(q"""$elementName.${TermName(NameTransformer.encode(prefix))}""") { - (prefixExpr, propertyName) => - q"""$prefixExpr.${TermName(NameTransformer.encode(propertyName))}""" - } - } + val attributeAccess = propertyAccess(key, q"$elementName") atPos(value.pos) { value match { @@ -381,8 +388,10 @@ object dom { """ }) } - val elementDef = - q"val $elementName = _root_.com.thoughtworks.binding.dom.Runtime.TagsAndTags2.${TermName(label)}.render" + + val tagAccess = propertyAccess(tag, q"_root_.com.thoughtworks.binding.dom.Runtime.TagsAndTags2") + + val elementDef = q"val $elementName = $tagAccess.render" idOption match { case None => valDefs -> q""" @@ -405,6 +414,18 @@ object dom { attributes.collectFirst { case (UnprefixedName(`unprefixedName`), Text(text)) => text } } + private def propertyAccess(xmlName: QName, objectAccess: RefTree): Select = { + xmlName match { + case UnprefixedName(localPart) => + q"$objectAccess.${TermName(NameTransformer.encode(localPart))}" + case PrefixedName(prefix, localPart) => + localPart.split(':').foldLeft(q"$objectAccess.${TermName(NameTransformer.encode(prefix))}") { + (prefixExpr, segmentName) => + q"$prefixExpr.${TermName(NameTransformer.encode(segmentName))}" + } + } + } + private def transformed: PartialFunction[Tree, Tree] = { case Block(stats, expr) => super.transform(Block(stats.flatMap { diff --git a/dom/src/test/scala/com/thoughtworks/binding/domTest.scala b/dom/src/test/scala/com/thoughtworks/binding/domTest.scala index dc9bfc19..a87a7414 100644 --- a/dom/src/test/scala/com/thoughtworks/binding/domTest.scala +++ b/dom/src/test/scala/com/thoughtworks/binding/domTest.scala @@ -437,5 +437,15 @@ final class domTest extends FreeSpec with Matchers { assert(div.value.className == "DIV-1") } + "CustomTag" in { + @dom def tag = { + {customTag.tagName} + } + val div = document.createElement("div") + dom.render(div, tag) + + assert(div.outerHTML == """
CUSTOM-TAG
""") + } + }