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/advanced/Async.scala8
-rw-r--r--examples/demos/src/main/scala/scrollmenu/Controller.scala90
-rw-r--r--examples/demos/src/main/scala/scrollmenu/ScrollMenu.scala117
-rw-r--r--examples/demos/src/main/scala/scrollmenu/ScrollSpy.scala170
4 files changed, 219 insertions, 166 deletions
diff --git a/examples/demos/src/main/scala/advanced/Async.scala b/examples/demos/src/main/scala/advanced/Async.scala
index 481e80e..c4d7366 100644
--- a/examples/demos/src/main/scala/advanced/Async.scala
+++ b/examples/demos/src/main/scala/advanced/Async.scala
@@ -29,11 +29,11 @@ object Async {
type ME = dom.MouseEvent
val mousemove =
- Channel[ME](canvas.onmousemove = _)
+ new Channel[ME](canvas.onmousemove = _)
val mouseup =
- Channel[ME](canvas.onmouseup = _)
+ new Channel[ME](canvas.onmouseup = _)
val mousedown =
- Channel[ME](canvas.onmousedown = _)
+ new Channel[ME](canvas.onmousedown = _)
async{
while(true){
@@ -99,7 +99,7 @@ object Async {
}
}
-case class Channel[T](init: (T => Unit) => Unit){
+class Channel[T](init: (T => Unit) => Unit){
init(update)
private[this] var value: Promise[T] = null
def apply(): Future[T] = {
diff --git a/examples/demos/src/main/scala/scrollmenu/Controller.scala b/examples/demos/src/main/scala/scrollmenu/Controller.scala
index 4480445..fe35be8 100644
--- a/examples/demos/src/main/scala/scrollmenu/Controller.scala
+++ b/examples/demos/src/main/scala/scrollmenu/Controller.scala
@@ -28,53 +28,53 @@ object Controller{
val snippets = dom.document.getElementsByClassName("highlight-me")
snippets.foreach(js.Dynamic.global.hljs.highlightBlock(_))
- def rest() = {
- val scrollSpy = new ScrollSpy(structure, main)
- val list = ul(cls := "menu-item-list collapsed")(
- scrollSpy.domTrees.map(_.value.frag)
+
+ val scrollSpy = new ScrollSpy(structure, main)
+ val list = ul(cls := "menu-item-list collapsed")(
+ scrollSpy.domTrees.value.frag
+ ).render
+
+ def updateScroll() = scrollSpy()
+ val expandIcon = i(cls := "fa fa-caret-down").render
+ val expandLink =
+ a(
+ expandIcon,
+ href := "javascript:",
+ marginLeft := "0px",
+ paddingLeft := "15px",
+ paddingRight := "15px",
+ position.absolute,
+ top := "0px",
+ right := "0px",
+ cls := "pure-menu-selected",
+ onclick := { (e: dom.Event) =>
+ expandIcon.classList.toggle("fa-caret-down")
+ expandIcon.classList.toggle("fa-caret-up")
+ list.classList.toggle("collapsed")
+ list.classList.toggle("expanded")
+ scrollSpy.toggleOpen()
+// updateScroll()
+ }
+ ).render
+
+
+ menu.appendChild(
+ div(cls := "pure-menu pure-menu-open")(
+ a(cls := "pure-menu-heading")(
+ "Contents", expandLink
+ ),
+ list
).render
+ )
- def updateScroll() = scrollSpy()
- val expandIcon = i(cls := "fa fa-caret-down").render
- val expandLink =
- a(
- expandIcon,
- href := "javascript:",
- marginLeft := "0px",
- paddingLeft := "15px",
- paddingRight := "15px",
- position.absolute,
- top := "0px",
- right := "0px",
- 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")(
- "Contents", expandLink
- ),
- list
- ).render
- )
-
- menuLink.onclick = (e: dom.MouseEvent) => {
- layout.classList.toggle("active")
- menu.classList.toggle("active")
- menuLink.classList.toggle("active")
- }
-
- main.onscroll = (e: dom.UIEvent) => updateScroll()
- updateScroll()
+ menuLink.onclick = (e: dom.MouseEvent) => {
+ layout.classList.toggle("active")
+ menu.classList.toggle("active")
+ menuLink.classList.toggle("active")
}
- dom.setTimeout(rest _, 10)
+
+ 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
deleted file mode 100644
index 9ce9a2b..0000000
--- a/examples/demos/src/main/scala/scrollmenu/ScrollMenu.scala
+++ /dev/null
@@ -1,117 +0,0 @@
-package scrollmenu
-
-import org.scalajs.dom
-
-import scala.scalajs.js
-import scalatags.JsDom.all._
-
-case class Tree[T](value: T, children: Vector[Tree[T]])
-
-case class MenuNode(frag: dom.HTMLElement, id: String, 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,
- var clean: Boolean = false){
- 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,
- Controller.munge(t.value),
- 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 = -1
- def apply() = {
- dom.clearTimeout(scrolling)
- scrolling = dom.setTimeout(() => start(), 200)
- }
- private[this] def start() = {
-// scrolling = false
- def scroll(el: dom.Element) = {
- val rect = el.getBoundingClientRect()
- if (rect.top <= 0)
- el.scrollIntoView(true)
- else if (rect.top > dom.innerHeight)
- el.scrollIntoView(false)
- }
- def walkTree(tree: Tree[MenuNode]): Boolean = {
- val Tree(MenuNode(menuItem, itemId, start, end), children) = tree
- val before = headers(start) <= main.scrollTop
- val after = (end >= headers.length) || headers(end) > main.scrollTop
-
- 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) {
- // 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"))
- if (dom.location.hash != itemId)
- dom.history.pushState(null, null, "#"+itemId)
- 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")
- }
- win
- }
- domTrees.map(walkTree)
- }
-
-
-} \ No newline at end of file
diff --git a/examples/demos/src/main/scala/scrollmenu/ScrollSpy.scala b/examples/demos/src/main/scala/scrollmenu/ScrollSpy.scala
new file mode 100644
index 0000000..736f9b3
--- /dev/null
+++ b/examples/demos/src/main/scala/scrollmenu/ScrollSpy.scala
@@ -0,0 +1,170 @@
+package scrollmenu
+
+import org.scalajs.dom
+import org.scalajs.dom.extensions._
+import scala.scalajs.js
+import scalatags.JsDom.all._
+
+case class Tree[T](value: T, children: Vector[Tree[T]])
+
+case class MenuNode(frag: dom.HTMLElement, id: String, 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){
+ lazy val domTrees = {
+ var i = -1
+ def recurse(t: Tree[String], depth: Int): Tree[MenuNode] = {
+ val curr =
+ li(
+ a(
+ t.value,
+ display := (if (i == -1) "none" else "block"),
+ 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,
+ Controller.munge(t.value),
+ originalI,
+ if (children.length > 0) children.map(_.value.end).max else originalI + 1
+ ),
+ children
+ )
+ }
+
+ val domTrees = recurse(structure, 0)
+ domTrees
+ }
+ def offset(el: dom.HTMLElement, parent: dom.HTMLElement): Double = {
+ if (el == parent) 0
+ else el.offsetTop + offset(el.offsetParent.asInstanceOf[dom.HTMLElement], parent)
+ }
+ lazy val headers = {
+ val menuItems = {
+ def rec(current: Tree[String]): Seq[String] = {
+ current.value +: current.children.flatMap(rec)
+ }
+ rec(structure).tail
+ }
+
+ js.Array(
+ menuItems.map(name => dom.document.getElementById(Controller.munge(name)))
+ .map((el) => () => offset(el, main)):_*
+ )
+ }
+
+ var open = false
+ def toggleOpen() = {
+ open = !open
+ if (open){
+ def rec(tree: Tree[MenuNode])(f: MenuNode => Unit): Unit = {
+ f(tree.value)
+ tree.children.foreach(rec(_)(f))
+ }
+ rec(domTrees)(setFullHeight)
+ }else{
+ start(force = true)
+ }
+ }
+
+ def setFullHeight(mn: MenuNode) = {
+ mn.frag
+ .children(1)
+ .asInstanceOf[dom.HTMLElement]
+ .style
+ .maxHeight = (mn.end - mn.start + 1) * 44 + "px"
+ }
+ private[this] var scrolling = false
+ private[this] var scrollTop = -1
+ def apply(): Unit = {
+ if (!scrolling) {
+ scrolling = true
+ scrollTop = main.scrollTop
+ dom.setTimeout({() =>
+ scrolling = false
+ if (scrollTop == main.scrollTop) start()
+ else apply()
+ },
+ 75
+ )
+ }
+ }
+ private[this] var previousWin: MenuNode = null
+ private[this] def start(force: Boolean = false) = {
+
+ def scroll(el: dom.Element) = {
+ val rect = el.getBoundingClientRect()
+ if (rect.top <= 0)
+ el.scrollIntoView(true)
+ else if (rect.top > dom.innerHeight)
+ el.scrollIntoView(false)
+ }
+ val scrollTop = main.scrollTop
+ def walkIndex(tree: Tree[MenuNode]): List[Tree[MenuNode]] = {
+ val t @ Tree(m, children) = tree
+ val win = if(m.start == -1) true
+ else {
+ val before = headers(m.start)() <= scrollTop
+ val after = (m.end >= headers.length) || headers(m.end)() > scrollTop
+ before && after
+ }
+ val childIndexes = children.map(walkIndex)
+ val childWin = childIndexes.indexWhere(_ != null)
+ if (childWin != -1) t :: childIndexes(childWin)
+ else if (win) List(t)
+ else null
+ }
+
+ val winPath = walkIndex(domTrees)
+ val winItem = winPath.last.value
+ def walkTree(indices: List[Tree[MenuNode]]): Int = indices match {
+ case Nil => 0
+ case (Tree(mn, children) :: rest) =>
+
+ mn.frag.classList.remove("hide")
+ mn.frag.classList.remove("selected")
+
+ mn.frag.children(0).classList.add("pure-menu-selected")
+ for {
+ child <- children
+ if !indices.headOption.exists(_.value.frag == child.value.frag)
+ } walkHide(child)
+
+ val size = walkTree(rest) + children.length
+ mn.frag.children(1).asInstanceOf[dom.HTMLElement].style.maxHeight =
+ if (!open) size * 44 + "px" else "none"
+ size
+ }
+
+ def walkHide(tree: Tree[MenuNode]): Unit = {
+ val frag = tree.value.frag
+
+ frag.children(0).classList.remove("pure-menu-selected")
+ frag.classList.add("hide")
+
+ frag.children(1).asInstanceOf[dom.HTMLElement].style.maxHeight =
+ if (!open) "0px" else "none"
+
+ if (tree.value.start < winItem.start) frag.classList.add("selected")
+ else frag.classList.remove("selected")
+ tree.children.foreach(walkHide)
+ }
+
+ if (winItem != previousWin || force){
+ scroll(winItem.frag.children(0))
+ dom.history.replaceState(null, null, "#" + winItem.id)
+ previousWin = winItem
+// println(winPath.map(_.value.id))
+ walkTree(winPath)
+ }
+ }
+} \ No newline at end of file