kdu-router 3.0.7 → 3.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,108 @@
1
+ /* @flow */
2
+
3
+ import { _Kdu } from '../install'
4
+ import { warn, isError } from './warn'
5
+
6
+ export function resolveAsyncComponents (matched: Array<RouteRecord>): Function {
7
+ return (to, from, next) => {
8
+ let hasAsync = false
9
+ let pending = 0
10
+ let error = null
11
+
12
+ flatMapComponents(matched, (def, _, match, key) => {
13
+ // if it's a function and doesn't have cid attached,
14
+ // assume it's an async component resolve function.
15
+ // we are not using Kdu's default async resolving mechanism because
16
+ // we want to halt the navigation until the incoming component has been
17
+ // resolved.
18
+ if (typeof def === 'function' && def.cid === undefined) {
19
+ hasAsync = true
20
+ pending++
21
+
22
+ const resolve = once(resolvedDef => {
23
+ if (isESModule(resolvedDef)) {
24
+ resolvedDef = resolvedDef.default
25
+ }
26
+ // save resolved on async factory in case it's used elsewhere
27
+ def.resolved = typeof resolvedDef === 'function'
28
+ ? resolvedDef
29
+ : _Kdu.extend(resolvedDef)
30
+ match.components[key] = resolvedDef
31
+ pending--
32
+ if (pending <= 0) {
33
+ next()
34
+ }
35
+ })
36
+
37
+ const reject = once(reason => {
38
+ const msg = `Failed to resolve async component ${key}: ${reason}`
39
+ process.env.NODE_ENV !== 'production' && warn(false, msg)
40
+ if (!error) {
41
+ error = isError(reason)
42
+ ? reason
43
+ : new Error(msg)
44
+ next(error)
45
+ }
46
+ })
47
+
48
+ let res
49
+ try {
50
+ res = def(resolve, reject)
51
+ } catch (e) {
52
+ reject(e)
53
+ }
54
+ if (res) {
55
+ if (typeof res.then === 'function') {
56
+ res.then(resolve, reject)
57
+ } else {
58
+ // new syntax in Kdu 2.3
59
+ const comp = res.component
60
+ if (comp && typeof comp.then === 'function') {
61
+ comp.then(resolve, reject)
62
+ }
63
+ }
64
+ }
65
+ }
66
+ })
67
+
68
+ if (!hasAsync) next()
69
+ }
70
+ }
71
+
72
+ export function flatMapComponents (
73
+ matched: Array<RouteRecord>,
74
+ fn: Function
75
+ ): Array<?Function> {
76
+ return flatten(matched.map(m => {
77
+ return Object.keys(m.components).map(key => fn(
78
+ m.components[key],
79
+ m.instances[key],
80
+ m, key
81
+ ))
82
+ }))
83
+ }
84
+
85
+ export function flatten (arr: Array<any>): Array<any> {
86
+ return Array.prototype.concat.apply([], arr)
87
+ }
88
+
89
+ const hasSymbol =
90
+ typeof Symbol === 'function' &&
91
+ typeof Symbol.toStringTag === 'symbol'
92
+
93
+ function isESModule (obj) {
94
+ return obj.__esModule || (hasSymbol && obj[Symbol.toStringTag] === 'Module')
95
+ }
96
+
97
+ // in Webpack 2, require.ensure now also returns a Promise
98
+ // so the resolve/reject functions may get called an extra time
99
+ // if the user uses an arrow function shorthand that happens to
100
+ // return that Promise.
101
+ function once (fn) {
102
+ let called = false
103
+ return function (...args) {
104
+ if (called) return
105
+ called = true
106
+ return fn.apply(this, args)
107
+ }
108
+ }
@@ -0,0 +1,132 @@
1
+ /* @flow */
2
+
3
+ import type KduRouter from '../index'
4
+ import { stringifyQuery } from './query'
5
+
6
+ const trailingSlashRE = /\/?$/
7
+
8
+ export function createRoute (
9
+ record: ?RouteRecord,
10
+ location: Location,
11
+ redirectedFrom?: ?Location,
12
+ router?: KduRouter
13
+ ): Route {
14
+ const stringifyQuery = router && router.options.stringifyQuery
15
+
16
+ let query: any = location.query || {}
17
+ try {
18
+ query = clone(query)
19
+ } catch (e) {}
20
+
21
+ const route: Route = {
22
+ name: location.name || (record && record.name),
23
+ meta: (record && record.meta) || {},
24
+ path: location.path || '/',
25
+ hash: location.hash || '',
26
+ query,
27
+ params: location.params || {},
28
+ fullPath: getFullPath(location, stringifyQuery),
29
+ matched: record ? formatMatch(record) : []
30
+ }
31
+ if (redirectedFrom) {
32
+ route.redirectedFrom = getFullPath(redirectedFrom, stringifyQuery)
33
+ }
34
+ return Object.freeze(route)
35
+ }
36
+
37
+ function clone (value) {
38
+ if (Array.isArray(value)) {
39
+ return value.map(clone)
40
+ } else if (value && typeof value === 'object') {
41
+ const res = {}
42
+ for (const key in value) {
43
+ res[key] = clone(value[key])
44
+ }
45
+ return res
46
+ } else {
47
+ return value
48
+ }
49
+ }
50
+
51
+ // the starting route that represents the initial state
52
+ export const START = createRoute(null, {
53
+ path: '/'
54
+ })
55
+
56
+ function formatMatch (record: ?RouteRecord): Array<RouteRecord> {
57
+ const res = []
58
+ while (record) {
59
+ res.unshift(record)
60
+ record = record.parent
61
+ }
62
+ return res
63
+ }
64
+
65
+ function getFullPath (
66
+ { path, query = {}, hash = '' },
67
+ _stringifyQuery
68
+ ): string {
69
+ const stringify = _stringifyQuery || stringifyQuery
70
+ return (path || '/') + stringify(query) + hash
71
+ }
72
+
73
+ export function isSameRoute (a: Route, b: ?Route): boolean {
74
+ if (b === START) {
75
+ return a === b
76
+ } else if (!b) {
77
+ return false
78
+ } else if (a.path && b.path) {
79
+ return (
80
+ a.path.replace(trailingSlashRE, '') === b.path.replace(trailingSlashRE, '') &&
81
+ a.hash === b.hash &&
82
+ isObjectEqual(a.query, b.query)
83
+ )
84
+ } else if (a.name && b.name) {
85
+ return (
86
+ a.name === b.name &&
87
+ a.hash === b.hash &&
88
+ isObjectEqual(a.query, b.query) &&
89
+ isObjectEqual(a.params, b.params)
90
+ )
91
+ } else {
92
+ return false
93
+ }
94
+ }
95
+
96
+ function isObjectEqual (a = {}, b = {}): boolean {
97
+ // handle null value #1566
98
+ if (!a || !b) return a === b
99
+ const aKeys = Object.keys(a)
100
+ const bKeys = Object.keys(b)
101
+ if (aKeys.length !== bKeys.length) {
102
+ return false
103
+ }
104
+ return aKeys.every(key => {
105
+ const aVal = a[key]
106
+ const bVal = b[key]
107
+ // check nested equality
108
+ if (typeof aVal === 'object' && typeof bVal === 'object') {
109
+ return isObjectEqual(aVal, bVal)
110
+ }
111
+ return String(aVal) === String(bVal)
112
+ })
113
+ }
114
+
115
+ export function isIncludedRoute (current: Route, target: Route): boolean {
116
+ return (
117
+ current.path.replace(trailingSlashRE, '/').indexOf(
118
+ target.path.replace(trailingSlashRE, '/')
119
+ ) === 0 &&
120
+ (!target.hash || current.hash === target.hash) &&
121
+ queryIncludes(current.query, target.query)
122
+ )
123
+ }
124
+
125
+ function queryIncludes (current: Dictionary<string>, target: Dictionary<string>): boolean {
126
+ for (const key in target) {
127
+ if (!(key in current)) {
128
+ return false
129
+ }
130
+ }
131
+ return true
132
+ }
@@ -0,0 +1,156 @@
1
+ /* @flow */
2
+
3
+ import type Router from '../index'
4
+ import { assert } from './warn'
5
+ import { getStateKey, setStateKey } from './state-key'
6
+ import { extend } from './misc'
7
+
8
+ const positionStore = Object.create(null)
9
+
10
+ export function setupScroll () {
11
+ // Fix for #1585 for Firefox
12
+ // Fix for #2195 Add optional third attribute to workaround a bug in safari https://bugs.webkit.org/show_bug.cgi?id=182678
13
+ // Fix for #2774 Support for apps loaded from Windows file shares not mapped to network drives: replaced location.origin with
14
+ // window.location.protocol + '//' + window.location.host
15
+ // location.host contains the port and location.hostname doesn't
16
+ const protocolAndPath = window.location.protocol + '//' + window.location.host
17
+ const absolutePath = window.location.href.replace(protocolAndPath, '')
18
+ // preserve existing history state as it could be overriden by the user
19
+ const stateCopy = extend({}, window.history.state)
20
+ stateCopy.key = getStateKey()
21
+ window.history.replaceState(stateCopy, '', absolutePath)
22
+ window.addEventListener('popstate', e => {
23
+ saveScrollPosition()
24
+ if (e.state && e.state.key) {
25
+ setStateKey(e.state.key)
26
+ }
27
+ })
28
+ }
29
+
30
+ export function handleScroll (
31
+ router: Router,
32
+ to: Route,
33
+ from: Route,
34
+ isPop: boolean
35
+ ) {
36
+ if (!router.app) {
37
+ return
38
+ }
39
+
40
+ const behavior = router.options.scrollBehavior
41
+ if (!behavior) {
42
+ return
43
+ }
44
+
45
+ if (process.env.NODE_ENV !== 'production') {
46
+ assert(typeof behavior === 'function', `scrollBehavior must be a function`)
47
+ }
48
+
49
+ // wait until re-render finishes before scrolling
50
+ router.app.$nextTick(() => {
51
+ const position = getScrollPosition()
52
+ const shouldScroll = behavior.call(
53
+ router,
54
+ to,
55
+ from,
56
+ isPop ? position : null
57
+ )
58
+
59
+ if (!shouldScroll) {
60
+ return
61
+ }
62
+
63
+ if (typeof shouldScroll.then === 'function') {
64
+ shouldScroll
65
+ .then(shouldScroll => {
66
+ scrollToPosition((shouldScroll: any), position)
67
+ })
68
+ .catch(err => {
69
+ if (process.env.NODE_ENV !== 'production') {
70
+ assert(false, err.toString())
71
+ }
72
+ })
73
+ } else {
74
+ scrollToPosition(shouldScroll, position)
75
+ }
76
+ })
77
+ }
78
+
79
+ export function saveScrollPosition () {
80
+ const key = getStateKey()
81
+ if (key) {
82
+ positionStore[key] = {
83
+ x: window.pageXOffset,
84
+ y: window.pageYOffset
85
+ }
86
+ }
87
+ }
88
+
89
+ function getScrollPosition (): ?Object {
90
+ const key = getStateKey()
91
+ if (key) {
92
+ return positionStore[key]
93
+ }
94
+ }
95
+
96
+ function getElementPosition (el: Element, offset: Object): Object {
97
+ const docEl: any = document.documentElement
98
+ const docRect = docEl.getBoundingClientRect()
99
+ const elRect = el.getBoundingClientRect()
100
+ return {
101
+ x: elRect.left - docRect.left - offset.x,
102
+ y: elRect.top - docRect.top - offset.y
103
+ }
104
+ }
105
+
106
+ function isValidPosition (obj: Object): boolean {
107
+ return isNumber(obj.x) || isNumber(obj.y)
108
+ }
109
+
110
+ function normalizePosition (obj: Object): Object {
111
+ return {
112
+ x: isNumber(obj.x) ? obj.x : window.pageXOffset,
113
+ y: isNumber(obj.y) ? obj.y : window.pageYOffset
114
+ }
115
+ }
116
+
117
+ function normalizeOffset (obj: Object): Object {
118
+ return {
119
+ x: isNumber(obj.x) ? obj.x : 0,
120
+ y: isNumber(obj.y) ? obj.y : 0
121
+ }
122
+ }
123
+
124
+ function isNumber (v: any): boolean {
125
+ return typeof v === 'number'
126
+ }
127
+
128
+ const hashStartsWithNumberRE = /^#\d/
129
+
130
+ function scrollToPosition (shouldScroll, position) {
131
+ const isObject = typeof shouldScroll === 'object'
132
+ if (isObject && typeof shouldScroll.selector === 'string') {
133
+ // getElementById would still fail if the selector contains a more complicated query like #main[data-attr]
134
+ // but at the same time, it doesn't make much sense to select an element with an id and an extra selector
135
+ const el = hashStartsWithNumberRE.test(shouldScroll.selector) // $flow-disable-line
136
+ ? document.getElementById(shouldScroll.selector.slice(1)) // $flow-disable-line
137
+ : document.querySelector(shouldScroll.selector)
138
+
139
+ if (el) {
140
+ let offset =
141
+ shouldScroll.offset && typeof shouldScroll.offset === 'object'
142
+ ? shouldScroll.offset
143
+ : {}
144
+ offset = normalizeOffset(offset)
145
+ position = getElementPosition(el, offset)
146
+ } else if (isValidPosition(shouldScroll)) {
147
+ position = normalizePosition(shouldScroll)
148
+ }
149
+ } else if (isObject && isValidPosition(shouldScroll)) {
150
+ position = normalizePosition(shouldScroll)
151
+ }
152
+
153
+ if (position) {
154
+ window.scrollTo(position.x, position.y)
155
+ }
156
+ }
@@ -0,0 +1,22 @@
1
+ /* @flow */
2
+ import { inBrowser } from './dom'
3
+
4
+ // use User Timing api (if present) for more accurate key precision
5
+ const Time =
6
+ inBrowser && window.performance && window.performance.now
7
+ ? window.performance
8
+ : Date
9
+
10
+ export function genStateKey (): string {
11
+ return Time.now().toFixed(3)
12
+ }
13
+
14
+ let _key: string = genStateKey()
15
+
16
+ export function getStateKey () {
17
+ return _key
18
+ }
19
+
20
+ export function setStateKey (key: string) {
21
+ return (_key = key)
22
+ }
@@ -0,0 +1,25 @@
1
+ /* @flow */
2
+
3
+ export function assert (condition: any, message: string) {
4
+ if (!condition) {
5
+ throw new Error(`[kdu-router] ${message}`)
6
+ }
7
+ }
8
+
9
+ export function warn (condition: any, message: string) {
10
+ if (process.env.NODE_ENV !== 'production' && !condition) {
11
+ typeof console !== 'undefined' && console.warn(`[kdu-router] ${message}`)
12
+ }
13
+ }
14
+
15
+ export function isError (err: any): boolean {
16
+ return Object.prototype.toString.call(err).indexOf('Error') > -1
17
+ }
18
+
19
+ export function isExtendedError (constructor: Function, err: any): boolean {
20
+ return (
21
+ err instanceof constructor ||
22
+ // _name is to support IE9 too
23
+ (err && (err.name === constructor.name || err._name === constructor._name))
24
+ )
25
+ }
package/types/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
- import "./kdu";
2
- import { KduRouter } from "./router";
1
+ import './kdu'
2
+ import { KduRouter } from './router'
3
3
 
