summaryrefslogtreecommitdiff
path: root/examples/demos/src
diff options
context:
space:
mode:
Diffstat (limited to 'examples/demos/src')
-rw-r--r--examples/demos/src/main/scala/scrollmenu/Controller.scala99
-rw-r--r--examples/demos/src/main/scala/scrollmenu/ScrollMenu.scala49
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