router-dom
Advanced tools
| # Changelog | ||
| ## 3.0.0 2024-09-25 | ||
| - pin path-to-regexp to version 6.3.0 | ||
| - rename restoreScrollOnReload to restoreScroll | ||
| - default restoreScroll to true | ||
| - add improved restoreScroll behavior per route | ||
| - add option `fetchOptions` to router for forms |
+2
-1
@@ -28,3 +28,3 @@ export default class Router { | ||
| [cycles.afterEnter]?(routingProps: RoutingProps): Promise<any> | void; | ||
| restoreScrollOnReload?: boolean; | ||
| restoreScroll?: boolean; | ||
| } | ||
@@ -44,2 +44,3 @@ interface RouteParam extends RouteBasic { | ||
| scrollBehavior?: ScrollBehavior; | ||
| fetchOptions?: RequestInit; | ||
| } | ||
@@ -46,0 +47,0 @@ interface RoutingProps { |
+22
-13
@@ -17,3 +17,3 @@ import { pathToRegexp, match } from "path-to-regexp"; | ||
| // Reload -> store scrollPosition | ||
| addEventListener("beforeunload", () => sessionStorage.setItem(storageKey, `${scrollX} ${scrollY}`)); | ||
| addEventListener("beforeunload", () => sessionStorage.setItem(`${storageKey}-${location.pathname + location.search}`, `${scrollX} ${scrollY}`)); | ||
| export default class Router { | ||
@@ -41,2 +41,3 @@ options; | ||
| return { | ||
| restoreScroll: true, | ||
| ...route, | ||
@@ -87,2 +88,8 @@ path: pathToRegexp(base + route.path), | ||
| if (route) { | ||
| // Store position | ||
| let currStorageKey; | ||
| if (this.oldRoute) { | ||
| currStorageKey = `${storageKey}-${from}`; | ||
| sessionStorage.setItem(currStorageKey, `${scrollX} ${scrollY}`); | ||
| } | ||
| try { | ||
@@ -106,8 +113,2 @@ const { params } = match(route.originalPath, { | ||
| }; | ||
| // Reset Scroll, just like Browser | ||
| scrollTo({ | ||
| top: 0, | ||
| left: 0, | ||
| behavior: this.options.scrollBehavior || "auto", | ||
| }); | ||
| // Trigger leave | ||
@@ -152,2 +153,3 @@ if (this.oldRoute) { | ||
| } | ||
| currStorageKey = `${storageKey}-${to}`; | ||
| // Trigger afterEnter | ||
@@ -165,12 +167,9 @@ await route["afterEnter" /* cycles.afterEnter */]?.(props); | ||
| finally { | ||
| dispatchEvent(new Event("afterRouting")); | ||
| // Reload -> restore scroll position | ||
| if (!this.oldRoute && | ||
| route.restoreScrollOnReload && | ||
| sessionStorage.getItem(storageKey)) { | ||
| if (route.restoreScroll && sessionStorage.getItem(currStorageKey)) { | ||
| const [left, top] = sessionStorage | ||
| .getItem(storageKey) | ||
| .getItem(currStorageKey) | ||
| .split(" ") | ||
| .map(Number); | ||
| sessionStorage.removeItem(storageKey); | ||
| sessionStorage.removeItem(currStorageKey); | ||
| scrollTo({ | ||
@@ -182,2 +181,11 @@ top, | ||
| } | ||
| else { | ||
| // Reset Scroll, just like Browser | ||
| scrollTo({ | ||
| top: 0, | ||
| left: 0, | ||
| behavior: this.options.scrollBehavior || "auto", | ||
| }); | ||
| } | ||
| dispatchEvent(new Event("afterRouting")); | ||
| } | ||
@@ -248,2 +256,3 @@ } | ||
| : {}), | ||
| ...router.options.fetchOptions, | ||
| }) | ||
@@ -250,0 +259,0 @@ .then((res) => router.options.formHandler(res, e)) |
+2
-2
| MIT License | ||
| Copyright (c) 2021 Fabian Krutsch | ||
| Copyright (c) 2021 Fabian Klingenberg | ||
@@ -21,2 +21,2 @@ Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
| SOFTWARE. | ||
| SOFTWARE. |
+9
-9
| { | ||
| "name": "router-dom", | ||
| "version": "2.2.11", | ||
| "version": "3.0.0", | ||
| "description": "A lightweight router for everyone", | ||
@@ -23,12 +23,12 @@ "type": "module", | ||
| "update": "npx npm-check-updates -u && npx typesync && npm i && npm outdated", | ||
| "test": "tsc && esbuild dist/router.js --bundle --format=esm --outfile=src/bundle.js && wtr src/tests/*.html --node-resolve --playwright --browsers chromium firefox webkit && node -e \"fs.rmSync('src/bundle.js')\"" | ||
| "test": "tsc && wtr src/tests/*.html --node-resolve --playwright --browsers chromium firefox webkit" | ||
| }, | ||
| "author": "Fabian Krutsch <f.krutsch@gmx.de> (https://krutsch.netlify.app/)", | ||
| "author": "Fabian Klingenberg <klingenberg.fabian@gmx.de> (https://klingenberg.netlify.app/)", | ||
| "license": "MIT", | ||
| "devDependencies": { | ||
| "@esm-bundle/chai": "^4.3.4", | ||
| "@web/test-runner": "^0.16.1", | ||
| "@web/test-runner-playwright": "^0.10.0", | ||
| "esbuild": "^0.17.18", | ||
| "typescript": "^5.0.4" | ||
| "@web/test-runner": "^0.19.0", | ||
| "@web/test-runner-playwright": "^0.11.0", | ||
| "esbuild": "^0.24.0", | ||
| "typescript": "^5.6.2" | ||
| }, | ||
@@ -41,5 +41,5 @@ "repository": { | ||
| "dependencies": { | ||
| "hydro-js": "^1.5.14", | ||
| "path-to-regexp": "^6.2.1" | ||
| "hydro-js": "^1.5.21", | ||
| "path-to-regexp": "6.3.0" | ||
| } | ||
| } |
+3
-3
@@ -59,4 +59,4 @@ # router-dom | ||
| Either a template or and element will be rendered in your element with attribute `data-outlet`.<br> | ||
| You can also specifiy one-level of children.<br> One more interesting property is the `restoreScrollOnReload`. | ||
| The second argument is the optional object options: it can take a general errorHandler, a formHandler and the scrollBehavior. If there is a formHandler, form submits will handled via attributes on the form element and fetch. | ||
| You can also specifiy one-level of children.<br> One more interesting property is the `restoreScroll`. | ||
| The second argument is the optional object options: it can take a general errorHandler, a formHandler, a fetchOptions for the form and the scrollBehavior. If there is a formHandler, form submits will handled via attributes on the form element and fetch. | ||
@@ -67,3 +67,3 @@ ```js | ||
| path: "/", | ||
| restoreScrollOnReload: true, | ||
| restoreScroll: true, // defaults to true | ||
| }, | ||
@@ -70,0 +70,0 @@ { |
+28
-18
@@ -27,3 +27,6 @@ import type { MatchResult } from "path-to-regexp"; | ||
| addEventListener("beforeunload", () => | ||
| sessionStorage.setItem(storageKey, `${scrollX} ${scrollY}`) | ||
| sessionStorage.setItem( | ||
| `${storageKey}-${location.pathname + location.search}`, | ||
| `${scrollX} ${scrollY}` | ||
| ) | ||
| ); | ||
@@ -55,2 +58,3 @@ | ||
| return { | ||
| restoreScroll: true, | ||
| ...route, | ||
@@ -110,2 +114,9 @@ path: pathToRegexp(base + route.path), | ||
| if (route) { | ||
| // Store position | ||
| let currStorageKey: string; | ||
| if (this.oldRoute) { | ||
| currStorageKey = `${storageKey}-${from}`; | ||
| sessionStorage.setItem(currStorageKey, `${scrollX} ${scrollY}`); | ||
| } | ||
| try { | ||
@@ -132,9 +143,2 @@ const { params } = match(route.originalPath, { | ||
| // Reset Scroll, just like Browser | ||
| scrollTo({ | ||
| top: 0, | ||
| left: 0, | ||
| behavior: this.options.scrollBehavior || "auto", | ||
| }); | ||
| // Trigger leave | ||
@@ -185,2 +189,3 @@ if (this.oldRoute) { | ||
| } | ||
| currStorageKey = `${storageKey}-${to}`; | ||
@@ -196,15 +201,9 @@ // Trigger afterEnter | ||
| } finally { | ||
| dispatchEvent(new Event("afterRouting")); | ||
| // Reload -> restore scroll position | ||
| if ( | ||
| !this.oldRoute && | ||
| route.restoreScrollOnReload && | ||
| sessionStorage.getItem(storageKey) | ||
| ) { | ||
| if (route.restoreScroll && sessionStorage.getItem(currStorageKey!)) { | ||
| const [left, top] = sessionStorage | ||
| .getItem(storageKey)! | ||
| .getItem(currStorageKey!)! | ||
| .split(" ") | ||
| .map(Number); | ||
| sessionStorage.removeItem(storageKey); | ||
| sessionStorage.removeItem(currStorageKey!); | ||
| scrollTo({ | ||
@@ -215,3 +214,12 @@ top, | ||
| }); | ||
| } else { | ||
| // Reset Scroll, just like Browser | ||
| scrollTo({ | ||
| top: 0, | ||
| left: 0, | ||
| behavior: this.options.scrollBehavior || "auto", | ||
| }); | ||
| } | ||
| dispatchEvent(new Event("afterRouting")); | ||
| } | ||
@@ -295,2 +303,3 @@ } | ||
| : {}), | ||
| ...router.options.fetchOptions, | ||
| }) | ||
@@ -379,3 +388,3 @@ .then((res) => router.options.formHandler!(res, e)) | ||
| [cycles.afterEnter]?(routingProps: RoutingProps): Promise<any> | void; | ||
| restoreScrollOnReload?: boolean; | ||
| restoreScroll?: boolean; | ||
| } | ||
@@ -395,2 +404,3 @@ interface RouteParam extends RouteBasic { | ||
| scrollBehavior?: ScrollBehavior; | ||
| fetchOptions?: RequestInit; | ||
| } | ||
@@ -397,0 +407,0 @@ interface RoutingProps { |
@@ -35,2 +35,3 @@ <!DOCTYPE html> | ||
| }, | ||
| fetchOptions: { signal: AbortSignal.timeout(1) }, | ||
| } | ||
@@ -46,5 +47,5 @@ ); | ||
| $("#submit").click(); | ||
| await sleep(1900); | ||
| expect(window.form).to.be.not.undefined; | ||
| expect(window.formEvent).to.be.not.undefined; | ||
| await sleep(50); | ||
| expect(window.err).to.not.be.undefined; | ||
| expect(window.errEvent).to.not.be.undefined; | ||
| }); | ||
@@ -69,2 +70,3 @@ }); | ||
| </form> | ||
| <form action="https://httpbin.org/asdasdas" method="get"> | ||
@@ -71,0 +73,0 @@ <label for="name2">Enter your name: </label> |
@@ -28,3 +28,2 @@ <!DOCTYPE html> | ||
| </div>`, | ||
| restoreScrollOnReload: true, | ||
| }, | ||
@@ -56,2 +55,3 @@ ]); | ||
| </div>`, | ||
| restoreScroll: false, | ||
| }, | ||
@@ -105,2 +105,38 @@ ]); | ||
| }); | ||
| it("does restore the scroll when scrolling, navigating and navigating back", async () => { | ||
| document.body.insertAdjacentHTML( | ||
| "beforeend", | ||
| `<div style="height: 2000px;"> | ||
| <a href="/a">A</a> | ||
| <a href="/b">B</a> | ||
| <div data-outlet></div> | ||
| </div>` | ||
| ); | ||
| router = new Router([ | ||
| { | ||
| path: "/a", | ||
| element: `<div>A Content</div>`, | ||
| }, | ||
| { | ||
| path: "/b", | ||
| element: `<div>B Content</div>`, | ||
| }, | ||
| ]); | ||
| window.router = router; | ||
| router.go("/a"); | ||
| await sleep(50); | ||
| window.scrollTo(0, 500); | ||
| router.go("/b"); | ||
| await sleep(50); | ||
| expect(window.scrollY).to.equal(0); | ||
| history.back(); | ||
| await sleep(50); | ||
| expect(window.scrollY).to.equal(500); | ||
| }); | ||
| }); | ||
@@ -107,0 +143,0 @@ }); |
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
62764
3.54%15
7.14%752
2.73%Updated
Updated