|
65 | 65 |
|
66 | 66 | function _resolvePath(to) {
|
67 | 67 | let newFullPath;
|
68 |
| - if (to.startsWith('/')) { // Absolute within the base |
69 |
| - newFullPath = (router.base.replace(/\/$/, '') + to).replace(/\/\//g, '/'); |
| 68 | + if (to.startsWith('/')) { // Path starts with a slash |
| 69 | + if (router.base !== '/' && (to === router.base || to.startsWith(router.base + '/'))) { |
| 70 | + // 'to' is an absolute path that already includes the base of this nested router |
| 71 | + newFullPath = to; |
| 72 | + } else { |
| 73 | + // 'to' is absolute relative to this router's base, or this is the root router |
| 74 | + newFullPath = (router.base.replace(/\/$/, '') + to).replace(/\/\//g, '/'); |
| 75 | + } |
70 | 76 | } else { // Relative to current path within the base
|
71 | 77 | const currentDir = router.path.substring(0, router.path.lastIndexOf('/') + 1);
|
72 | 78 | newFullPath = (router.base.replace(/\/$/, '') + '/' + currentDir + to).replace(/\/\//g, '/');
|
|
85 | 91 | return newFullPath.replace(/\/\//g, '/') || '/';
|
86 | 92 | }
|
87 | 93 |
|
88 |
| - async function _runGuards(toFullPath, fromFullPath, navigationAction) { |
| 94 | + async function _runGuards(toFullPath, fromFullPath, toQueryForGuards, navigationAction) { // Added toQueryForGuards |
89 | 95 | if (router.beforeEach.length === 0) {
|
90 | 96 | navigationAction();
|
91 | 97 | return;
|
92 | 98 | }
|
93 | 99 |
|
94 |
| - const toQuery = typeof window !== 'undefined' ? _parseQuery(window.location.search) : {}; // Or parse from toFullPath if it includes query |
| 100 | + // const toQuery = typeof window !== 'undefined' ? _parseQuery(window.location.search) : {}; // OLD |
95 | 101 | const fromQuery = router.fromPath ? _parseQuery(new URL(router.fromPath, window.location.origin).search) : {};
|
96 | 102 |
|
97 | 103 | // Extract params - this is tricky without knowing the matching route yet.
|
98 | 104 | // For guards, `params` might be less critical or require a preliminary match.
|
99 | 105 | // For now, passing empty params. A more advanced version could pre-match.
|
100 |
| - const toRouteInfo = { path: toFullPath, params: {}, query: toQuery }; |
| 106 | + const toRouteInfo = { path: toFullPath, params: {}, query: toQueryForGuards }; // Use passed toQueryForGuards |
101 | 107 | const fromRouteInfo = router.fromPath ? { path: fromFullPath, params: {}, query: fromQuery } : null;
|
102 | 108 |
|
103 | 109 | let i = 0;
|
|
144 | 150 | // If url prop was used, initialQuery has it. Otherwise, it's {}.
|
145 | 151 | }
|
146 | 152 | }
|
| 153 | +
|
| 154 | + function removeQueryParams(keysToRemove) { |
| 155 | + if (typeof window !== 'undefined') { |
| 156 | + const currentSearch = window.location.search; // Get current query from actual URL |
| 157 | + const params = new URLSearchParams(currentSearch); |
| 158 | + |
| 159 | + keysToRemove.forEach(key => { |
| 160 | + params.delete(key); |
| 161 | + }); |
| 162 | + |
| 163 | + const newSearchString = params.toString(); |
| 164 | + const newQueryPart = newSearchString ? `?${newSearchString}` : ''; |
| 165 | +
|
| 166 | + // Use the router's own navigate method. |
| 167 | + // router.path is the path relative to this router's base. |
| 168 | + // We pass the current relative path along with the new query string. |
| 169 | + // This ensures guards are run and state is updated consistently. |
| 170 | + // Using replace: true is often better for query-only changes. |
| 171 | + router.navigate(router.path + newQueryPart, { replace: true }); |
| 172 | + } |
| 173 | + } |
147 | 174 |
|
148 | 175 | // This function is called by popstate or after successful guards in navigate
|
149 | 176 | function _performUpdate(fullPathToNavigate) {
|
|
155 | 182 | async function handlePopState() {
|
156 | 183 | if (typeof window !== 'undefined') {
|
157 | 184 | const targetFullPath = window.location.pathname; // This includes path only
|
| 185 | + const currentQuery = _parseQuery(window.location.search); // Parse query at the time of popstate |
158 | 186 | // Query will be re-parsed from window.location.search in _updateRouterState
|
159 |
| - await _runGuards(targetFullPath, router.fullPath, () => { |
| 187 | + await _runGuards(targetFullPath, router.fullPath, currentQuery, () => { // Pass currentQuery |
160 | 188 | _performUpdate(targetFullPath);
|
161 | 189 | });
|
162 | 190 | }
|
163 | 191 | }
|
164 | 192 |
|
165 | 193 | async function navigate(to, { replace = false } = {}) {
|
166 | 194 | if (typeof window !== 'undefined') {
|
167 |
| - const targetFullPath = _resolvePath(to); // Resolves path part of 'to' |
168 |
| - let targetSearch = window.location.search; // Default to current search query |
169 |
| -
|
170 |
| - // If 'to' includes a query string, use that instead. |
171 |
| - const toParts = to.split('?'); |
172 |
| - if (toParts.length > 1) { |
173 |
| - targetSearch = '?' + toParts[1]; |
| 195 | + let pathInput = to; |
| 196 | + let searchInput = ''; // e.g., "?foo=bar" or "" |
| 197 | + const queryIndex = to.indexOf('?'); |
| 198 | + if (queryIndex !== -1) { |
| 199 | + pathInput = to.substring(0, queryIndex); |
| 200 | + searchInput = to.substring(queryIndex); |
174 | 201 | }
|
175 |
| - |
176 |
| - await _runGuards(targetFullPath, router.fullPath, () => { |
| 202 | +
|
| 203 | + const targetFullPath = _resolvePath(pathInput); // pathInput is now path-only |
| 204 | + const targetSearch = searchInput; // searchInput is query from 'to', or empty |
| 205 | +
|
| 206 | + const toQueryForGuards = _parseQuery(targetSearch); // Parse the query we intend to navigate to |
| 207 | +
|
| 208 | + await _runGuards(targetFullPath, router.fullPath, toQueryForGuards, () => { |
177 | 209 | router.fromPath = router.fullPath; // Set fromPath before history change
|
178 | 210 | if (replace) {
|
179 | 211 | window.history.replaceState({}, "", targetFullPath + targetSearch);
|
180 | 212 | } else {
|
181 | 213 | window.history.pushState({}, "", targetFullPath + targetSearch);
|
182 | 214 | }
|
183 |
| - _performUpdate(targetFullPath); // Update internal state after history change. _updateRouterState will parse new search. |
| 215 | + // _performUpdate will use targetFullPath and then re-read window.location.search |
| 216 | + // which is fine as history API has just updated it. |
| 217 | + _performUpdate(targetFullPath); |
184 | 218 | });
|
185 | 219 | }
|
186 | 220 | }
|
|
207 | 241 | router.navigate = navigate;
|
208 | 242 | router.getQueryParam = (key) => router.query[key];
|
209 | 243 | router.hasQueryParam = (key) => Object.prototype.hasOwnProperty.call(router.query, key);
|
| 244 | + router.removeQueryParams = removeQueryParams; |
210 | 245 |
|
211 | 246 | setContext('router', router);
|
212 | 247 | </script>
|
|
0 commit comments