diff options
Diffstat (limited to 'examples/demos/src')
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 |