summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLi Haoyi <haoyi@dropbox.com>2014-11-13 00:56:59 -0800
committerLi Haoyi <haoyi@dropbox.com>2014-11-13 00:56:59 -0800
commit3beb1dd285d89dd4b746b1c4cd287a8748376556 (patch)
tree071d5c4fd22c83fa6889a891161b0387e1f49e06
parent7f33cb682200a501e0a48ab2fbad688317d80754 (diff)
downloadhands-on-scala-js-3beb1dd285d89dd4b746b1c4cd287a8748376556.tar.gz
hands-on-scala-js-3beb1dd285d89dd4b746b1c4cd287a8748376556.tar.bz2
hands-on-scala-js-3beb1dd285d89dd4b746b1c4cd287a8748376556.zip
Commit after converting to React.js
-rwxr-xr-xbook/src/main/resources/css/side-menu.css10
-rw-r--r--book/src/main/scala/book/Book.scala3
-rw-r--r--book/src/main/scalatex/book/Index.scalatex56
-rw-r--r--build.sbt5
-rw-r--r--examples/demos/Controller.scala214
-rw-r--r--examples/demos/build.sbt2
6 files changed, 121 insertions, 169 deletions
diff --git a/book/src/main/resources/css/side-menu.css b/book/src/main/resources/css/side-menu.css
index 8a9e25e..e5d014c 100755
--- a/book/src/main/resources/css/side-menu.css
+++ b/book/src/main/resources/css/side-menu.css
@@ -34,11 +34,11 @@ Add transition to containers so they can push in and out.
}
.hide,
.menu-item-list > li{
- -webkit-transition: height 0.2s ease-out;
- -moz-transition: height 0.2s ease-out;
- -ms-transition: height 0.2s ease-out;
- -o-transition: height 0.2s ease-out;
- transition: height 0.2s ease-out;
+ -webkit-transition: all 0.2s ease-out;
+ -moz-transition: all 0.2s ease-out;
+ -ms-transition: all 0.2s ease-out;
+ -o-transition: all 0.2s ease-out;
+ transition: all 0.2s ease-out;
}
/*
diff --git a/book/src/main/scala/book/Book.scala b/book/src/main/scala/book/Book.scala
index ea57945..857bf37 100644
--- a/book/src/main/scala/book/Book.scala
+++ b/book/src/main/scala/book/Book.scala
@@ -24,8 +24,9 @@ object Book {
"META-INF/resources/webjars/font-awesome/4.2.0/fonts/fontawesome-webfont.svg",
"META-INF/resources/webjars/font-awesome/4.2.0/fonts/fontawesome-webfont.ttf",
"META-INF/resources/webjars/font-awesome/4.2.0/fonts/fontawesome-webfont.woff",
+ "META-INF/resources/webjars/react/0.11.1/react.min.js",
"css/side-menu.css",
- "example-fastopt.js",
+ "example-opt.js",
"webpage/weather.js",
"favicon.svg",
"favicon.png"
diff --git a/book/src/main/scalatex/book/Index.scalatex b/book/src/main/scalatex/book/Index.scalatex
index 59d474e..e9a8fc8 100644
--- a/book/src/main/scalatex/book/Index.scalatex
+++ b/book/src/main/scalatex/book/Index.scalatex
@@ -20,43 +20,43 @@
@sect("Intro to Scala.js")
@Intro()
-@sect("Hands On", "Writing your first Scala.js programs")
- @p
- This half of the book walks you through various facets of the Scala.js development experience. From making your first app, to testing and publishing modules, to writing an integrated client-server application.
+ @sect("Hands On", "Writing your first Scala.js programs")
+ @p
+ This half of the book walks you through various facets of the Scala.js development experience. From making your first app, to testing and publishing modules, to writing an integrated client-server application.
- @sect("Getting Started")
- @handson.GettingStarted()
+ @sect("Getting Started")
+ @handson.GettingStarted()
- @sect("Making a Canvas App")
- @handson.CanvasApp()
+ @sect("Making a Canvas App")
+ @handson.CanvasApp()
- @sect("Interactive Web Pages")
- @handson.WebPage()
+ @sect("Interactive Web Pages")
+ @handson.WebPage()
- @sect("The Command Line")
- @handson.CommandLine()
+ @sect("The Command Line")
+ @handson.CommandLine()
- @sect("Cross Publishing Libraries")
- @handson.PublishingModules()
+ @sect("Cross Publishing Libraries")
+ @handson.PublishingModules()
- @sect("Integrating Client-Server")
- @handson.ClientServer()
+ @sect("Integrating Client-Server")
+ @handson.ClientServer()
-@sect("In Depth", "Exploring Scala.js")
- @p
- This half of the book dives into a few aspects of Scala.js much more deeply that the hands-on introduction does. It's aimed at someone who has already used Scala.js, and wants to explore the edge-cases, how things work under-the-cover, or why it has been designed in such a way. It's not a formal specification; rather, it's aim is to be a useful reference to read instead of (or in preparation for) digging into the implementation code.
+ @sect("In Depth", "Exploring Scala.js")
+ @p
+ This half of the book dives into a few aspects of Scala.js much more deeply that the hands-on introduction does. It's aimed at someone who has already used Scala.js, and wants to explore the edge-cases, how things work under-the-cover, or why it has been designed in such a way. It's not a formal specification; rather, it's aim is to be a useful reference to read instead of (or in preparation for) digging into the implementation code.
- @sect("Javascript Interoperability")
- @indepth.JavascriptInterop()
+ @sect("Javascript Interoperability")
+ @indepth.JavascriptInterop()
- @sect("Deviations from Scala-JVM")
- @indepth.SemanticDifferences()
+ @sect("Deviations from Scala-JVM")
+ @indepth.SemanticDifferences()
- @sect("The Compilation Pipeline")
- @indepth.CompilationPipeline()
+ @sect("The Compilation Pipeline")
+ @indepth.CompilationPipeline()
- @sect("Scala.js' Design Space")
- @indepth.DesignSpace()
+ @sect("Scala.js' Design Space")
+ @indepth.DesignSpace()
- @sect("Java APIs")
- @indepth.JavaAPIs() \ No newline at end of file
+ @sect("Java APIs")
+ @indepth.JavaAPIs() \ No newline at end of file
diff --git a/build.sbt b/build.sbt
index a2012d5..8bb2856 100644
--- a/build.sbt
+++ b/build.sbt
@@ -37,14 +37,15 @@ lazy val book = Project(
"org.webjars" % "highlightjs" % "8.2-1",
"org.webjars" % "pure" % "0.5.0",
"org.webjars" % "font-awesome" % "4.2.0",
+ "org.webjars" % "react" % "0.11.1",
"org.scala-lang" % "scala-reflect" % scalaVersion.value,
"org.scala-lang" % "scala-compiler" % scalaVersion.value,
"org.eclipse.jgit" % "org.eclipse.jgit" % "3.5.1.201410131835-r",
"com.lihaoyi" %%% "upickle" % "0.2.5"
),
(resources in Compile) += {
- (fastOptJS in (demos, Compile)).value
- (artifactPath in (demos, Compile, fastOptJS)).value
+ (fullOptJS in (demos, Compile)).value
+ (artifactPath in (demos, Compile, fullOptJS)).value
},
(unmanagedResourceDirectories in Compile) ++=
(unmanagedResourceDirectories in (demos, Compile)).value,
diff --git a/examples/demos/Controller.scala b/examples/demos/Controller.scala
index 0936776..e17cde9 100644
--- a/examples/demos/Controller.scala
+++ b/examples/demos/Controller.scala
@@ -1,4 +1,7 @@
+import japgolly.scalajs.react.React
+import japgolly.scalajs.react.vdom.VDomBuilder
+
import scala.scalajs.js
import scala.scalajs.js.annotation.JSExport
import org.scalajs.dom
@@ -6,15 +9,7 @@ import org.scalajs.dom.extensions._
import scala.collection.mutable
import scalatags.JsDom.all._
-object Renderer{
- import japgolly.scalajs.react._ // React
- import vdom.ReactVDom._ // Scalatags → React virtual DOM
- import vdom.ReactVDom.all._ // Scalatags html & css (div, h1, textarea, etc.)
-
- val Menu = ReactComponentB[Int]("Menu").render{ p =>
- }.build
-}
case class Tree[T](name: T, children: Vector[Tree[T]])
@JSExport
object Controller{
@@ -38,6 +33,14 @@ object Controller{
def main(data: scala.scalajs.js.Any) = {
val structure = upickle.readJs[Tree[String]](upickle.json.readJs(data))
+ var i = 0
+ def recurse(t: Tree[String]): Tree[(String, String, Int)] = {
+ val curr = (t.name, munge(t.name), i)
+ i += 1
+ val children = t.children.map(recurse)
+ Tree(curr, children)
+ }
+
val Seq(main, menu, layout, menuLink) = Seq(
"main", "menu", "layout", "menuLink"
@@ -47,43 +50,6 @@ object Controller{
snippets.foreach(js.Dynamic.global.hljs.highlightBlock(_))
- val contentTree = {
- def rec(current: Tree[String], depth: Int): Tree[dom.HTMLElement] = {
- val myCls =
- "menu-item" +
- (if (depth <= 1) " menu-item-divided" else "")
-
- val frag =
- li(
- paddingLeft := s"${depth * 15}px",
- a(
- current.name,
- href:="#"+munge(current.name),
- cls:=myCls
- )
- ).render
- Tree(frag, current.children.map(rec(_, depth + 1)))
- }
- rec(structure, 0)
- }
- val contentList = {
- def rec(current: Tree[dom.HTMLElement]): Seq[dom.HTMLElement] = {
- current.name +: current.children.flatMap(rec)
- }
- rec(contentTree).toVector
- }
-
- val frag = div(cls:="pure-menu pure-menu-open")(
- a(cls:="pure-menu-heading", href:="#")(
- "Contents"
- ),
- ul(cls:="menu-item-list")(
- contentList.drop(1)
- )
- )
- menu.appendChild(frag.render)
-
-
val headers = {
def offset(el: dom.HTMLElement, parent: dom.HTMLElement): Double = {
if (el == parent) 0
@@ -101,50 +67,33 @@ object Controller{
.toVector
}
- scrollSpy(main, headers, contentList, contentTree)
+ val menuBar = React.renderComponent(
+ Menu(recurse(structure)),
+ menu
+ )
menuLink.onclick = (e: dom.MouseEvent) => {
toggleClass(layout, "active")
toggleClass(menu, "active")
toggleClass(menuLink, "active")
}
- }
-
- /**
- * Needs to be done in a sketchy imperative fashion for performance:
- * onscroll gets called quite a lot, so any additional work makes it
- * noticeable jerky
- */
- def scrollSpy(main: dom.HTMLElement,
- headers: Vector[Double],
- contentList: Vector[dom.HTMLElement],
- contentTree: Tree[dom.HTMLElement]) = {
-
-
var scrolling = false
- var lastIndex = -1
- def run() = {
- scrolling = false
- val threshold = main.scrollTop + main.clientHeight
-
- var index = 0
- while(index < headers.length && index >= 0){
- index += 1
- if (headers(index) > threshold) index *= -1
- }
- index = -index
-
- if (index != lastIndex){
- updateSideBar(lastIndex, index, contentList, contentTree)
- lastIndex = index
- }
- }
- run()
main.onscroll = (e: dom.UIEvent) => {
if (!scrolling){
scrolling = true
- dom.requestAnimationFrame((d: Double) => run())
+ dom.requestAnimationFrame{(d: Double) =>
+ scrolling = false
+ val threshold = main.scrollTop + main.clientHeight
+
+ var index = 0
+ while(index < headers.length && index >= 0){
+ index += 1
+ if (headers(index) > threshold) index *= -1
+ }
+ index = -index
+ menuBar.setState(index)
+ }
}
}
}
@@ -152,64 +101,65 @@ object Controller{
val rect = el.getBoundingClientRect()
rect.top >= 0 && rect.bottom <= dom.innerHeight
}
- val lastShown = new js.Array[dom.HTMLElement](0)
- val lastLined = new js.Array[dom.HTMLElement](0)
- def updateSideBar(lastIndex: Int,
- index: Int,
- contentList: Vector[dom.HTMLElement],
- contentTree: Tree[dom.HTMLElement]) = {
-
- println(s"MOVING $lastIndex -> $index")
- if (!isElementInViewport(contentList(index))) {
- contentList(index).scrollIntoView(lastIndex > index)
- }
- val shown = new js.Array[dom.HTMLElement](0)
- val lined = new js.Array[dom.HTMLElement](0)
- /**
- * Makes two passes over the children list; once to determine if
- * the current element is a parent of the current header, and another
- * to mark all the children of the current element with the correct
- * CSS classes.
- */
- def rec(curr: Tree[dom.HTMLElement]): Boolean = {
-
- var found = false
+
+ import japgolly.scalajs.react._ // React
+ import vdom.ReactVDom._ // Scalatags → React virtual DOM
+ import vdom.ReactVDom.all._ // Scalatags html & css (div, h1, textarea, etc.)
+
+ val Menu = ReactComponentB[Tree[(String, String, Int)]]("Menu")
+ .getInitialState(_ => 0)
+ .render{ (structure, _, index) =>
+
+ val contentList = {
var i = 0
- var j = 0
- while (j < curr.children.length){
- val x = curr.children(j)
- j+= 1
- val f = rec(x)
- found |= f
- if (!found) i += 1
+ def rec1(current: Tree[(String, String, Int)]): Tree[(String, String, Int, Boolean)] = {
+ val initialI = i
+ i += 1
+ val children = current.children.map(rec1)
+
+ val win = i >= index && initialI < index
+ Tree(
+ (current.name._1, current.name._2, current.name._3, win),
+ children
+ )
}
+ def rec(current: Tree[(String, String, Int, Boolean)],
+ depth: Int,
+ classes: String): Iterator[Tag] = {
- if (found || curr.name == contentList(index)){
- var j = 0
- while (j < curr.children.length){
- val x = curr.children(j)
- if (found && i > 0){
- lined.push(x.name)
- i -= 1
- }
+ val winIndex = current.children.indexWhere(_.name._4)
+ val (before, after) = current.children.splitAt(winIndex)
- j+= 1
- shown.push(x.name)
- }
- lined.push(curr.name)
- true
- }else false
+ val myCls =
+ "menu-item" +
+ (if (depth <= 1) " menu-item-divided" else "")
+ val (name, munged, currIndex, win) = current.name
+ val frag =
+ li(paddingLeft := s"${depth * 15}px")(
+ a(
+ name,
+ href:="#"+munged,
+ cls:=myCls
+ ),
+ cls:=classes + (if (win) " pure-menu-selected" else "")
+ )
+ val afterCls = if (!win) "hide" else ""
+ Iterator(frag) ++
+ before.flatMap(rec(_, depth+1, "lined")) ++
+ after.flatMap(rec(_, depth+1, afterCls))
+ }
+ rec(rec1(structure), 0, "")
}
- rec(contentTree)
- for(el <- contentList){
- if (shown.indexOf(el) != -1) removeClass(el, "hide")
- else addClass(el, "hide")
- if (lined.indexOf(el) == -1) removeClass(el, "lined")
- else addClass(el, "lined")
- }
- if (lastIndex != -1) removeClass(contentList(lastIndex), "pure-menu-selected")
- addClass(contentList(index), "pure-menu-selected")
- }
-}
+ val frag = div(cls:="pure-menu pure-menu-open")(
+ a(cls:="pure-menu-heading", href:="#")(
+ "Contents"
+ ),
+ ul(cls:="menu-item-list")(
+ contentList.drop(1).toVector
+ )
+ )
+ frag
+ }.build
+} \ No newline at end of file
diff --git a/examples/demos/build.sbt b/examples/demos/build.sbt
index 9be4d4d..040f9fd 100644
--- a/examples/demos/build.sbt
+++ b/examples/demos/build.sbt
@@ -10,7 +10,7 @@ scalaVersion := "2.11.4"
libraryDependencies += "com.github.japgolly.scalajs-react" %%% "core" % "0.5.1"
-jsDependencies += "org.webjars" % "react" % "0.11.1" / "react-with-addons.js" commonJSName "React"
+
libraryDependencies += "com.lihaoyi" %%% "upickle" % "0.2.5"