Skip to content

Commit f0dd44d

Browse files
committed
Support custom tags, attributes and styles.
Fixes #61
1 parent 07c4174 commit f0dd44d

File tree

6 files changed

+36
-16
lines changed

6 files changed

+36
-16
lines changed

README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,15 @@ Extensions
231231
```scala
232232
div(if (allowEdit) editButton else EmptyTag)
233233
```
234+
* Custom tags, attributes and styles.
235+
```scala
236+
val a = "customAttr" .reactAttr
237+
val s = "customStyle".reactStyle
238+
val t = "customTag" .reactTag
239+
240+
// <customTag customAttr="hello" style="customStyle:123;">bye</customTag>
241+
t(a := "hello", s := "123", "bye")
242+
```
234243

235244
#### React
236245
* Where `this.setState(State)` is applicable, you can also run `modState(State => State)`.

core/src/main/scala/japgolly/scalajs/react/vdom/Extra.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,12 @@ object Extra {
2727
@inline def ?=(m: => TagMod): TagMod = if (_b) m else EmptyTag
2828
}
2929

30+
final class StringExt(val _s: String) extends AnyVal {
31+
@inline def reactAttr : Attr = Attr(_s)
32+
@inline def reactStyle: Style = Style(_s, _s)
33+
@inline def reactTag : ReactTag = makeAbstractReactTag(_s, Scalatags.NamespaceHtml.implicitNamespace)
34+
}
35+
3036
trait Tags {
3137
import NamespaceHtml._
3238
final val big = "big".tag[dom.HTMLHtmlElement]

core/src/main/scala/japgolly/scalajs/react/vdom/Implicits.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ abstract class Implicits extends LowPri {
6969
// Extensions
7070
@inline implicit final def _react_ext_attr(a: Attr) = new Extra.AttrExt(a)
7171
@inline implicit final def _react_ext_bool(a: Boolean) = new Extra.BooleanExt(a)
72+
@inline implicit final def _react_ext_str (a: String) = new Extra.StringExt(a)
7273
}
7374

7475
object Implicits extends Implicits

core/src/main/scala/japgolly/scalajs/react/vdom/Scalatags.scala

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ trait TagMod {
2929

3030
final case class ReactTag private[vdom](tag: String,
3131
modifiers: List[Seq[TagMod]],
32-
void: Boolean,
3332
namespace: Namespace) extends DomFrag {
3433

3534
def render: ReactElement = {
@@ -66,7 +65,7 @@ final case class ReactTag private[vdom](tag: String,
6665
}
6766

6867
def apply(xs: TagMod*): ReactTag =
69-
this.copy(tag = tag, void = void, modifiers = xs :: modifiers)
68+
this.copy(tag = tag, modifiers = xs :: modifiers)
7069

7170
override def toString = render.toString
7271
}
@@ -84,6 +83,7 @@ final case class Attr(name: String) {
8483
* Wraps up a CSS style in a value.
8584
*/
8685
case class Style(jsName: String, cssName: String) {
86+
// val s2 = camelCase(s); Style(s2, s2)
8787
def :=[T](v: T)(implicit ev: StyleValue[T]): TagMod = StylePair(this, v, ev)
8888
}
8989

@@ -189,8 +189,10 @@ private[vdom] object Scalatags {
189189
override def apply(b: Builder, s: Style, t: T[A]) = ot.foreach(t)(v(b, s, _))
190190
}
191191

192-
@inline def makeAbstractReactTag(tag: String, void: Boolean, namespaceConfig: Namespace): ReactTag =
193-
ReactTag(tag, Nil, void, namespaceConfig)
192+
@inline def makeAbstractReactTag(tag: String, namespaceConfig: Namespace): ReactTag = {
193+
Escaping.assertValidTag(tag)
194+
ReactTag(tag, Nil, namespaceConfig)
195+
}
194196

195197
implicit final class SeqFrag[A <% Frag](xs: Seq[A]) extends Frag {
196198
def applyTo(t: Builder): Unit = xs.foreach(_.applyTo(t))
@@ -229,19 +231,15 @@ private[vdom] object Scalatags {
229231
/**
230232
* Converts the string to a [[ReactTag]]
231233
*/
232-
def tag[N <: dom.Node](implicit namespaceConfig: Namespace): ReactTag = {
233-
Escaping.assertValidTag(s)
234-
makeAbstractReactTag(s, false, namespaceConfig)
235-
}
234+
def tag[N <: dom.Node](implicit namespaceConfig: Namespace): ReactTag =
235+
makeAbstractReactTag(s, namespaceConfig)
236236

237237
/**
238238
* Converts the string to a void [[ReactTag]]; that means that they cannot
239239
* contain any content, and can be rendered as self-closing tags.
240240
*/
241-
def voidTag[N <: dom.Node](implicit namespaceConfig: Namespace): ReactTag = {
242-
Escaping.assertValidTag(s)
243-
makeAbstractReactTag(s, true, namespaceConfig)
244-
}
241+
def voidTag[N <: dom.Node](implicit namespaceConfig: Namespace): ReactTag =
242+
makeAbstractReactTag(s, namespaceConfig)
245243

246244
/**
247245
* Converts the string to a [[Attr]]
@@ -253,10 +251,7 @@ private[vdom] object Scalatags {
253251
* style, and the jsName of the style is generated by converted the dashes
254252
* to camelcase.
255253
*/
256-
def style = {
257-
// val s2 = camelCase(s); Style(s2, s2)
258-
Style(s, s)
259-
}
254+
def style = Style(s, s)
260255
}
261256

262257
/**

doc/CHANGELOG-0.7.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
# 0.7.1 ([commit log](https://github.com/japgolly/scalajs-react/compare/v0.7.0...v0.7.1))
2+
3+
* Support custom tags, attributes and styles via `"string".react{Attr,Style,Tag}`.
4+
5+
16
# 0.7.0 ([commit log](https://github.com/japgolly/scalajs-react/compare/v0.6.1...v0.7.0))
27

38
To ease migration, here is a script that perform 98% of the required changes for you:

test/src/test/scala/japgolly/scalajs/react/CoreTest.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,10 @@ object CoreTest extends TestSuite {
156156
a(tabIndex := 1, cls := "lol")(href := "boo", alt := "g"),
157157
"""<a tabindex="1" class="lol" href="boo" alt="g"></a>""")
158158
}
159+
160+
'customAttr - test(div("accept".reactAttr := "yay"), """<div accept="yay"></div>""")
161+
'customStyle - test(div("face".reactStyle := "yay"), """<div style="face:yay;"></div>""")
162+
'customTag - test(("ass".reactTag)("Snicker"), """<ass>Snicker</ass>""")
159163
}
160164

161165
'classSet {

0 commit comments

Comments
 (0)