diff options
Diffstat (limited to 'client/src/router')
-rw-r--r-- | client/src/router/Router.scala | 105 |
1 files changed, 105 insertions, 0 deletions
diff --git a/client/src/router/Router.scala b/client/src/router/Router.scala new file mode 100644 index 0000000..60e5ef2 --- /dev/null +++ b/client/src/router/Router.scala @@ -0,0 +1,105 @@ +package yoga.router + +import scala.scalajs.js +import scala.scalajs.js.URIUtils +import scala.xml.Node +import java.net.URI + +import mhtml.Var +import org.scalajs.dom.raw.{ Element, Node => RawNode } +import org.scalajs.dom.{ document, window, Event, MouseEvent } + +import yoga.model.Route +import yoga.view.index.Index +import yoga.view.licence.LicencePage +import yoga.view.notFound.NotFound + +object Router { + + private val route: Var[Route] = Var(parseRoute(Path.current())) + + def render(): Node = + <div + mhtml-onmount={ (n: RawNode) => + document.addEventListener("click", Handler.click) + window.addEventListener("popstate", Handler.popState) + } + mhtml-onumnount={ (n: RawNode) => + document.removeEventListener("click", Handler.click) + window.removeEventListener("popstate", Handler.popState) + } + > + {route.map { + case Route.Index => Index() + case Route.Licence(uri) => LicencePage(uri) + case Route.NotFound => NotFound() + }} + </div> + + object Path { + + val index: String = "/" + + def licence(uri: URI): String = s"/licence/${uri}" + + def current(): String = + URIUtils.decodeURI(s"${window.location.pathname}${window.location.search}") + + } + + private def parseRoute(path: String): Route = + pathAndParams(path) match { + case (List(), _) => Route.Index + case (List("licence", uri), _) => Route.Licence(URI.create(uri)) + case _ => Route.NotFound + } + + private def pathAndParams(path: String): (List[String], List[String]) = { + println(path) + def splitPath(path: String) = path.split("/").drop(1).toList + path.split('?') match { + case Array(path) => (splitPath(path), Nil) + case Array(path, params) => (splitPath(path), params.split("&").toList) + } + } + + private object Handler { + + lazy val click: js.Function1[MouseEvent, Unit] = + routeTransitionOnAnchorClick { path => + route := parseRoute(path) + window.history.pushState(null, "", path) + } + + lazy val popState: js.Function1[Event, Unit] = + _ => route := parseRoute(Path.current()) + + } + + private def routeTransitionOnAnchorClick(goTo: String => Unit)(event: MouseEvent): Unit = + if (event.button == 0) + Dom.findSelfOrParent("a", event.target.asInstanceOf[RawNode]) match { + case Some(node) => + val elem = node.asInstanceOf[Element] + if (elem.getAttribute("data-nav") != "ignore") { + event.preventDefault() + val href = elem.getAttribute("href") + if (href != Path.current()) goTo(href) + } + case _ => + } + + private object Dom { + + @annotation.tailrec + def findSelfOrParent(nodeName: String, current: RawNode): Option[RawNode] = + if (current.nodeName.toString.toLowerCase == nodeName.toLowerCase) + Some(current) + else if (current.parentNode == null) + None + else + findSelfOrParent(nodeName, current.parentNode) + + } + +} |