aboutsummaryrefslogtreecommitdiff
path: root/client/src/router
diff options
context:
space:
mode:
Diffstat (limited to 'client/src/router')
-rw-r--r--client/src/router/Router.scala105
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)
+
+ }
+
+}