From 70f9164e167398d75f99130a5325a3411215feb5 Mon Sep 17 00:00:00 2001 From: Li Haoyi Date: Sat, 15 Nov 2014 15:59:38 -0800 Subject: works --- .../src/main/scala/scrollmenu/Controller.scala | 49 +------------- .../src/main/scala/scrollmenu/ScrollMenu.scala | 74 +++++++++++++++++++--- 2 files changed, 69 insertions(+), 54 deletions(-) (limited to 'examples') diff --git a/examples/demos/src/main/scala/scrollmenu/Controller.scala b/examples/demos/src/main/scala/scrollmenu/Controller.scala index 1fcb68c..de120c6 100644 --- a/examples/demos/src/main/scala/scrollmenu/Controller.scala +++ b/examples/demos/src/main/scala/scrollmenu/Controller.scala @@ -20,28 +20,6 @@ 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], 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" @@ -50,26 +28,8 @@ object Controller{ 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) + val scrollSpy = new ScrollSpy(structure, main) menu.appendChild( div(cls:="pure-menu pure-menu-open")( @@ -77,7 +37,7 @@ object Controller{ "Contents" ), ul(cls:="menu-item-list")( - domTrees.map(_.value.frag) + scrollSpy.domTrees.map(_.value.frag) ) ).render ) @@ -92,8 +52,5 @@ object Controller{ 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 index 9c0fbd2..7b238f4 100644 --- a/examples/demos/src/main/scala/scrollmenu/ScrollMenu.scala +++ b/examples/demos/src/main/scala/scrollmenu/ScrollMenu.scala @@ -2,6 +2,8 @@ package scrollmenu import org.scalajs.dom +import scalatags.JsDom.all._ + case class Tree[T](value: T, children: Vector[Tree[T]]) case class MenuNode(frag: dom.HTMLElement, start: Int, end: Int) @@ -10,18 +12,69 @@ 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){ +class ScrollSpy(structure: Tree[String], main: dom.HTMLElement){ + val (headers, domTrees) = { + var i = 0 + def recurse(t: Tree[String], depth: Int): Tree[MenuNode] = { + val curr = + li( + a( + t.value, + href:="#"+Controller.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 + ) + } + 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(Controller.munge) + .map(dom.document.getElementById) + .map(offset(_, main)) + .toVector + } + val domTrees = structure.children.map(recurse(_, 0)) + (headers, domTrees) + } + + + private[this] var scrolling = false + def apply(threshold: => Double) = if (!scrolling){ scrolling = true - dom.requestAnimationFrame((d: Double) => start(threshold)) + dom.setTimeout(() => start(threshold), 200) } - def start(threshold: Double) = { + private[this] def start(threshold: Double) = { scrolling = false + def scroll(el: dom.HTMLElement) = { + val rect = el.getBoundingClientRect() + if (rect.top <= 0) + el.scrollIntoView(true) + else if (rect.bottom > dom.innerHeight) + el.scrollIntoView(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 Tree(MenuNode(menuItem, start, end), children) = tree + val before = headers(start) < threshold + val after = (end >= headers.length) || headers(end) > threshold val win = before && after if (win){ menuItem.classList.remove("hide") @@ -34,7 +87,10 @@ class ScrollSpy(headers: Vector[Double], domTrees: Seq[Tree[MenuNode]]){ winFound = winFound | newWinFound } if (!winFound) { + // This means it's the leaf element, because it won but there + // aren't any children which won, so it must be the actual leaf tree.children.foreach(_.value.frag.classList.remove("selected")) + scroll(menuItem) } menuItem.children(0).classList.add("pure-menu-selected") }else{ @@ -46,4 +102,6 @@ class ScrollSpy(headers: Vector[Double], domTrees: Seq[Tree[MenuNode]]){ } domTrees.map(walkTree) } + + } \ No newline at end of file -- cgit v1.2.3