kdu-router 3.4.0-beta.0 → 3.5.4
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.
- package/README.md +3 -1
- package/dist/kdu-router.common.js +381 -274
- package/dist/kdu-router.esm.browser.js +384 -276
- package/dist/kdu-router.esm.browser.min.js +3 -3
- package/dist/kdu-router.esm.js +381 -274
- package/dist/kdu-router.js +387 -281
- package/dist/kdu-router.min.js +3 -3
- package/package.json +15 -36
- package/src/components/link.js +30 -3
- package/src/components/view.js +6 -0
- package/src/create-matcher.js +28 -2
- package/src/create-route-map.js +18 -3
- package/src/history/abstract.js +4 -0
- package/src/history/base.js +32 -53
- package/src/history/hash.js +0 -11
- package/src/history/html5.js +7 -2
- package/src/index.js +21 -5
- package/src/util/errors.js +1 -0
- package/src/util/path.js +1 -1
- package/src/util/query.js +48 -31
- package/src/util/route.js +29 -10
- package/src/util/scroll.js +11 -1
- package/src/util/warn.js +1 -1
- package/types/index.d.ts +5 -1
- package/types/router.d.ts +52 -11
package/src/history/base.js
CHANGED
|
@@ -5,7 +5,7 @@ import type Router from '../index'
|
|
|
5
5
|
import { inBrowser } from '../util/dom'
|
|
6
6
|
import { runQueue } from '../util/async'
|
|
7
7
|
import { warn } from '../util/warn'
|
|
8
|
-
import { START, isSameRoute } from '../util/route'
|
|
8
|
+
import { START, isSameRoute, handleRouteEntered } from '../util/route'
|
|
9
9
|
import {
|
|
10
10
|
flatten,
|
|
11
11
|
flatMapComponents,
|
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
isNavigationFailure,
|
|
21
21
|
NavigationFailureType
|
|
22
22
|
} from '../util/errors'
|
|
23
|
+
import { handleScroll } from '../util/scroll'
|
|
23
24
|
|
|
24
25
|
export class History {
|
|
25
26
|
router: Router
|
|
@@ -84,6 +85,7 @@ export class History {
|
|
|
84
85
|
onAbort?: Function
|
|
85
86
|
) {
|
|
86
87
|
let route
|
|
88
|
+
// catch redirect option
|
|
87
89
|
try {
|
|
88
90
|
route = this.router.match(location, this.current)
|
|
89
91
|
} catch (e) {
|
|
@@ -93,10 +95,10 @@ export class History {
|
|
|
93
95
|
// Exception should still be thrown
|
|
94
96
|
throw e
|
|
95
97
|
}
|
|
98
|
+
const prev = this.current
|
|
96
99
|
this.confirmTransition(
|
|
97
100
|
route,
|
|
98
101
|
() => {
|
|
99
|
-
const prev = this.current
|
|
100
102
|
this.updateRoute(route)
|
|
101
103
|
onComplete && onComplete(route)
|
|
102
104
|
this.ensureURL()
|
|
@@ -117,16 +119,13 @@ export class History {
|
|
|
117
119
|
onAbort(err)
|
|
118
120
|
}
|
|
119
121
|
if (err && !this.ready) {
|
|
120
|
-
|
|
121
|
-
//
|
|
122
|
-
if (!isNavigationFailure(err, NavigationFailureType.redirected)) {
|
|
122
|
+
// Initial redirection should not mark the history as ready yet
|
|
123
|
+
// because it's triggered by the redirection instead
|
|
124
|
+
if (!isNavigationFailure(err, NavigationFailureType.redirected) || prev !== START) {
|
|
125
|
+
this.ready = true
|
|
123
126
|
this.readyErrorCbs.forEach(cb => {
|
|
124
127
|
cb(err)
|
|
125
128
|
})
|
|
126
|
-
} else {
|
|
127
|
-
this.readyCbs.forEach(cb => {
|
|
128
|
-
cb(route)
|
|
129
|
-
})
|
|
130
129
|
}
|
|
131
130
|
}
|
|
132
131
|
}
|
|
@@ -135,16 +134,19 @@ export class History {
|
|
|
135
134
|
|
|
136
135
|
confirmTransition (route: Route, onComplete: Function, onAbort?: Function) {
|
|
137
136
|
const current = this.current
|
|
137
|
+
this.pending = route
|
|
138
138
|
const abort = err => {
|
|
139
|
-
// changed after adding errors
|
|
140
|
-
// redirect and aborted navigation would produce an err == null
|
|
139
|
+
// changed after adding errors
|
|
140
|
+
// before that change, redirect and aborted navigation would produce an err == null
|
|
141
141
|
if (!isNavigationFailure(err) && isError(err)) {
|
|
142
142
|
if (this.errorCbs.length) {
|
|
143
143
|
this.errorCbs.forEach(cb => {
|
|
144
144
|
cb(err)
|
|
145
145
|
})
|
|
146
146
|
} else {
|
|
147
|
-
|
|
147
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
148
|
+
warn(false, 'uncaught error during route navigation:')
|
|
149
|
+
}
|
|
148
150
|
console.error(err)
|
|
149
151
|
}
|
|
150
152
|
}
|
|
@@ -159,6 +161,9 @@ export class History {
|
|
|
159
161
|
route.matched[lastRouteIndex] === current.matched[lastCurrentIndex]
|
|
160
162
|
) {
|
|
161
163
|
this.ensureURL()
|
|
164
|
+
if (route.hash) {
|
|
165
|
+
handleScroll(this.router, current, route, false)
|
|
166
|
+
}
|
|
162
167
|
return abort(createNavigationDuplicatedError(current, route))
|
|
163
168
|
}
|
|
164
169
|
|
|
@@ -180,7 +185,6 @@ export class History {
|
|
|
180
185
|
resolveAsyncComponents(activated)
|
|
181
186
|
)
|
|
182
187
|
|
|
183
|
-
this.pending = route
|
|
184
188
|
const iterator = (hook: NavigationGuard, next) => {
|
|
185
189
|
if (this.pending !== route) {
|
|
186
190
|
return abort(createNavigationCancelledError(current, route))
|
|
@@ -217,11 +221,9 @@ export class History {
|
|
|
217
221
|
}
|
|
218
222
|
|
|
219
223
|
runQueue(queue, iterator, () => {
|
|
220
|
-
const postEnterCbs = []
|
|
221
|
-
const isValid = () => this.current === route
|
|
222
224
|
// wait until async components are resolved before
|
|
223
225
|
// extracting in-component enter guards
|
|
224
|
-
const enterGuards = extractEnterGuards(activated
|
|
226
|
+
const enterGuards = extractEnterGuards(activated)
|
|
225
227
|
const queue = enterGuards.concat(this.router.resolveHooks)
|
|
226
228
|
runQueue(queue, iterator, () => {
|
|
227
229
|
if (this.pending !== route) {
|
|
@@ -231,9 +233,7 @@ export class History {
|
|
|
231
233
|
onComplete(route)
|
|
232
234
|
if (this.router.app) {
|
|
233
235
|
this.router.app.$nextTick(() => {
|
|
234
|
-
|
|
235
|
-
cb()
|
|
236
|
-
})
|
|
236
|
+
handleRouteEntered(route)
|
|
237
237
|
})
|
|
238
238
|
}
|
|
239
239
|
})
|
|
@@ -249,11 +249,16 @@ export class History {
|
|
|
249
249
|
// Default implementation is empty
|
|
250
250
|
}
|
|
251
251
|
|
|
252
|
-
|
|
252
|
+
teardown () {
|
|
253
|
+
// clean up event listeners
|
|
253
254
|
this.listeners.forEach(cleanupListener => {
|
|
254
255
|
cleanupListener()
|
|
255
256
|
})
|
|
256
257
|
this.listeners = []
|
|
258
|
+
|
|
259
|
+
// reset current history route
|
|
260
|
+
this.current = START
|
|
261
|
+
this.pending = null
|
|
257
262
|
}
|
|
258
263
|
}
|
|
259
264
|
|
|
@@ -344,15 +349,13 @@ function bindGuard (guard: NavigationGuard, instance: ?_Kdu): ?NavigationGuard {
|
|
|
344
349
|
}
|
|
345
350
|
|
|
346
351
|
function extractEnterGuards (
|
|
347
|
-
activated: Array<RouteRecord
|
|
348
|
-
cbs: Array<Function>,
|
|
349
|
-
isValid: () => boolean
|
|
352
|
+
activated: Array<RouteRecord>
|
|
350
353
|
): Array<?Function> {
|
|
351
354
|
return extractGuards(
|
|
352
355
|
activated,
|
|
353
356
|
'beforeRouteEnter',
|
|
354
357
|
(guard, _, match, key) => {
|
|
355
|
-
return bindEnterGuard(guard, match, key
|
|
358
|
+
return bindEnterGuard(guard, match, key)
|
|
356
359
|
}
|
|
357
360
|
)
|
|
358
361
|
}
|
|
@@ -360,41 +363,17 @@ function extractEnterGuards (
|
|
|
360
363
|
function bindEnterGuard (
|
|
361
364
|
guard: NavigationGuard,
|
|
362
365
|
match: RouteRecord,
|
|
363
|
-
key: string
|
|
364
|
-
cbs: Array<Function>,
|
|
365
|
-
isValid: () => boolean
|
|
366
|
+
key: string
|
|
366
367
|
): NavigationGuard {
|
|
367
368
|
return function routeEnterGuard (to, from, next) {
|
|
368
369
|
return guard(to, from, cb => {
|
|
369
370
|
if (typeof cb === 'function') {
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
// we will need to poll for registration until current route
|
|
375
|
-
// is no longer valid.
|
|
376
|
-
poll(cb, match.instances, key, isValid)
|
|
377
|
-
})
|
|
371
|
+
if (!match.enteredCbs[key]) {
|
|
372
|
+
match.enteredCbs[key] = []
|
|
373
|
+
}
|
|
374
|
+
match.enteredCbs[key].push(cb)
|
|
378
375
|
}
|
|
379
376
|
next(cb)
|
|
380
377
|
})
|
|
381
378
|
}
|
|
382
379
|
}
|
|
383
|
-
|
|
384
|
-
function poll (
|
|
385
|
-
cb: any, // somehow flow cannot infer this is a function
|
|
386
|
-
instances: Object,
|
|
387
|
-
key: string,
|
|
388
|
-
isValid: () => boolean
|
|
389
|
-
) {
|
|
390
|
-
if (
|
|
391
|
-
instances[key] &&
|
|
392
|
-
!instances[key]._isBeingDestroyed // do not reuse being destroyed instance
|
|
393
|
-
) {
|
|
394
|
-
cb(instances[key])
|
|
395
|
-
} else if (isValid()) {
|
|
396
|
-
setTimeout(() => {
|
|
397
|
-
poll(cb, instances, key, isValid)
|
|
398
|
-
}, 16)
|
|
399
|
-
}
|
|
400
|
-
}
|
package/src/history/hash.js
CHANGED
|
@@ -124,17 +124,6 @@ export function getHash (): string {
|
|
|
124
124
|
if (index < 0) return ''
|
|
125
125
|
|
|
126
126
|
href = href.slice(index + 1)
|
|
127
|
-
// decode the hash but not the search or hash
|
|
128
|
-
// as search(query) is already decoded
|
|
129
|
-
const searchIndex = href.indexOf('?')
|
|
130
|
-
if (searchIndex < 0) {
|
|
131
|
-
const hashIndex = href.indexOf('#')
|
|
132
|
-
if (hashIndex > -1) {
|
|
133
|
-
href = decodeURI(href.slice(0, hashIndex)) + href.slice(hashIndex)
|
|
134
|
-
} else href = decodeURI(href)
|
|
135
|
-
} else {
|
|
136
|
-
href = decodeURI(href.slice(0, searchIndex)) + href.slice(searchIndex)
|
|
137
|
-
}
|
|
138
127
|
|
|
139
128
|
return href
|
|
140
129
|
}
|
package/src/history/html5.js
CHANGED
|
@@ -86,8 +86,13 @@ export class HTML5History extends History {
|
|
|
86
86
|
}
|
|
87
87
|
|
|
88
88
|
export function getLocation (base: string): string {
|
|
89
|
-
let path =
|
|
90
|
-
|
|
89
|
+
let path = window.location.pathname
|
|
90
|
+
const pathLowerCase = path.toLowerCase()
|
|
91
|
+
const baseLowerCase = base.toLowerCase()
|
|
92
|
+
// base="/a" shouldn't turn path="/app" into "/a/pp"
|
|
93
|
+
// so we ensure the trailing slash in the base
|
|
94
|
+
if (base && ((pathLowerCase === baseLowerCase) ||
|
|
95
|
+
(pathLowerCase.indexOf(cleanPath(baseLowerCase + '/')) === 0))) {
|
|
91
96
|
path = path.slice(base.length)
|
|
92
97
|
}
|
|
93
98
|
return (path || '/') + window.location.search + window.location.hash
|
package/src/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { install } from './install'
|
|
4
4
|
import { START } from './util/route'
|
|
5
|
-
import { assert } from './util/warn'
|
|
5
|
+
import { assert, warn } from './util/warn'
|
|
6
6
|
import { inBrowser } from './util/dom'
|
|
7
7
|
import { cleanPath } from './util/path'
|
|
8
8
|
import { createMatcher } from './create-matcher'
|
|
@@ -23,6 +23,7 @@ export default class KduRouter {
|
|
|
23
23
|
static version: string
|
|
24
24
|
static isNavigationFailure: Function
|
|
25
25
|
static NavigationFailureType: any
|
|
26
|
+
static START_LOCATION: Route
|
|
26
27
|
|
|
27
28
|
app: any
|
|
28
29
|
apps: Array<any>
|
|
@@ -38,6 +39,9 @@ export default class KduRouter {
|
|
|
38
39
|
afterHooks: Array<?AfterNavigationHook>
|
|
39
40
|
|
|
40
41
|
constructor (options: RouterOptions = {}) {
|
|
42
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
43
|
+
warn(this instanceof KduRouter, `Router must be called with the new operator.`)
|
|
44
|
+
}
|
|
41
45
|
this.app = null
|
|
42
46
|
this.apps = []
|
|
43
47
|
this.options = options
|
|
@@ -101,10 +105,7 @@ export default class KduRouter {
|
|
|
101
105
|
// we do not release the router so it can be reused
|
|
102
106
|
if (this.app === app) this.app = this.apps[0] || null
|
|
103
107
|
|
|
104
|
-
if (!this.app)
|
|
105
|
-
// clean up event listeners
|
|
106
|
-
this.history.teardownListeners()
|
|
107
|
-
}
|
|
108
|
+
if (!this.app) this.history.teardown()
|
|
108
109
|
})
|
|
109
110
|
|
|
110
111
|
// main app previously initialized
|
|
@@ -246,7 +247,21 @@ export default class KduRouter {
|
|
|
246
247
|
}
|
|
247
248
|
}
|
|
248
249
|
|
|
250
|
+
getRoutes () {
|
|
251
|
+
return this.matcher.getRoutes()
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
addRoute (parentOrRoute: string | RouteConfig, route?: RouteConfig) {
|
|
255
|
+
this.matcher.addRoute(parentOrRoute, route)
|
|
256
|
+
if (this.history.current !== START) {
|
|
257
|
+
this.history.transitionTo(this.history.getCurrentLocation())
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
249
261
|
addRoutes (routes: Array<RouteConfig>) {
|
|
262
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
263
|
+
warn(false, 'router.addRoutes() is deprecated and has been removed in Kdu Router 4. Use router.addRoute() instead.')
|
|
264
|
+
}
|
|
250
265
|
this.matcher.addRoutes(routes)
|
|
251
266
|
if (this.history.current !== START) {
|
|
252
267
|
this.history.transitionTo(this.history.getCurrentLocation())
|
|
@@ -271,6 +286,7 @@ KduRouter.install = install
|
|
|
271
286
|
KduRouter.version = '__VERSION__'
|
|
272
287
|
KduRouter.isNavigationFailure = isNavigationFailure
|
|
273
288
|
KduRouter.NavigationFailureType = NavigationFailureType
|
|
289
|
+
KduRouter.START_LOCATION = START
|
|
274
290
|
|
|
275
291
|
if (inBrowser && window.Kdu) {
|
|
276
292
|
window.Kdu.use(KduRouter)
|
package/src/util/errors.js
CHANGED
package/src/util/path.js
CHANGED
package/src/util/query.js
CHANGED
|
@@ -9,11 +9,21 @@ const commaRE = /%2C/g
|
|
|
9
9
|
// fixed encodeURIComponent which is more conformant to RFC3986:
|
|
10
10
|
// - escapes [!'()*]
|
|
11
11
|
// - preserve commas
|
|
12
|
-
const encode = str =>
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
const encode = str =>
|
|
13
|
+
encodeURIComponent(str)
|
|
14
|
+
.replace(encodeReserveRE, encodeReserveReplacer)
|
|
15
|
+
.replace(commaRE, ',')
|
|
15
16
|
|
|
16
|
-
|
|
17
|
+
export function decode (str: string) {
|
|
18
|
+
try {
|
|
19
|
+
return decodeURIComponent(str)
|
|
20
|
+
} catch (err) {
|
|
21
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
22
|
+
warn(false, `Error decoding "${str}". Leaving it intact.`)
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return str
|
|
26
|
+
}
|
|
17
27
|
|
|
18
28
|
export function resolveQuery (
|
|
19
29
|
query: ?string,
|
|
@@ -30,11 +40,15 @@ export function resolveQuery (
|
|
|
30
40
|
}
|
|
31
41
|
for (const key in extraQuery) {
|
|
32
42
|
const value = extraQuery[key]
|
|
33
|
-
parsedQuery[key] = Array.isArray(value)
|
|
43
|
+
parsedQuery[key] = Array.isArray(value)
|
|
44
|
+
? value.map(castQueryParamValue)
|
|
45
|
+
: castQueryParamValue(value)
|
|
34
46
|
}
|
|
35
47
|
return parsedQuery
|
|
36
48
|
}
|
|
37
49
|
|
|
50
|
+
const castQueryParamValue = value => (value == null || typeof value === 'object' ? value : String(value))
|
|
51
|
+
|
|
38
52
|
function parseQuery (query: string): Dictionary<string> {
|
|
39
53
|
const res = {}
|
|
40
54
|
|
|
@@ -47,9 +61,7 @@ function parseQuery (query: string): Dictionary<string> {
|
|
|
47
61
|
query.split('&').forEach(param => {
|
|
48
62
|
const parts = param.replace(/\+/g, ' ').split('=')
|
|
49
63
|
const key = decode(parts.shift())
|
|
50
|
-
const val = parts.length > 0
|
|
51
|
-
? decode(parts.join('='))
|
|
52
|
-
: null
|
|
64
|
+
const val = parts.length > 0 ? decode(parts.join('=')) : null
|
|
53
65
|
|
|
54
66
|
if (res[key] === undefined) {
|
|
55
67
|
res[key] = val
|
|
@@ -64,33 +76,38 @@ function parseQuery (query: string): Dictionary<string> {
|
|
|
64
76
|
}
|
|
65
77
|
|
|
66
78
|
export function stringifyQuery (obj: Dictionary<string>): string {
|
|
67
|
-
const res = obj
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
return ''
|
|
72
|
-
}
|
|
79
|
+
const res = obj
|
|
80
|
+
? Object.keys(obj)
|
|
81
|
+
.map(key => {
|
|
82
|
+
const val = obj[key]
|
|
73
83
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
84
|
+
if (val === undefined) {
|
|
85
|
+
return ''
|
|
86
|
+
}
|
|
77
87
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
val.forEach(val2 => {
|
|
81
|
-
if (val2 === undefined) {
|
|
82
|
-
return
|
|
88
|
+
if (val === null) {
|
|
89
|
+
return encode(key)
|
|
83
90
|
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
91
|
+
|
|
92
|
+
if (Array.isArray(val)) {
|
|
93
|
+
const result = []
|
|
94
|
+
val.forEach(val2 => {
|
|
95
|
+
if (val2 === undefined) {
|
|
96
|
+
return
|
|
97
|
+
}
|
|
98
|
+
if (val2 === null) {
|
|
99
|
+
result.push(encode(key))
|
|
100
|
+
} else {
|
|
101
|
+
result.push(encode(key) + '=' + encode(val2))
|
|
102
|
+
}
|
|
103
|
+
})
|
|
104
|
+
return result.join('&')
|
|
88
105
|
}
|
|
89
|
-
})
|
|
90
|
-
return result.join('&')
|
|
91
|
-
}
|
|
92
106
|
|
|
93
|
-
|
|
94
|
-
|
|
107
|
+
return encode(key) + '=' + encode(val)
|
|
108
|
+
})
|
|
109
|
+
.filter(x => x.length > 0)
|
|
110
|
+
.join('&')
|
|
111
|
+
: null
|
|
95
112
|
return res ? `?${res}` : ''
|
|
96
113
|
}
|
package/src/util/route.js
CHANGED
|
@@ -70,23 +70,23 @@ function getFullPath (
|
|
|
70
70
|
return (path || '/') + stringify(query) + hash
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
-
export function isSameRoute (a: Route, b: ?Route): boolean {
|
|
73
|
+
export function isSameRoute (a: Route, b: ?Route, onlyPath: ?boolean): boolean {
|
|
74
74
|
if (b === START) {
|
|
75
75
|
return a === b
|
|
76
76
|
} else if (!b) {
|
|
77
77
|
return false
|
|
78
78
|
} else if (a.path && b.path) {
|
|
79
|
-
return (
|
|
80
|
-
a.path.replace(trailingSlashRE, '') === b.path.replace(trailingSlashRE, '') &&
|
|
79
|
+
return a.path.replace(trailingSlashRE, '') === b.path.replace(trailingSlashRE, '') && (onlyPath ||
|
|
81
80
|
a.hash === b.hash &&
|
|
82
|
-
isObjectEqual(a.query, b.query)
|
|
83
|
-
)
|
|
81
|
+
isObjectEqual(a.query, b.query))
|
|
84
82
|
} else if (a.name && b.name) {
|
|
85
83
|
return (
|
|
86
84
|
a.name === b.name &&
|
|
87
|
-
|
|
85
|
+
(onlyPath || (
|
|
86
|
+
a.hash === b.hash &&
|
|
88
87
|
isObjectEqual(a.query, b.query) &&
|
|
89
|
-
isObjectEqual(a.params, b.params)
|
|
88
|
+
isObjectEqual(a.params, b.params))
|
|
89
|
+
)
|
|
90
90
|
)
|
|
91
91
|
} else {
|
|
92
92
|
return false
|
|
@@ -96,14 +96,18 @@ export function isSameRoute (a: Route, b: ?Route): boolean {
|
|
|
96
96
|
function isObjectEqual (a = {}, b = {}): boolean {
|
|
97
97
|
// handle null value #1566
|
|
98
98
|
if (!a || !b) return a === b
|
|
99
|
-
const aKeys = Object.keys(a)
|
|
100
|
-
const bKeys = Object.keys(b)
|
|
99
|
+
const aKeys = Object.keys(a).sort()
|
|
100
|
+
const bKeys = Object.keys(b).sort()
|
|
101
101
|
if (aKeys.length !== bKeys.length) {
|
|
102
102
|
return false
|
|
103
103
|
}
|
|
104
|
-
return aKeys.every(key => {
|
|
104
|
+
return aKeys.every((key, i) => {
|
|
105
105
|
const aVal = a[key]
|
|
106
|
+
const bKey = bKeys[i]
|
|
107
|
+
if (bKey !== key) return false
|
|
106
108
|
const bVal = b[key]
|
|
109
|
+
// query values can be null and undefined
|
|
110
|
+
if (aVal == null || bVal == null) return aVal === bVal
|
|
107
111
|
// check nested equality
|
|
108
112
|
if (typeof aVal === 'object' && typeof bVal === 'object') {
|
|
109
113
|
return isObjectEqual(aVal, bVal)
|
|
@@ -130,3 +134,18 @@ function queryIncludes (current: Dictionary<string>, target: Dictionary<string>)
|
|
|
130
134
|
}
|
|
131
135
|
return true
|
|
132
136
|
}
|
|
137
|
+
|
|
138
|
+
export function handleRouteEntered (route: Route) {
|
|
139
|
+
for (let i = 0; i < route.matched.length; i++) {
|
|
140
|
+
const record = route.matched[i]
|
|
141
|
+
for (const name in record.instances) {
|
|
142
|
+
const instance = record.instances[name]
|
|
143
|
+
const cbs = record.enteredCbs[name]
|
|
144
|
+
if (!instance || !cbs) continue
|
|
145
|
+
delete record.enteredCbs[name]
|
|
146
|
+
for (let i = 0; i < cbs.length; i++) {
|
|
147
|
+
if (!instance._isBeingDestroyed) cbs[i](instance)
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
package/src/util/scroll.js
CHANGED
|
@@ -160,6 +160,16 @@ function scrollToPosition (shouldScroll, position) {
|
|
|
160
160
|
}
|
|
161
161
|
|
|
162
162
|
if (position) {
|
|
163
|
-
|
|
163
|
+
// $flow-disable-line
|
|
164
|
+
if ('scrollBehavior' in document.documentElement.style) {
|
|
165
|
+
window.scrollTo({
|
|
166
|
+
left: position.x,
|
|
167
|
+
top: position.y,
|
|
168
|
+
// $flow-disable-line
|
|
169
|
+
behavior: shouldScroll.behavior
|
|
170
|
+
})
|
|
171
|
+
} else {
|
|
172
|
+
window.scrollTo(position.x, position.y)
|
|
173
|
+
}
|
|
164
174
|
}
|
|
165
175
|
}
|
package/src/util/warn.js
CHANGED
|
@@ -7,7 +7,7 @@ export function assert (condition: any, message: string) {
|
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
export function warn (condition: any, message: string) {
|
|
10
|
-
if (
|
|
10
|
+
if (!condition) {
|
|
11
11
|
typeof console !== 'undefined' && console.warn(`[kdu-router] ${message}`)
|
|
12
12
|
}
|
|
13
13
|
}
|
package/types/index.d.ts
CHANGED
|
@@ -5,13 +5,17 @@ export default KduRouter
|
|
|
5
5
|
|
|
6
6
|
export {
|
|
7
7
|
RouterMode,
|
|
8
|
+
RouteMeta,
|
|
8
9
|
RawLocation,
|
|
9
10
|
RedirectOption,
|
|
10
11
|
RouterOptions,
|
|
11
12
|
RouteConfig,
|
|
12
13
|
RouteRecord,
|
|
14
|
+
RouteRecordPublic,
|
|
13
15
|
Location,
|
|
14
16
|
Route,
|
|
15
17
|
NavigationGuard,
|
|
16
|
-
NavigationGuardNext
|
|
18
|
+
NavigationGuardNext,
|
|
19
|
+
NavigationFailureType,
|
|
20
|
+
NavigationFailure
|
|
17
21
|
} from './router'
|
package/types/router.d.ts
CHANGED
|
@@ -21,7 +21,7 @@ export declare class KduRouter {
|
|
|
21
21
|
constructor(options?: RouterOptions)
|
|
22
22
|
|
|
23
23
|
app: Kdu
|
|
24
|
-
options: RouterOptions
|
|
24
|
+
options: RouterOptions
|
|
25
25
|
mode: RouterMode
|
|
26
26
|
currentRoute: Route
|
|
27
27
|
|
|
@@ -43,10 +43,16 @@ export declare class KduRouter {
|
|
|
43
43
|
go(n: number): void
|
|
44
44
|
back(): void
|
|
45
45
|
forward(): void
|
|
46
|
+
match (raw: RawLocation, current?: Route, redirectedFrom?: Location): Route
|
|
46
47
|
getMatchedComponents(to?: RawLocation | Route): Component[]
|
|
47
48
|
onReady(cb: Function, errorCb?: ErrorHandler): void
|
|
48
49
|
onError(cb: ErrorHandler): void
|
|
49
50
|
addRoutes(routes: RouteConfig[]): void
|
|
51
|
+
|
|
52
|
+
addRoute(parent: string, route: RouteConfig): void
|
|
53
|
+
addRoute(route: RouteConfig): void
|
|
54
|
+
getRoutes(): RouteRecordPublic[]
|
|
55
|
+
|
|
50
56
|
resolve(
|
|
51
57
|
to: RawLocation,
|
|
52
58
|
current?: Route,
|
|
@@ -63,19 +69,32 @@ export declare class KduRouter {
|
|
|
63
69
|
static install: PluginFunction<never>
|
|
64
70
|
static version: string
|
|
65
71
|
|
|
66
|
-
static isNavigationFailure: (
|
|
67
|
-
|
|
72
|
+
static isNavigationFailure: (
|
|
73
|
+
error: any,
|
|
74
|
+
type?: number
|
|
75
|
+
) => error is NavigationFailure
|
|
76
|
+
static NavigationFailureType: {
|
|
77
|
+
[k in keyof typeof NavigationFailureType]: NavigationFailureType
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
static START_LOCATION: Route
|
|
68
81
|
}
|
|
69
82
|
|
|
70
|
-
export enum
|
|
71
|
-
redirected =
|
|
72
|
-
aborted =
|
|
73
|
-
cancelled =
|
|
74
|
-
duplicated =
|
|
83
|
+
export enum NavigationFailureType {
|
|
84
|
+
redirected = 2,
|
|
85
|
+
aborted = 4,
|
|
86
|
+
cancelled = 8,
|
|
87
|
+
duplicated = 16
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export interface NavigationFailure extends Error {
|
|
91
|
+
to: Route
|
|
92
|
+
from: Route
|
|
93
|
+
type: number
|
|
75
94
|
}
|
|
76
95
|
|
|
77
96
|
type Position = { x: number; y: number }
|
|
78
|
-
type PositionResult = Position | { selector: string; offset?: Position } | void
|
|
97
|
+
type PositionResult = Position | { selector: string; offset?: Position, behavior?: ScrollBehavior } | void
|
|
79
98
|
|
|
80
99
|
export interface RouterOptions {
|
|
81
100
|
routes?: RouteConfig[]
|
|
@@ -107,7 +126,7 @@ interface _RouteConfigBase {
|
|
|
107
126
|
children?: RouteConfig[]
|
|
108
127
|
redirect?: RedirectOption
|
|
109
128
|
alias?: string | string[]
|
|
110
|
-
meta?:
|
|
129
|
+
meta?: RouteMeta
|
|
111
130
|
beforeEnter?: NavigationGuard
|
|
112
131
|
caseSensitive?: boolean
|
|
113
132
|
pathToRegexpOptions?: PathToRegexpOptions
|
|
@@ -134,6 +153,25 @@ export interface RouteRecord {
|
|
|
134
153
|
parent?: RouteRecord
|
|
135
154
|
redirect?: RedirectOption
|
|
136
155
|
matchAs?: string
|
|
156
|
+
meta: RouteMeta
|
|
157
|
+
beforeEnter?: (
|
|
158
|
+
route: Route,
|
|
159
|
+
redirect: (location: RawLocation) => void,
|
|
160
|
+
next: () => void
|
|
161
|
+
) => any
|
|
162
|
+
props:
|
|
163
|
+
| boolean
|
|
164
|
+
| Object
|
|
165
|
+
| RoutePropsFunction
|
|
166
|
+
| Dictionary<boolean | Object | RoutePropsFunction>
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export interface RouteRecordPublic {
|
|
170
|
+
path: string
|
|
171
|
+
components: Dictionary<Component>
|
|
172
|
+
instances: Dictionary<Kdu>
|
|
173
|
+
name?: string
|
|
174
|
+
redirect?: RedirectOption
|
|
137
175
|
meta: any
|
|
138
176
|
beforeEnter?: (
|
|
139
177
|
route: Route,
|
|
@@ -147,6 +185,7 @@ export interface RouteRecord {
|
|
|
147
185
|
| Dictionary<boolean | Object | RoutePropsFunction>
|
|
148
186
|
}
|
|
149
187
|
|
|
188
|
+
|
|
150
189
|
export interface Location {
|
|
151
190
|
name?: string
|
|
152
191
|
path?: string
|
|
@@ -166,5 +205,7 @@ export interface Route {
|
|
|
166
205
|
fullPath: string
|
|
167
206
|
matched: RouteRecord[]
|
|
168
207
|
redirectedFrom?: string
|
|
169
|
-
meta?:
|
|
208
|
+
meta?: RouteMeta
|
|
170
209
|
}
|
|
210
|
+
|
|
211
|
+
export interface RouteMeta extends Record<string | number | symbol, any> {}
|