Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/router/basics.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ export default Blits.Component('Poster', {

Whenever you navigate to a new page, the URL hash will automatically be updated. Unless specified otherwise, navigating to a new page, will add that route to the history stack. The `back` input action is automatically wired up to navigate back down the history stack.

If you want to disable this automatic history navigation on Back (for example, to let a top-level navigation component handle Back), set `this.$router.backNavigation = false`. Set `this.$router.backNavigation = true` to restore the default behavior.

By default, every time you navigate to a new route, the application focus will be automatically passed to the newly loaded page. If you instead want to maintain the current focus (for example in a widget that sits above your RouterView), you can use `passFocus: false` as part of the router options.

## Deeplinking
Expand Down
23 changes: 23 additions & 0 deletions docs/router/hooks.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,29 @@ In order to configure global router hooks the `routes` key in the Application co

Alongside the `router.routes` key we can now define a `router.hooks` object, which can have any of the following pre-defined hook functions:

### Router Settings

The router configuration also supports router settings alongside `router.routes` and `router.hooks`:

- `backNavigation` - Enable or disable RouterView history navigation on Back input (default: `true`). When set to `false`, the Back input will be passed to the parent component instead of navigating back through the history stack. This setting can be configured in the router config or changed at runtime via `this.$router.backNavigation`.

> **Note:** `backNavigation` is an app-wide setting that affects all `RouterView` instances in your application, as the router state is global and shared.

```js
export default Blits.Application({
router: {
backNavigation: false,
routes: [
{ path: '/', component: Home },
{ path: '/details', component: Details },
],
hooks: {
// router hooks can be defined here
}
}
})
```

### `beforeEach()`

Similar to the `before`-hook, the `beforeEach`-hook will be execute for every router navigation. This can be useful if you find yourself repeating the same functionality for many routes, for example an _authentication check_ or sending _telemetry_.
Expand Down
30 changes: 27 additions & 3 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -321,24 +321,29 @@ declare module '@lightningjs/blits' {
*/
back(): boolean;

/**
* Enable or disable RouterView history navigation on Back input
*/
backNavigation: boolean;

/**
* Get the current route read-only
*/
readonly currentRoute: Route;

/**
* Get the list of all routes
*/
*/
readonly routes: Route[];

/**
* Get navigating state
*/
*/
readonly navigating: boolean;

/**
* Reactive router state
*/
*/
state: {
/**
* Path of the current route
Expand Down Expand Up @@ -658,6 +663,25 @@ declare module '@lightningjs/blits' {
* ```
*/
routes?: Route[]

/**
* Enable or disable RouterView history navigation on Back input
*
* @default true
*
* @remarks
* This is an app-wide setting that affects all RouterView instances in your application.
* The router state is global and shared across all router instances.
*
* @example
* ```js
* router: {
* backNavigation: false, // Disable automatic back navigation
* routes: [...]
* }
* ```
*/
backNavigation?: boolean
}

export type ApplicationConfig<P extends Props, S, M, C, W> = ComponentConfig<P, S, M, C, W> & (
Expand Down
6 changes: 6 additions & 0 deletions src/component/base/router.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ export default {
value: {
to,
back,
get backNavigation() {
return state.backNavigation !== false
},
set backNavigation(enabled) {
state.backNavigation = enabled !== false
},
get currentRoute() {
return currentRoute
},
Expand Down
5 changes: 5 additions & 0 deletions src/component/setup/routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,17 @@
*/

import symbols from '../../lib/symbols.js'
import { state as routerState } from '../../router/router.js'

export default (component, data) => {
let routes = data
if (Array.isArray(data) === false) {
component[symbols.routerHooks] = data.hooks
routes = data.routes
// Set initial backNavigation value if provided in router config
if (data.backNavigation !== undefined) {
routerState.backNavigation = data.backNavigation !== false
}
}
component[symbols.routes] = []

Expand Down
75 changes: 42 additions & 33 deletions src/components/RouterView.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,46 +16,55 @@
*/

import Component from '../component.js'
import Router from '../router/router.js'
import Router, { state as routerState } from '../router/router.js'
import symbols from '../lib/symbols.js'
import Focus from '../focus.js'

let hashchangeHandler = null

/** @typedef {{ $input?: (event: any) => boolean, $focus?: (event: any) => void }} RouterViewParent */

export default () =>
Component('RouterView', {
template: `
<Element w="100%" height="100%"></Element>
`,
state() {
return {
activeView: null,
}
},
hooks: {
async ready() {
if (this.parent[symbols.routerHooks] && this.parent[symbols.routerHooks].init) {
await this.parent[symbols.routerHooks].init.apply(this.parent)
Component(
'RouterView',
/** @type {any} */ ({
template: `
<Element w="100%" height="100%"></Element>
`,
state() {
return {
activeView: null,
}
hashchangeHandler = () => Router.navigate.apply(this)
Router.navigate.apply(this)
window.addEventListener('hashchange', hashchangeHandler)
},
destroy() {
window.removeEventListener('hashchange', hashchangeHandler, false)
},
focus() {
if (this.activeView && Focus.get() === this) {
this.activeView.$focus()
}
hooks: {
async ready() {
if (this.parent[symbols.routerHooks] && this.parent[symbols.routerHooks].init) {
await this.parent[symbols.routerHooks].init.apply(this.parent)
}
hashchangeHandler = () => Router.navigate.apply(this)
Router.navigate.apply(this)
window.addEventListener('hashchange', hashchangeHandler)
},
destroy() {
window.removeEventListener('hashchange', hashchangeHandler, false)
},
focus() {
if (this.activeView && Focus.get() === this) {
this.activeView.$focus()
}
},
},
},
input: {
back(e) {
const navigating = Router.back.call(this)
if (navigating === false) {
this.parent.$focus(e)
}
input: {
back(e) {
if (routerState.backNavigation === false) {
this.parent.$input(e)
return
}
const navigating = Router.back.call(this)
if (navigating === false) {
this.parent.$focus(e)
}
},
Comment thread
il-sairamg marked this conversation as resolved.
},
},
})
})
)
5 changes: 5 additions & 0 deletions src/router/router.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ import Settings from '../settings.js'
* @typedef {import('../component.js').BlitsComponent} BlitsComponent - The element of the route
* @typedef {import('../engines/L3/element.js').BlitsElement} BlitsElement - The element of the route
*
* @typedef {BlitsComponent|BlitsComponentFactory} RouteView
* @typedef {RouteView & { default?: BlitsComponentFactory }} RouteViewWithOptionalDefault
*
* @typedef {Object} Route
* @property {string} path - The path of the route
* @property {string} hash - The hash of the route
Expand Down Expand Up @@ -57,6 +60,7 @@ export const state = reactive(
data: null,
params: null,
hash: '',
backNavigation: true,
},
Settings.get('reactivityMode'),
true
Expand Down Expand Up @@ -354,6 +358,7 @@ export const navigate = async function () {
/** @type {import('../engines/L3/element.js').BlitsElement} */
let holder

/** @type {RouteViewWithOptionalDefault|undefined|null} */
let view
let focus
// when navigating back let's see if we're navigating back to a route that was kept alive
Expand Down