1 | /**
|
2 | @license
|
3 | Copyright (c) 2018 The Polymer Project Authors. All rights reserved.
|
4 | This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
|
5 | The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
|
6 | The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
|
7 | Code distributed by Google as part of the polymer project is also
|
8 | subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
|
9 | */
|
10 |
|
11 | /**
|
12 | Basic router that calls a callback whenever the location is updated.
|
13 |
|
14 | Example:
|
15 |
|
16 | import { installRouter } from 'pwa-helpers/router.js';
|
17 |
|
18 | installRouter((location) => handleNavigation(location));
|
19 |
|
20 | For example, if you're using this router in a Redux-connected component,
|
21 | you could dispatch an action in the callback:
|
22 |
|
23 | import { installRouter } from 'pwa-helpers/router.js';
|
24 | import { navigate } from '../actions/app.js';
|
25 |
|
26 | installRouter((location) => store.dispatch(navigate(location)))
|
27 |
|
28 | If you need to force a navigation to a new location programmatically, you can
|
29 | do so by pushing a new state using the History API, and then manually
|
30 | calling the callback with the new location:
|
31 |
|
32 | window.history.pushState({}, '', '/new-route');
|
33 | handleNavigation(window.location);
|
34 |
|
35 | Optionally, you can use the second argument to read the event that caused the
|
36 | navigation. For example, you may want to scroll to top only after a link click.
|
37 |
|
38 | installRouter((location, event) => {
|
39 | // Only scroll to top on link clicks, not popstate events.
|
40 | if (event && event.type === 'click') {
|
41 | window.scrollTo(0, 0);
|
42 | }
|
43 | handleNavigation(location);
|
44 | });
|
45 | */
|
46 | export const installRouter = (locationUpdatedCallback: (location:Location, event: Event|null) => void) => {
|
47 | document.body.addEventListener('click', e => {
|
48 | if (e.defaultPrevented || e.button !== 0 ||
|
49 | e.metaKey || e.ctrlKey || e.shiftKey) return;
|
50 |
|
51 | const anchor = e.composedPath().filter(
|
52 | n => (n as HTMLElement).tagName === 'A'
|
53 | )[0] as HTMLAnchorElement | undefined;
|
54 | if (!anchor || anchor.target ||
|
55 | anchor.hasAttribute('download') ||
|
56 | anchor.getAttribute('rel') === 'external') return;
|
57 |
|
58 | const href = anchor.href;
|
59 | if (!href || href.indexOf('mailto:') !== -1) return;
|
60 |
|
61 | const location = window.location;
|
62 | const origin = location.origin || location.protocol + '//' + location.host;
|
63 | if (href.indexOf(origin) !== 0) return;
|
64 |
|
65 | e.preventDefault();
|
66 | if (href !== location.href) {
|
67 | window.history.pushState({}, '', href);
|
68 | locationUpdatedCallback(location, e);
|
69 | }
|
70 | });
|
71 |
|
72 | window.addEventListener('popstate', e => locationUpdatedCallback(window.location, e));
|
73 | locationUpdatedCallback(window.location, null /* event */);
|
74 | };
|