forked from remix-run/react-router
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathisActive.js
147 lines (124 loc) · 3.92 KB
/
isActive.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
import { matchPattern } from './PatternUtils'
function deepEqual(a, b) {
if (a == b)
return true
if (a == null || b == null)
return false
if (Array.isArray(a)) {
return (
Array.isArray(b) &&
a.length === b.length &&
a.every((item, index) => deepEqual(item, b[index]))
)
}
if (typeof a === 'object') {
for (let p in a) {
if (!Object.prototype.hasOwnProperty.call(a, p)) {
continue
}
if (a[p] === undefined) {
if (b[p] !== undefined) {
return false
}
} else if (!Object.prototype.hasOwnProperty.call(b, p)) {
return false
} else if (!deepEqual(a[p], b[p])) {
return false
}
}
return true
}
return String(a) === String(b)
}
/**
* Returns true if the current pathname matches the supplied one, net of
* leading and trailing slash normalization. This is sufficient for an
* indexOnly route match.
*/
function pathIsActive(pathname, currentPathname) {
// Normalize leading slash for consistency. Leading slash on pathname has
// already been normalized in isActive. See caveat there.
if (currentPathname.charAt(0) !== '/') {
currentPathname = `/${currentPathname}`
}
// Normalize the end of both path names too. Maybe `/foo/` shouldn't show
// `/foo` as active, but in this case, we would already have failed the
// match.
if (pathname.charAt(pathname.length - 1) !== '/') {
pathname += '/'
}
if (currentPathname.charAt(currentPathname.length - 1) !== '/') {
currentPathname += '/'
}
return currentPathname === pathname
}
/**
* Returns true if the given pathname matches the active routes and params.
*/
function routeIsActive(pathname, routes, params) {
let remainingPathname = pathname, paramNames = [], paramValues = []
// for...of would work here but it's probably slower post-transpilation.
for (let i = 0, len = routes.length; i < len; ++i) {
const route = routes[i]
const pattern = route.path || ''
if (pattern.charAt(0) === '/') {
remainingPathname = pathname
paramNames = []
paramValues = []
}
if (remainingPathname !== null && pattern) {
const matched = matchPattern(pattern, remainingPathname)
if (matched) {
remainingPathname = matched.remainingPathname
paramNames = [ ...paramNames, ...matched.paramNames ]
paramValues = [ ...paramValues, ...matched.paramValues ]
} else {
remainingPathname = null
}
if (remainingPathname === '') {
// We have an exact match on the route. Just check that all the params
// match.
// FIXME: This doesn't work on repeated params.
return paramNames.every((paramName, index) => (
String(paramValues[index]) === String(params[paramName])
))
}
}
}
return false
}
/**
* Returns true if all key/value pairs in the given query are
* currently active.
*/
function queryIsActive(query, activeQuery) {
if (activeQuery == null)
return query == null
if (query == null)
return true
return deepEqual(query, activeQuery)
}
/**
* Returns true if a <Link> to the given pathname/query combination is
* currently active.
*/
export default function isActive(
{ pathname, query }, indexOnly, currentLocation, routes, params
) {
if (currentLocation == null)
return false
// TODO: This is a bit ugly. It keeps around support for treating pathnames
// without preceding slashes as absolute paths, but possibly also works
// around the same quirks with basenames as in matchRoutes.
if (pathname.charAt(0) !== '/') {
pathname = `/${pathname}`
}
if (!pathIsActive(pathname, currentLocation.pathname)) {
// The path check is necessary and sufficient for indexOnly, but otherwise
// we still need to check the routes.
if (indexOnly || !routeIsActive(pathname, routes, params)) {
return false
}
}
return queryIsActive(query, currentLocation.query)
}