diff options
author | Li Haoyi <haoyi@dropbox.com> | 2014-11-14 16:05:31 -0800 |
---|---|---|
committer | Li Haoyi <haoyi@dropbox.com> | 2014-11-14 16:05:31 -0800 |
commit | 50595f9e1b05d33f47e540c24e33e50bd1625315 (patch) | |
tree | a3a982c44e6c24d8bd1278d4dc030d7b86f58b41 /examples/demos/src/main/scala | |
parent | 64b283eb61f2c20e39e4c55e8696522569420028 (diff) | |
download | hands-on-scala-js-50595f9e1b05d33f47e540c24e33e50bd1625315.tar.gz hands-on-scala-js-50595f9e1b05d33f47e540c24e33e50bd1625315.tar.bz2 hands-on-scala-js-50595f9e1b05d33f47e540c24e33e50bd1625315.zip |
Moved some stuff around
Diffstat (limited to 'examples/demos/src/main/scala')
-rw-r--r-- | examples/demos/src/main/scala/scrollmenu/Controller.scala | 99 | ||||
-rw-r--r-- | examples/demos/src/main/scala/scrollmenu/ScrollMenu.scala | 49 |
2 files changed, 148 insertions, 0 deletions
diff --git a/examples/demos/src/main/scala/scrollmenu/Controller.scala b/examples/demos/src/main/scala/scrollmenu/Controller.scala new file mode 100644 index 0000000..1fcb68c --- /dev/null +++ b/examples/demos/src/main/scala/scrollmenu/Controller.scala @@ -0,0 +1,99 @@ +package scrollmenu + +import org.scalajs.dom +import org.scalajs.dom.extensions._ + +import scala.scalajs.js +import scala.scalajs.js.annotation.JSExport +import scalatags.JsDom.all._ + + + +@JSExport +object Controller{ + + def munge(name: String) = { + name.replace(" ", "") + } + + @JSExport + 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], depth: Int): Tree[MenuNode] = { + val curr = + li( + a( + t.value, + href:="#"+munge(t.value), + cls:="menu-item" + ) + ) + val originalI = i + i += 1 + val children = t.children.map(recurse(_, depth + 1)) + Tree( + MenuNode( + curr(ul(paddingLeft := "15px",children.map(_.value.frag))).render, + originalI, + if (children.length > 0) children.map(_.value.end).max else originalI + 1 + ), + children + ) + } + + val Seq(main, menu, layout, menuLink) = Seq( + "main", "menu", "layout", "menuLink" + ).map(dom.document.getElementById) + + val snippets = dom.document.getElementsByClassName("highlight-me") + + snippets.foreach(js.Dynamic.global.hljs.highlightBlock(_)) + def offset(el: dom.HTMLElement, parent: dom.HTMLElement): Double = { + if (el == parent) 0 + else el.offsetTop + offset(el.offsetParent.asInstanceOf[dom.HTMLElement], parent) + } + val headers = { + + val menuItems = { + def rec(current: Tree[String]): Seq[String] = { + current.value +: current.children.flatMap(rec) + } + rec(structure).tail + } + menuItems.map(munge) + .map(dom.document.getElementById) + .map(offset(_, main)) + .toVector + } + + val domTrees = structure.children.map(recurse(_, 0)) + val scrollSpy = new ScrollSpy(headers, domTrees) + + menu.appendChild( + div(cls:="pure-menu pure-menu-open")( + a(cls:="pure-menu-heading", href:="#")( + "Contents" + ), + ul(cls:="menu-item-list")( + domTrees.map(_.value.frag) + ) + ).render + ) + + menuLink.onclick = (e: dom.MouseEvent) => { + layout.classList.toggle("active") + menu.classList.toggle("active") + menuLink.classList.toggle("active") + } + + main.onscroll = (e: dom.UIEvent) => { + scrollSpy(main.scrollTop + main.clientHeight) + } + } + def isElementInViewport(el: dom.HTMLElement) = { + val rect = el.getBoundingClientRect() + rect.top >= 0 && rect.bottom <= dom.innerHeight + } +} diff --git a/examples/demos/src/main/scala/scrollmenu/ScrollMenu.scala b/examples/demos/src/main/scala/scrollmenu/ScrollMenu.scala new file mode 100644 index 0000000..9c0fbd2 --- /dev/null +++ b/examples/demos/src/main/scala/scrollmenu/ScrollMenu.scala @@ -0,0 +1,49 @@ +package scrollmenu + +import org.scalajs.dom + +case class Tree[T](value: T, children: Vector[Tree[T]]) + +case class MenuNode(frag: dom.HTMLElement, start: Int, end: Int) + +/** + * High performance scrollspy to work keep the left menu bar in sync. + * Lots of sketchy imperative code in order to maximize performance. + */ +class ScrollSpy(headers: Vector[Double], domTrees: Seq[Tree[MenuNode]]){ + var scrolling = false + def apply(threshold: Double) = if (!scrolling){ + scrolling = true + dom.requestAnimationFrame((d: Double) => start(threshold)) + } + def start(threshold: Double) = { + scrolling = false + def walkTree(tree: Tree[MenuNode]): Boolean = { + val Tree(MenuNode(menuItem, index, next), children) = tree + val before = headers(index) < threshold + val after = (next >= headers.length) || headers(next) > threshold + val win = before && after + if (win){ + menuItem.classList.remove("hide") + var winFound = false + + for(c <- tree.children){ + val newWinFound = walkTree(c) + if (!winFound) c.value.frag.classList.add("selected") + else c.value.frag.classList.remove("selected") + winFound = winFound | newWinFound + } + if (!winFound) { + tree.children.foreach(_.value.frag.classList.remove("selected")) + } + menuItem.children(0).classList.add("pure-menu-selected") + }else{ + menuItem.children(0).classList.remove("pure-menu-selected") + menuItem.classList.add("hide") + menuItem.classList.remove("selected") + } + win + } + domTrees.map(walkTree) + } +}
\ No newline at end of file |