Skip to content

Commit 82415b5

Browse files
committed
Router scrolls to top (by default) on route change
see #57
1 parent f16fe42 commit 82415b5

File tree

5 files changed

+181
-133
lines changed

5 files changed

+181
-133
lines changed

extra/ROUTER.md

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ val RootComponent = ReactComponentB[MyPage.Router]("Root")
111111
Note that the `Router` type is prefixed as `ReactComponentB[MyPage.Router]` and not `ReactComponentB[Router]`.
112112
Routers are not interchangable between routing rule sets.
113113

114-
#### Renmdering your Router
114+
#### Rendering your Router
115115

116116
Before a `Router` can be created it needs to the base URL, which is the prefix portion of the URL that is the same for all your pages routes.
117117

@@ -176,7 +176,7 @@ override protected def interceptRender(i: InterceptionR): ReactElement =
176176
i.element)
177177
```
178178

179-
### URL rewriting
179+
#### URL rewriting
180180

181181
`RoutingRules` comes with a built-in (although inactive) URL rewriting rule called `removeTrailingSlashes`.
182182
It can be installed via `register()` and is a good example of how to create dynamic matching rules.
@@ -188,3 +188,12 @@ def removeTrailingSlashes: DynamicRoute = {
188188
parser { case regex(p) => p }.redirection(p => (Path(p), Redirect.Replace))
189189
}
190190
```
191+
192+
#### Callback: `onRouteChange`
193+
By default, when a new route is activated the window is scrolled to the top.
194+
This behaviour can be removed or customised by providing a callback in your routing rules called `onRouteChange`.
195+
196+
```scala
197+
onRouteChange { loc =>
198+
...
199+
}

extra/src/main/scala/japgolly/scalajs/react/extra/router/Router.scala

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,8 @@ object Router {
4141
* @tparam P Routing rules context. Prevents different routing rule sets being mixed up.
4242
*/
4343
final class Router[P](val baseUrl: BaseUrl,
44-
val pathAction: Path => RouteAction[P],
44+
pathAction: Path => RouteAction[P],
45+
onSync: Location[P] => IO[Unit],
4546
logger: Router.Logger) extends Broadcaster[Unit] {
4647

4748
type Cmd[A] = RouteCmd[P, A]
@@ -87,7 +88,7 @@ final class Router[P](val baseUrl: BaseUrl,
8788
)
8889

8990
def syncToWindowUrlS: ReactST[IO, Loc, Unit] =
90-
ReactS.setM(syncToWindowUrl)
91+
ReactS.setM(syncToWindowUrl) addCallbackS onSync
9192

9293
def wrongBase(wrongUrl: AbsUrl): Prog[Loc] = {
9394
val url = AbsUrl(baseUrl.value)

extra/src/main/scala/japgolly/scalajs/react/extra/router/RoutingRules.scala

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package japgolly.scalajs.react.extra.router
22

3+
import org.scalajs.dom
4+
import scalaz.effect.IO
35
import scalaz.{-\/, \/-, \/}
46
import japgolly.scalajs.react.{ReactElement, ReactComponentC, TopNode}
57

@@ -29,18 +31,30 @@ trait RoutingRules {
2931
private[this] var dynRoutes = Vector.empty[DRouteFn]
3032
private[this] val parseD: DRouteFn = p => dynRoutes.foldLeft(None: Option[RouteAction[P]])(_ orElse _(p))
3133

34+
private[this] var onRouteChangeAction: Loc => IO[Unit] =
35+
Function const IO(dom.window.scrollTo(0, 0))
36+
3237
// ===================================================================================================================
3338
// Interception
3439

3540
protected case class InterceptionR(loc: Loc, router: Router, element: ReactElement)
3641

42+
/** Customise all renderable routes. */
3743
protected def interceptRender(i: InterceptionR): ReactElement = i.element
3844

3945
private def mkloc(path: Path, render: Renderer): Loc = {
4046
lazy val l: Loc = Location(path, r => interceptRender(InterceptionR(l, r, render(r))))
4147
l
4248
}
4349

50+
/** Perform an action when a new route is activated. */
51+
protected def onRouteChange(a: Loc => Unit): Unit =
52+
onRouteChangeIO(l => IO(a(l)))
53+
54+
/** Perform an action when a new route is activated. */
55+
protected def onRouteChangeIO(a: Loc => IO[Unit]): Unit =
56+
onRouteChangeAction = a
57+
4458
// ===================================================================================================================
4559
// Actions
4660

@@ -140,7 +154,7 @@ trait RoutingRules {
140154
import Router.{Logger, nopLogger}
141155

142156
final def routingEngine(base: BaseUrl, logger: Logger = nopLogger): Router =
143-
new Router(base, totalParser, logger)
157+
new Router(base, totalParser, onRouteChangeAction, logger)
144158

145159
final def router(base: BaseUrl, logger: Logger = nopLogger): Router.Component[P] =
146160
Router.component(routingEngine(base, logger))

0 commit comments

Comments
 (0)