4
- export default KduRouter;
4
+ export default KduRouter
5
5
 
6
6
  export {
7
7
  RouterMode,
@@ -13,4 +13,4 @@ export {
13
13
  Location,
14
14
  Route,
15
15
  NavigationGuard
16
- } from "./router";
16
+ } from './router'
package/types/kdu.d.ts CHANGED
@@ -2,21 +2,21 @@
2
2
  * Augment the typings of Kdu.js
3
3
  */
4
4
 
5
- import Kdu from "kdu";
6
- import KduRouter, { Route, RawLocation, NavigationGuard } from "./index";
5
+ import Kdu from 'kdu'
6
+ import KduRouter, { Route, RawLocation, NavigationGuard } from './index'
7
7
 
8
- declare module "kdu/types/kdu" {
8
+ declare module 'kdu/types/kdu' {
9
9
  interface Kdu {
10
- $router: KduRouter;
11
- $route: Route;
10
+ $router: KduRouter
11
+ $route: Route
12
12
  }
13
13
  }
14
14
 
15
- declare module "kdu/types/options" {
15
+ declare module 'kdu/types/options' {
16
16
  interface ComponentOptions<V extends Kdu> {
17
- router?: KduRouter;
18
- beforeRouteEnter?: NavigationGuard<V>;
19
- beforeRouteLeave?: NavigationGuard<V>;
20
- beforeRouteUpdate?: NavigationGuard<V>;
17
+ router?: KduRouter
18
+ beforeRouteEnter?: NavigationGuard<V>
19
+ beforeRouteLeave?: NavigationGuard<V>
20
+ beforeRouteUpdate?: NavigationGuard<V>
21
21
  }
22
22
  }