summaryrefslogblamecommitdiff
path: root/examples/demos/Controller.scala
blob: e17cde9df78de769865beb300d121934bb6bebc6 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
 


                                              





                                           
 
 
                                                      





                             
                                                    





                                                                         

                                                       

                                               



                                          
                                                                           







                                                                 
 


                                                
 



                                                                      





                                                                                         
                                                       






                                                       
                 
     
 
 



                                        



                                               
     

                         


                                         











                                                            


       



                                                   









                                                                                   
               









                                                                                              
       


                                                            
 

                                                                
 


                                                        
 















                                                                   
     
 










                                                     
import japgolly.scalajs.react.React
import japgolly.scalajs.react.vdom.VDomBuilder

import scala.scalajs.js
import scala.scalajs.js.annotation.JSExport
import org.scalajs.dom
import org.scalajs.dom.extensions._
import scala.collection.mutable
import scalatags.JsDom.all._


case class Tree[T](name: T, children: Vector[Tree[T]])
@JSExport
object Controller{

  def munge(name: String) = {
    name.replace(" ", "")
  }
  def addClass(el: dom.HTMLElement, cls: String) = {
    removeClass(el, cls)
    el.className = el.className + " " + cls
  }
  def removeClass(el: dom.HTMLElement, cls: String) = {
    el.className = el.className.split(' ').filter(_ != cls).mkString(" ")
  }
  def toggleClass(el: dom.HTMLElement, cls: String) = {
    val frags = el.className.split(' ')
    if (!frags.contains(cls)) addClass(el, cls)
    else removeClass(el, cls)
  }
  @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]): Tree[(String, String, Int)] = {
      val curr = (t.name, munge(t.name), i)
      i += 1
      val children = t.children.map(recurse)
      Tree(curr, 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(_))

    val headers = {
      def offset(el: dom.HTMLElement, parent: dom.HTMLElement): Double = {
        if (el == parent) 0
        else el.offsetTop + offset(el.offsetParent.asInstanceOf[dom.HTMLElement], parent)
      }
      val menuItems = {
        def rec(current: Tree[String]): Seq[String] = {
          current.name +: current.children.flatMap(rec)
        }
        rec(structure).tail
      }
      menuItems.map(munge)
        .map(dom.document.getElementById)
        .map(offset(_, main))
        .toVector
    }


    val menuBar = React.renderComponent(
      Menu(recurse(structure)),
      menu
    )
    menuLink.onclick = (e: dom.MouseEvent) => {
      toggleClass(layout, "active")
      toggleClass(menu, "active")
      toggleClass(menuLink, "active")
    }

    var scrolling = false
    main.onscroll = (e: dom.UIEvent) => {
      if (!scrolling){
        scrolling = true
        dom.requestAnimationFrame{(d: Double) =>
          scrolling = false
          val threshold = main.scrollTop + main.clientHeight

          var index = 0
          while(index < headers.length && index >= 0){
            index += 1
            if (headers(index) > threshold) index *= -1
          }
          index = -index
          menuBar.setState(index)
        }
      }
    }
  }
  def isElementInViewport(el: dom.HTMLElement) = {
    val rect = el.getBoundingClientRect()
    rect.top >= 0 && rect.bottom <= dom.innerHeight
  }

  import japgolly.scalajs.react._ // React
  import vdom.ReactVDom._         // Scalatags → React virtual DOM
  import vdom.ReactVDom.all._     // Scalatags html & css (div, h1, textarea, etc.)

  val Menu = ReactComponentB[Tree[(String, String, Int)]]("Menu")
                            .getInitialState(_ => 0)
                            .render{ (structure, _, index) =>

    val contentList = {
      var i = 0
      def rec1(current: Tree[(String, String, Int)]): Tree[(String, String, Int, Boolean)] = {
        val initialI = i
        i += 1
        val children = current.children.map(rec1)

        val win = i >= index && initialI < index
        Tree(
          (current.name._1, current.name._2, current.name._3, win),
          children
        )
      }
      def rec(current: Tree[(String, String, Int, Boolean)],
              depth: Int,
              classes: String): Iterator[Tag] = {

        val winIndex = current.children.indexWhere(_.name._4)
        val (before, after) = current.children.splitAt(winIndex)

        val myCls =
          "menu-item" +
          (if (depth <= 1) " menu-item-divided" else "")

        val (name, munged, currIndex, win) = current.name
        val frag =
          li(paddingLeft := s"${depth * 15}px")(
            a(
              name,
              href:="#"+munged,
              cls:=myCls
            ),
            cls:=classes + (if (win) " pure-menu-selected" else "")
          )
        val afterCls = if (!win) "hide" else ""
        Iterator(frag) ++
        before.flatMap(rec(_, depth+1, "lined")) ++
        after.flatMap(rec(_, depth+1, afterCls))
      }
      rec(rec1(structure), 0, "")
    }

    val frag = div(cls:="pure-menu  pure-menu-open")(
      a(cls:="pure-menu-heading", href:="#")(
        "Contents"
      ),
      ul(cls:="menu-item-list")(
        contentList.drop(1).toVector
      )
    )
    frag
  }.build
}