Skip to content

Commit 3c154e2

Browse files
authored
Merge pull request #514 from japgolly/topic/vdom-revision
VDOM revision
2 parents 1fe2338 + 9f00b1f commit 3c154e2

File tree

18 files changed

+248
-183
lines changed

18 files changed

+248
-183
lines changed

core/src/main/scala/japgolly/scalajs/react/component/Generic.scala

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,14 @@ object Generic {
5858
type Raw <: RAW.React.Element
5959
val raw: Raw
6060
def displayName: String
61+
62+
final def vdomElement: vdom.VdomElement =
63+
vdom.VdomElement(raw)
6164
}
6265

66+
implicit def unmountedRawToVdomElement(u: UnmountedRaw): vdom.VdomElement =
67+
u.vdomElement
68+
6369
trait UnmountedSimple[P, M] extends UnmountedRaw {
6470
final type Props = P
6571
final type Mounted = M
@@ -68,7 +74,6 @@ object Generic {
6874
def mapUnmountedProps[P2](f: P => P2): UnmountedSimple[P2, M]
6975
def mapMounted[M2](f: M => M2): UnmountedSimple[P, M2]
7076

71-
def vdomElement: vdom.VdomElement
7277
def key: Option[Key]
7378
def ref: Option[Ref]
7479
def props: Props

core/src/main/scala/japgolly/scalajs/react/component/Js.scala

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,6 @@ object Js extends JsBaseComponentTemplate[RAW.React.ComponentClassP] {
6767
override def root = this
6868
override val raw = r
6969
override val mountRaw = m.asInstanceOf[RAW.React.ComponentUntyped => M] // TODO Do better
70-
override val vdomElement = vdom.VdomElement(raw)
7170
override def key = jsNullToOption(raw.key)
7271
override def ref = jsNullToOption(raw.ref)
7372
override def props = raw.props
@@ -83,7 +82,6 @@ object Js extends JsBaseComponentTemplate[RAW.React.ComponentClassP] {
8382
override def root = from.root
8483
override def props = mp(from.props)
8584
override val raw = from.raw
86-
override def vdomElement = from.vdomElement
8785
override def key = from.key
8886
override def ref = from.ref
8987
override def propsChildren = from.propsChildren

core/src/main/scala/japgolly/scalajs/react/component/JsFn.scala

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,6 @@ object JsFn extends JsBaseComponentTemplate[RAW.React.StatelessFunctionalCompone
140140
override def root = this
141141
override val raw = r
142142
override val mountRaw = constUnit
143-
override val vdomElement = vdom.VdomElement(raw)
144143
override def key = jsNullToOption(raw.key)
145144
override def ref = None // orNullToOption(raw.ref)
146145
override def props = raw.props.asInstanceOf[P]
@@ -153,7 +152,6 @@ object JsFn extends JsBaseComponentTemplate[RAW.React.StatelessFunctionalCompone
153152
override def root = from.root
154153
override val raw = from.raw
155154
override val mountRaw = mm compose from.mountRaw
156-
override def vdomElement = from.vdomElement
157155
override def key = from.key
158156
override def ref = from.ref
159157
override def props = mp(from.props)

core/src/main/scala/japgolly/scalajs/react/component/JsForwardRef.scala

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,6 @@ object JsForwardRef {
148148
override def root = this
149149
override val raw = r
150150
override val mountRaw = constUnit
151-
override val vdomElement = vdom.VdomElement(raw)
152151
override def key = jsNullToOption(raw.key)
153152
override def ref = jsNullToOption(raw.ref).map(r => Ref.fromJs(r.asInstanceOf[Raw.React.RefHandle[R]]))
154153
override def props = raw.props.asInstanceOf[P]
@@ -161,7 +160,6 @@ object JsForwardRef {
161160
override def root = from.root
162161
override val raw = from.raw
163162
override val mountRaw = mm compose from.mountRaw
164-
override def vdomElement = from.vdomElement
165163
override def key = from.key
166164
override def ref = from.ref
167165
override def props = mp(from.props)

core/src/main/scala/japgolly/scalajs/react/internal/package.scala

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package japgolly.scalajs.react
22

3+
import scala.collection.generic.CanBuildFrom
4+
import scala.collection.mutable.Builder
35
import scala.util.{Failure, Success, Try}
46

57
package object internal {
@@ -16,4 +18,21 @@ package object internal {
1618
def catchAll[A](a: => A): Try[A] =
1719
try Success(a)
1820
catch {case t: Throwable => Failure(t) }
21+
22+
def intercalateTo[F[_], A](as: Iterator[A], sep: A)(implicit cbf: CanBuildFrom[Nothing, A, F[A]]): F[A] = {
23+
val b = cbf.apply()
24+
intercalateInto(b, as, sep)
25+
b.result()
26+
}
27+
28+
def intercalateInto[A](b: Builder[A, _], it: Iterator[A], sep: A): Unit = {
29+
if (it.hasNext) {
30+
val first = it.next()
31+
b += first
32+
for (a <- it) {
33+
b += sep
34+
b += a
35+
}
36+
}
37+
}
1938
}

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

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,10 +114,16 @@ object Builder {
114114

115115
class ToVdomElement extends ToJs {
116116
def render(tag: String): VdomElement = {
117+
val r = (new ToRawReactElement).render(tag)
118+
VdomElement(r)
119+
}
120+
}
121+
122+
class ToRawReactElement extends ToJs {
123+
def render(tag: String): raw.React.Element = {
117124
addClassNameToProps()
118125
addStyleToProps()
119-
val e = ToRawReactElement.build(tag, props, key, children)
120-
VdomElement(e)
126+
ToRawReactElement.build(tag, props, key, children)
121127
}
122128
}
123129

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

Lines changed: 29 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -16,41 +16,45 @@ abstract class Exports {
1616
private[vdom] implicit final def VdomExtString(a: String): ExportsStatic.VdomExtString =
1717
new ExportsStatic.VdomExtString(a)
1818

19-
final type VdomNode = japgolly.scalajs.react.vdom.VdomNode
20-
final val VdomNode = japgolly.scalajs.react.vdom.VdomNode
19+
final val EmptyVdom: VdomNode = japgolly.scalajs.react.vdom.VdomNode.empty
2120

22-
final type VdomElement = japgolly.scalajs.react.vdom.VdomElement
23-
final val VdomElement = japgolly.scalajs.react.vdom.VdomElement
21+
@inline
22+
final def HtmlTag(name: String): HtmlTag = HtmlTagOf(name)
23+
final type HtmlTag = HtmlTagOf[HtmlTopNode]
24+
final type HtmlTagOf[+N <: HtmlTopNode] = japgolly.scalajs.react.vdom.HtmlTagOf[N]
25+
final val HtmlTagOf = japgolly.scalajs.react.vdom.HtmlTagOf
2426

2527
@deprecated("Use React.Fragment", "1.3.0")
26-
final val ReactFragment = japgolly.scalajs.react.feature.ReactFragment
28+
final val ReactFragment = japgolly.scalajs.react.feature.ReactFragment
2729

28-
final val ReactPortal = japgolly.scalajs.react.vdom.ReactPortal
30+
final val ReactPortal = japgolly.scalajs.react.vdom.ReactPortal
2931

30-
final type VdomTagOf[+N <: TopNode] = japgolly.scalajs.react.vdom.TagOf[N]
31-
final type VdomTag = VdomTagOf[TopNode]
32+
@inline
33+
final def SvgTag(name: String): SvgTag = SvgTagOf(name)
34+
final type SvgTag = SvgTagOf[SvgTopNode]
35+
final type SvgTagOf[+N <: SvgTopNode] = japgolly.scalajs.react.vdom.SvgTagOf[N]
36+
final val SvgTagOf = japgolly.scalajs.react.vdom.SvgTagOf
3237

33-
final type HtmlTagOf[+N <: HtmlTopNode] = japgolly.scalajs.react.vdom.HtmlTagOf[N]
34-
final val HtmlTagOf = japgolly.scalajs.react.vdom.HtmlTagOf
35-
final type HtmlTag = HtmlTagOf[HtmlTopNode]
36-
@inline final def HtmlTag(name: String): HtmlTag = HtmlTagOf(name)
38+
/** Tag modifier.
39+
* Apply it to a [[VdomTag]]. */
40+
final type TagMod = japgolly.scalajs.react.vdom.TagMod
41+
final val TagMod = japgolly.scalajs.react.vdom.TagMod
3742

38-
final type SvgTagOf[+N <: SvgTopNode] = japgolly.scalajs.react.vdom.SvgTagOf[N]
39-
final val SvgTagOf = japgolly.scalajs.react.vdom.SvgTagOf
40-
final type SvgTag = SvgTagOf[SvgTopNode]
41-
@inline final def SvgTag(name: String): SvgTag = SvgTagOf(name)
43+
final type VdomAttr[-U] = japgolly.scalajs.react.vdom.Attr[U]
44+
final val VdomAttr = japgolly.scalajs.react.vdom.Attr
4245

43-
/** Tag modification. Apply it to a [[VdomTag]]. */
44-
final type TagMod = japgolly.scalajs.react.vdom.TagMod
45-
final val TagMod = japgolly.scalajs.react.vdom.TagMod
46-
final val EmptyVdom = japgolly.scalajs.react.vdom.VdomNode.empty
46+
final type VdomArray = japgolly.scalajs.react.vdom.VdomArray
47+
final val VdomArray = japgolly.scalajs.react.vdom.VdomArray
4748

48-
final type VdomAttr[-U] = japgolly.scalajs.react.vdom.Attr[U]
49-
final val VdomAttr = japgolly.scalajs.react.vdom.Attr
49+
final type VdomElement = japgolly.scalajs.react.vdom.VdomElement
50+
final val VdomElement = japgolly.scalajs.react.vdom.VdomElement
5051

51-
final val VdomStyle = japgolly.scalajs.react.vdom.Style
52+
final type VdomNode = japgolly.scalajs.react.vdom.VdomNode
53+
final val VdomNode = japgolly.scalajs.react.vdom.VdomNode
5254

53-
final type VdomArray = japgolly.scalajs.react.vdom.VdomArray
54-
final val VdomArray = japgolly.scalajs.react.vdom.VdomArray
55+
final val VdomStyle = japgolly.scalajs.react.vdom.Style
56+
57+
final type VdomTag = VdomTagOf[TopNode]
58+
final type VdomTagOf[+N <: TopNode] = japgolly.scalajs.react.vdom.TagOf[N]
5559

5660
}

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

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -291,18 +291,18 @@ trait HtmlAttrs {
291291
private def classSetImpl(z: TagMod, ps: Seq[(String, Boolean)]): TagMod =
292292
ps.foldLeft(z)((q, p) =>
293293
if (p._2)
294-
q(cls := p._1)
294+
TagMod(q, cls := p._1)
295295
else
296296
q)
297297

298298
final def classSet(ps: (String, Boolean)*): TagMod =
299-
classSetImpl(EmptyVdom, ps)
299+
classSetImpl(TagMod.empty, ps)
300300

301301
final def classSet1(a: String, ps: (String, Boolean)*): TagMod =
302302
classSetImpl(cls := a, ps)
303303

304304
final def classSetM(ps: Map[String, Boolean]): TagMod =
305-
classSetImpl(EmptyVdom, ps.toSeq)
305+
classSetImpl(TagMod.empty, ps.toSeq)
306306

307307
final def classSet1M(a: String, ps: Map[String, Boolean]): TagMod =
308308
classSetImpl(cls := a, ps.toSeq)
@@ -313,6 +313,7 @@ trait HtmlAttrs {
313313
* default value is 20 (HTML5).
314314
*/
315315
final def cols = VdomAttr("cols")
316+
316317
/**
317318
* This attribute gives the value associated with the http-equiv or name
318319
* attribute, depending of the context.

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

Lines changed: 27 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ package japgolly.scalajs.react.vdom
22

33
import japgolly.scalajs.react.{PropsChildren, raw}
44
import japgolly.scalajs.react.component.Generic
5-
import japgolly.scalajs.react.internal.OptionLike
5+
import japgolly.scalajs.react.feature.ReactFragment
6+
import japgolly.scalajs.react.internal._
67
import scala.scalajs.js
7-
import Exports.VdomTag
88

99
// =====================================================================================================================
1010

@@ -64,15 +64,37 @@ trait ImplicitsForTagMod {
6464
// =====================================================================================================================
6565

6666
object ImplicitsForVdomNode {
67+
6768
final class TraversableOnceExt[A](private val as: TraversableOnce[A]) extends AnyVal {
6869

70+
/** Like `.mkString(String)` in Scala stdlib. */
71+
def mkReactFragment(sep: VdomNode)(implicit f: A => VdomNode): VdomElement =
72+
mkReactFragment(VdomNode.empty, sep, VdomNode.empty)
73+
74+
/** Like `.mkString(String, String, String)` in Scala stdlib. */
75+
def mkReactFragment(start: VdomNode, sep: VdomNode, end: VdomNode)(implicit f: A => VdomNode): VdomElement = {
76+
val b = List.newBuilder[VdomNode]
77+
if (start ne VdomNode.empty) b += start
78+
intercalateInto(b, as.toIterator.map(f), sep)
79+
if (end ne VdomNode.empty) b += end
80+
ReactFragment(b.result(): _*)
81+
}
82+
6983
/** Like `.mkString(String)` in Scala stdlib. */
7084
def mkTagMod(sep: TagMod)(implicit f: A => TagMod): TagMod =
71-
TagMod.intercalate(as.toIterator.map(f), sep)
85+
mkTagMod(VdomNode.empty, sep, VdomNode.empty)
7286

7387
/** Like `.mkString(String, String, String)` in Scala stdlib. */
74-
def mkTagMod(start: TagMod, sep: TagMod, end: TagMod)(implicit f: A => TagMod): TagMod =
75-
TagMod.Composite(Vector.empty :+ start :+ mkTagMod(sep) :+ end)
88+
def mkTagMod(start: TagMod, sep: TagMod, end: TagMod)(implicit f: A => TagMod): TagMod = {
89+
val b = Vector.newBuilder[TagMod]
90+
if (start ne VdomNode.empty) b += start
91+
intercalateInto(b, as.toIterator.map(f), sep)
92+
if (end ne VdomNode.empty) b += end
93+
TagMod.fromTraversableOnce(b.result())
94+
}
95+
96+
def toReactFragment(implicit f: A => VdomNode): VdomElement =
97+
ReactFragment(as.toIterator.map(f).toList: _*)
7698

7799
def toTagMod(implicit f: A => TagMod): TagMod =
78100
TagMod.fromTraversableOnce(as.toIterator.map(f))
@@ -105,21 +127,10 @@ trait ImplicitsForVdomNode {
105127

106128
// =====================================================================================================================
107129

108-
trait ImplicitsForVdomElement {
109-
implicit def vdomElementFromTag[A](a: A)(implicit f: A => VdomTag): VdomElement =
110-
f(a).render
111-
112-
implicit def vdomElementFromComponent(u: Generic.UnmountedWithRoot[_, _, _, _]): VdomElement =
113-
u.vdomElement
114-
}
115-
116-
// =====================================================================================================================
117-
118130
trait Implicits
119131
extends ImplicitsForTagMod
120132
with ImplicitsForVdomAttr
121133
with ImplicitsForVdomNode
122-
with ImplicitsForVdomElement
123134

124135
object Implicits extends Implicits
125136

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

Lines changed: 20 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
package japgolly.scalajs.react.vdom
22

3-
import japgolly.scalajs.react.{Callback, Ref, raw => Raw}
3+
import japgolly.scalajs.react.{Ref, raw => Raw}
4+
import scala.scalajs.js
45

56
class TagOf[+N <: TopNode] private[vdom](final val tag: String,
67
final protected val modifiers: List[Seq[TagMod]],
7-
final val namespace: Namespace) extends TagMod {
8+
final val namespace: Namespace) extends VdomElement {
89

910
@deprecated("Use .withRef instead", "1.2.0")
1011
def ref[NN >: N <: TopNode](r: Ref.Set[NN]): TagOf[NN] =
@@ -16,55 +17,44 @@ class TagOf[+N <: TopNode] private[vdom](final val tag: String,
1617
case None => this
1718
}
1819

19-
override def apply(xs: TagMod*): TagOf[N] =
20+
def apply(xs: TagMod*): TagOf[N] =
2021
copy(modifiers = xs :: modifiers)
2122

2223
protected def copy(tag: String = this.tag,
2324
modifiers: List[Seq[TagMod]] = this.modifiers,
2425
namespace: Namespace = this.namespace): TagOf[N] =
2526
new TagOf(tag, modifiers, namespace)
2627

27-
/**
28-
* Walks the [[modifiers]] to apply them to a particular [[Builder]].
29-
* Super sketchy/procedural for max performance.
30-
*/
31-
private[this] def build(b: Builder): Unit = {
32-
var current = modifiers
33-
val arr = new Array[Seq[TagMod]](modifiers.length)
28+
@deprecated("Tags now extend VdomElement. Calling .render does nothing.", "1.4.0")
29+
def render: VdomElement = this
30+
31+
override lazy val rawElement: Raw.React.Element = {
32+
val b = new Builder.ToRawReactElement()
3433

35-
var i = 0
34+
val arr = new js.Array[Seq[TagMod]]
35+
var current = modifiers
3636
while (current != Nil) {
37-
arr(i) = current.head
37+
arr.push(current.head)
3838
current = current.tail
39-
i += 1
4039
}
41-
4240
var j = arr.length
4341
while (j > 0) {
4442
j -= 1
45-
val frag = arr(j)
46-
var i = 0
47-
while (i < frag.length) {
48-
frag(i).applyTo(b)
49-
i += 1
50-
}
43+
arr(j).foreach(_.applyTo(b))
5144
}
52-
}
5345

54-
lazy val render: VdomElement = {
55-
val b = new Builder.ToVdomElement()
56-
build(b)
5746
b.render(tag)
5847
}
5948

60-
override def toString =
61-
render.toString
62-
6349
override def applyTo(b: Builder): Unit =
64-
b.appendChild(render.rawElement)
50+
b.appendChild(rawElement)
51+
52+
override def toString =
53+
if (modifiers.isEmpty)
54+
s"<$tag />"
55+
else
56+
s"<$tag>…</$tag>"
6557

66-
def renderIntoDOM(container: Raw.ReactDOM.Container, callback: Callback = Callback.empty) =
67-
render.renderIntoDOM(container, callback)
6858
}
6959

7060
object TagOf {

0 commit comments

Comments
 (0)