npm package: @ruphin/spa-router
Description: A minimal spa router
Author: Goffert van Gool
Latest version: 1.0.0

# spa-router

[![NPM Latest version](https://img.shields.io/npm/v/@ruphin/spa-router.svg)](https://www.npmjs.com/package/@ruphin/spa-router)
[![Code Style: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg)](https://github.com/prettier/prettier)

A minimal javascript spa router. If enabled, it intercepts browser navigation to same-origin locations, and uses pushState to navigate instead. Fires an event when navigation is intercepted. This module implements the bare fundamentals required for frontend navigation. It is not intended to replace a full-featured router, but it contains all the features you need to build a routing solution.

It supports pages with a `<base>` element. Please don't use the `<base>` element.

## Compatibility

| Chrome | Safari | Edge | Firefox | Edge Legacy | IE11 |
| ------ | ------ | ---- | ------- | ----------- | ---- |
| ✓      | ✓      | ✓    | ✓       | ✗ \*        | ✗ \* |

\* Requires transpiling to ES5 and polyfills for `URL()`, `Event()`, and `Event.prototype.composedPath()`

## Installation

spa-router is available on [npm](https://www.npmjs.com/package/@ruphin/spa-router) as `@ruphin/spa-router`.

## Examples

```javascript
import {
  interceptNavigation,
  router,
  ROUTE_CHANGED,
  navigate,
} from "@ruphin/spa-router";

// Enable navigation interception to paths that match the 'include' list and do not match the 'exclude' list
interceptNavigation({
  include: [/^\/my\//, /^\/application\//, /^\/paths\//],
  exclude: [/^\/path\/that\/should\/reload\//],
});

// Respond to route changes
router.addEventListener(
  ROUTE_CHANGED,
  ({ routePath, routeQuery, routeHash }) => {
    // ...
  }
);

// Navigate to a route programatically
navigate("/somewhere");
```

## API

### router

Fires a ROUTE_CHANGED event whenever browser navigation happens.
The event contains the path, query, and hash components of the new location.

```javascript
import { router, ROUTE_CHANGED } from "@ruphin/spa-router";

router.addEventListener(
  ROUTE_CHANGED,
  ({ routePath, routeQuery, routeHash }) => {
    console.log("PATH: ", routePath);
    console.log("QUERY: ", routeQuery);
    console.log("HASH: ", routeHash);
  }
);
```

The router also exposes a getter for the current path, query, and hash

```javascript
import { router } from "@ruphin/spa-router";

const { path, query, hash } = router;
```

### interceptNavigation()

Enables intercepting navigation. After calling this, the browser will no longer reload when the user navigates to a same-domain link. Instead, the new url will be added to the browser navigation history, and a route change event is fired.

Navigation is intercepted when the user navigates by:

- Clicking a link (`<a href="/some/url">`)
- Changing the browser history by clicking forward or back
- Calling the `navigate()` function

The following methods of navigating are _not_ intercepted:

- Assigning to `window.location`
- Manipulating `window.history` in javascript

This function has has an optional argument with two options:

    {
      include: <Array> of <RegExp> to paths that should be intercepted
      exclude: <Array> of <RegExp> to paths that should not be intercepted
    }

This function may be called multiple times. Each call beyond the first adds the provided `included` and `excluded` expressions to the system.

The `include` option defaults to `[/.?/]` which means all same-domain paths will be intercepted by default if the `include` option is not provided. Pass an empty array `[]` to the `include` option to avoid enabling interception on all same-domain paths.

Calling `interceptNavigation()` without arguments effectively enables navigation interception on all same-domain paths.

```javascript
import { interceptNavigation } from "@ruphin/spa-router";

// Intercept any navigation to paths that begin with '/my/', '/application/', or '/paths/'
// But NOT navigation to paths that begin with '/paths/that/should/reload/'
interceptNavigation({
  include: [/^\/my\//, /^\/application\//, /^\/paths\//],
  exclude: [/^\/paths\/that\/should\/reload\//],
});
```

### navigate()

Updates the browser location as if the user clicked on a link. Uses the same baseURI as the current document, so it behaves exactly like a regular link.

```javascript
import { navigate } from "@ruphin/spa-router";

// Navigate to some href. Behaves just like clicking `<a href="[given href]">`
navigate("/new_path?query=new_value#new_hash");
navigate("https://github.com/ruphin");
navigate("#anchor");
```

### currentPath()

Returns the active path (returns the same value as router.path)

```javascript
import { currentPath } from "@ruphin/spa-router";

// If the current url is https://example.com/path?query=value#hash
currentPath() === "/path";
```

### currentQuery()

Returns the active query component (returns the same value as router.query)

```javascript
import { currentQuery } from "@ruphin/spa-router";

// If the current url is https://example.com/path?query=value#hash
currentQuery() === "query=value";
```

### currentHash()

Returns the active hash (returns the same value as router.hash)

```javascript
import { currentHash } from "@ruphin/spa-router";

// If the current url is https://example.com/path?query=value#hash
currentHash() === "hash";
```