From efc1b9cfd9c04b8d7f8cf0fb35e77bcb94a25a6e Mon Sep 17 00:00:00 2001 From: Li Haoyi Date: Tue, 18 Nov 2014 03:12:51 -0800 Subject: Fixed sidebar scrolling problems and added a button to collapse/expand the sidebar --- book/src/main/resources/css/side-menu.css | 6 +-- book/src/main/scala/book/Book.scala | 1 + .../scalatex/book/handson/GettingStarted.scalatex | 2 +- .../src/main/scala/scrollmenu/Controller.scala | 49 ++++++++++++++++++---- .../src/main/scala/scrollmenu/ScrollMenu.scala | 16 +++++-- .../scala/scalaparser/syntax/Identifiers.scala | 2 +- .../src/test/scala/scalaparser/SyntaxTest.scala | 27 ++++++++---- 7 files changed, 76 insertions(+), 27 deletions(-) diff --git a/book/src/main/resources/css/side-menu.css b/book/src/main/resources/css/side-menu.css index fbb2b26..81f1ee7 100755 --- a/book/src/main/resources/css/side-menu.css +++ b/book/src/main/resources/css/side-menu.css @@ -302,17 +302,17 @@ code{ .header-link{ opacity: 0.05; } -.menu-item-list li.hide > ul{ +.collapsed.menu-item-list li.hide > ul{ opacity: 1; overflow: hidden; height: 0px; } -.menu-item-list li > ul{ +.collapsed.menu-item-list li > ul{ opacity: 1; overflow: hidden; } -#menu .menu-item-list li.selected > a{ +#menu .collapsed.menu-item-list li.selected > a{ border-left: 2px solid white; } diff --git a/book/src/main/scala/book/Book.scala b/book/src/main/scala/book/Book.scala index d78e622..9323fff 100644 --- a/book/src/main/scala/book/Book.scala +++ b/book/src/main/scala/book/Book.scala @@ -71,6 +71,7 @@ object Book { span ), div(id:="menu") + ), div(id:="main", div(id:="main-box")( diff --git a/book/src/main/scalatex/book/handson/GettingStarted.scalatex b/book/src/main/scalatex/book/handson/GettingStarted.scalatex index b766ea7..314a425 100644 --- a/book/src/main/scalatex/book/handson/GettingStarted.scalatex +++ b/book/src/main/scalatex/book/handson/GettingStarted.scalatex @@ -243,7 +243,7 @@ @p As you can see, this code is still very verbose, with lots of unnecessarily long identifiers such as @hl.javascript{Lexample_ScalaJSExample} in it. This is because we've only performed the @i{fast optimization} on this file, to try and keep the time taken to edit -> compile while developing reasonably short. - @sect{Full Optimization} + @sect{Optimization} @p If we're planning on publishing the app for real, we can run the @i{full optimization}. This takes several seconds longer than the @i{fast optimization}, but results in a significantly smaller and leaner output file @code{example-opt.js}. diff --git a/examples/demos/src/main/scala/scrollmenu/Controller.scala b/examples/demos/src/main/scala/scrollmenu/Controller.scala index de120c6..132a073 100644 --- a/examples/demos/src/main/scala/scrollmenu/Controller.scala +++ b/examples/demos/src/main/scala/scrollmenu/Controller.scala @@ -28,29 +28,60 @@ object Controller{ val snippets = dom.document.getElementsByClassName("highlight-me") snippets.foreach(js.Dynamic.global.hljs.highlightBlock(_)) - val scrollSpy = new ScrollSpy(structure, main) + val list = ul(cls:="menu-item-list collapsed")( + scrollSpy.domTrees.map(_.value.frag) + ).render + + def updateScroll() = scrollSpy(main.scrollTop + main.clientHeight) + val expandIcon = i(cls:="fa fa-caret-down").render + val expandLink = + a( + expandIcon, + href:="javascript:", + marginLeft:="0px", + paddingLeft:="15px", + paddingRight:="15px", + cls:="pure-menu-selected", + onclick := { (e: dom.Event) => + expandIcon.classList.toggle("fa-caret-down") + expandIcon.classList.toggle("fa-caret-up") + list.classList.toggle("collapsed") + scrollSpy.clean = !scrollSpy.clean + updateScroll() + } + ).render + menu.appendChild( - div(cls:="pure-menu pure-menu-open")( - a(cls:="pure-menu-heading", href:="#")( - "Contents" - ), + div( + zIndex:=10, + position:="absolute", + cls:="pure-menu pure-menu-open", ul(cls:="menu-item-list")( - scrollSpy.domTrees.map(_.value.frag) + li( + width:="43px", + float:="right", + expandLink + ) ) ).render ) + menu.appendChild( + div(cls:="pure-menu pure-menu-open")( + list + ).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) - } + main.onscroll = (e: dom.UIEvent) => updateScroll() + updateScroll() } } diff --git a/examples/demos/src/main/scala/scrollmenu/ScrollMenu.scala b/examples/demos/src/main/scala/scrollmenu/ScrollMenu.scala index 7b238f4..5bd5259 100644 --- a/examples/demos/src/main/scala/scrollmenu/ScrollMenu.scala +++ b/examples/demos/src/main/scala/scrollmenu/ScrollMenu.scala @@ -2,6 +2,7 @@ package scrollmenu import org.scalajs.dom +import scala.scalajs.js import scalatags.JsDom.all._ case class Tree[T](value: T, children: Vector[Tree[T]]) @@ -12,7 +13,9 @@ 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(structure: Tree[String], main: dom.HTMLElement){ +class ScrollSpy(structure: Tree[String], + main: dom.HTMLElement, + var clean: Boolean = false){ val (headers, domTrees) = { var i = 0 def recurse(t: Tree[String], depth: Int): Tree[MenuNode] = { @@ -64,18 +67,20 @@ class ScrollSpy(structure: Tree[String], main: dom.HTMLElement){ } private[this] def start(threshold: Double) = { scrolling = false - def scroll(el: dom.HTMLElement) = { + def scroll(el: dom.Element) = { val rect = el.getBoundingClientRect() if (rect.top <= 0) el.scrollIntoView(true) - else if (rect.bottom > dom.innerHeight) + else if (rect.top > dom.innerHeight) el.scrollIntoView(false) } def walkTree(tree: Tree[MenuNode]): Boolean = { 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") var winFound = false @@ -90,10 +95,13 @@ class ScrollSpy(structure: Tree[String], main: dom.HTMLElement){ // 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) + + scroll(menuItem.children(0)) + } menuItem.children(0).classList.add("pure-menu-selected") }else{ + if(clean) tree.children.map(walkTree) menuItem.children(0).classList.remove("pure-menu-selected") menuItem.classList.add("hide") menuItem.classList.remove("selected") diff --git a/scalatexApi/src/main/scala/scalaparser/syntax/Identifiers.scala b/scalatexApi/src/main/scala/scalaparser/syntax/Identifiers.scala index 2590dfa..d10c7ea 100644 --- a/scalatexApi/src/main/scala/scalaparser/syntax/Identifiers.scala +++ b/scalatexApi/src/main/scala/scalaparser/syntax/Identifiers.scala @@ -15,7 +15,7 @@ trait Identifiers { self: Parser with Basic => def Id = rule { PlainId | ("`" ~ oneOrMore(noneOf("`")) ~ "`") } def IdRest = rule { zeroOrMore(zeroOrMore("_") ~ oneOrMore(!"_" ~ Letter | Digit)) ~ - optional(oneOrMore("_") ~ Operator) + optional(oneOrMore("_") ~ optional(Operator)) } diff --git a/scalatexApi/src/test/scala/scalaparser/SyntaxTest.scala b/scalatexApi/src/test/scala/scalaparser/SyntaxTest.scala index 29d5b1d..9559154 100644 --- a/scalatexApi/src/test/scala/scalaparser/SyntaxTest.scala +++ b/scalatexApi/src/test/scala/scalaparser/SyntaxTest.scala @@ -351,6 +351,15 @@ object SyntaxTest extends TestSuite{ |} """.stripMargin ) + * - check( + """ + |object O{ + | c match { + | case b_ => 1 + | } + |} + """.stripMargin + ) } def checkFile(path: String) = check(io.Source.fromFile(path).mkString) 'file{ @@ -378,15 +387,15 @@ object SyntaxTest extends TestSuite{ } 'omg{ -// val root = new java.io.File("../scala-js/") -// def listFiles(s: java.io.File): Iterator[String] = { -// val (dirs, files) = s.listFiles().toIterator.partition(_.isDirectory) -// files.map(_.getPath) ++ dirs.flatMap(listFiles) -// } -// for(f <- listFiles(root).filter(_.endsWith(".scala"))){ -// println("CHECKING " + f) -// checkFile(f) -// } + val root = new java.io.File("../scala-js/") + def listFiles(s: java.io.File): Iterator[String] = { + val (dirs, files) = s.listFiles().toIterator.partition(_.isDirectory) + files.map(_.getPath) ++ dirs.flatMap(listFiles) + } + for(f <- listFiles(root).filter(_.endsWith(".scala"))){ + println("CHECKING " + f) + checkFile(f) + } } } } -- cgit v1.2.3