-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #10 from Netcentric/router
Initial Router version
- Loading branch information
Showing
4 changed files
with
211 additions
and
0 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
# eddys-router | ||
SPA Style router with soft naviagtion betweeen pages | ||
|
||
## Description | ||
This is a simple router that uses the `fetch` API to load content from the server and update just the `<main>` tag. | ||
It also uses the `history` API to update the URL and listen for changes to the URL. | ||
|
||
Excluded paths can be configured in the router.js file to prevent the router from loading content for certain paths. This is useful for paths that should be loaded in the traditional way, (for example, a page that contains a react app using react-router) | ||
|
||
If other components need to be updated when the URL changes (for example the navigation), they can listen for the `router:navgate` event on the `window` object. (see `listenToNavigationeEvents()` in `header.js` for an example) | ||
|
||
## How to use | ||
1. Install the router.js to your repository | ||
|
||
```bash | ||
npm i @netcentric/eddys-router | ||
``` | ||
|
||
2. Load the router script in your head.html file | ||
|
||
```html | ||
<script src="/libs/eddys-router/router.js" type="module"></script> | ||
``` | ||
|
||
3. Configure the excluded paths | ||
|
||
```javascript | ||
window.router = { | ||
excludedPaths: [ | ||
'/content/excluded-path', | ||
'/content/excluded-path-2' | ||
]; | ||
}; | ||
``` | ||
|
||
4. If needed listen for the `router:navgate` event in other components | ||
|
||
```javascript | ||
window.addEventListener('router:navgate', (event) => { | ||
// do something with the event | ||
}); | ||
``` | ||
|
||
## Demo Links | ||
|
||
- Preview: https://main--eddys-router--nfarmache-nc.hlx.page/ | ||
- Live: https://main--eddys-router--nfarmache-nc.hlx.live/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
import { decorateMain } from '../scripts/scripts.js'; | ||
import { loadBlocks, decorateTemplateAndTheme } from '../scripts/aem.js'; | ||
|
||
|
||
|
||
function isPathExcluded(path) { | ||
return ((window.router || {}).excludedPaths || []).some((excludedPath) => ( | ||
path === excludedPath | ||
|| path.startsWith(`${excludedPath}/`) | ||
)); | ||
} | ||
|
||
function emitNavigateEvent() { | ||
window.dispatchEvent( | ||
new CustomEvent('router:navgate', { | ||
bubbles: true, | ||
}), | ||
); | ||
} | ||
|
||
async function render(html) { | ||
const main = document.querySelector('body>main'); | ||
const head = document.querySelector('html>head'); | ||
const newDocument = new DOMParser().parseFromString(html, 'text/html'); | ||
const newHead = newDocument.querySelector('html>head'); | ||
|
||
// replace meta tags | ||
[...head.querySelectorAll('meta')].forEach((tag) => tag.remove()); | ||
const metaHtml = [...newHead.querySelectorAll('meta')].map((meta) => (meta).outerHTML).join('\n'); | ||
head.querySelector('title').insertAdjacentHTML('afterend', metaHtml); | ||
|
||
// replace title | ||
document.title = newDocument.title; | ||
|
||
// replace main | ||
main.innerHTML = newDocument.querySelector('body>main').innerHTML; | ||
main.classList.add('hidden'); | ||
document.body.className = 'appear'; | ||
decorateTemplateAndTheme(); | ||
decorateMain(main); | ||
await loadBlocks(main); | ||
main.classList.remove('hidden'); | ||
emitNavigateEvent(); | ||
} | ||
|
||
async function navigate(path, shouldPushState = true) { | ||
fetch(path) | ||
.then((response) => { | ||
const contentType = response.headers.get('content-type'); | ||
if (!response.ok || !contentType || !contentType.includes('text/html')) { | ||
window.location.href = path; | ||
} | ||
|
||
return response.text(); | ||
}) | ||
.then(async (html) => { | ||
if (shouldPushState) { | ||
window.history.pushState({}, '', path); | ||
} | ||
|
||
await render(html); | ||
}); | ||
} | ||
|
||
function checkUrl(href, force = false) { | ||
const url = new URL(href, document.location.href); | ||
const path = `${url.pathname}${url.search}${url.hash}`; | ||
const simplePath = url.pathname; | ||
|
||
if (force) { | ||
return { path, shouldFetchPage: true }; | ||
} | ||
|
||
// check origin | ||
if (url.origin !== document.location.origin) { | ||
return { shouldFetchPage: false }; | ||
} | ||
|
||
// check if it's the same page | ||
if (simplePath === document.location.pathname) { | ||
return { shouldFetchPage: false }; | ||
} | ||
|
||
// check excluded paths | ||
if (isPathExcluded(simplePath)) { | ||
return { shouldFetchPage: false }; | ||
} | ||
|
||
// ok | ||
return { path, shouldFetchPage: true }; | ||
} | ||
|
||
const clickHandler = (event) => { | ||
const { target } = event; | ||
if (target.tagName !== 'A' || typeof target.href === 'undefined') return; | ||
const { shouldFetchPage, path } = checkUrl(target.href); | ||
if (!shouldFetchPage) return; | ||
|
||
event.preventDefault(); | ||
navigate(path); | ||
}; | ||
|
||
const popstateHandler = () => { | ||
const { path } = checkUrl(document.location.href, true); | ||
navigate(path, false); | ||
}; | ||
|
||
function router() { | ||
if (isPathExcluded(document.location.pathname)) { | ||
return; | ||
} | ||
|
||
document.addEventListener('click', clickHandler); | ||
window.addEventListener('popstate', popstateHandler); | ||
} | ||
|
||
router(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
{ | ||
"name": "@netcentric/eddys-router", | ||
"version": "1.0.0", | ||
"description": "Plugin to add SPA style routing to AEM EdgeDelivery sites.", | ||
"license": "Apache-2.0", | ||
"private": false, | ||
"publishConfig": { | ||
"access": "public" | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "git+https://github.com/netcentric/eddys-collection.git" | ||
}, | ||
"main": "router.js", | ||
"author": "Cognizant Netcentric (https://www.netcentric.biz)", | ||
"scripts": { | ||
"postinstall": "node install.js", | ||
"release": "semantic-release", | ||
"test": "echo \"eddys-router\"" | ||
}, | ||
"browserslist": [ | ||
"last 2 versions", | ||
"not ie > 0", | ||
"not ie_mob > 0", | ||
"not dead", | ||
"not OperaMini all" | ||
], | ||
"engines": { | ||
"node": ">=18.0.0" | ||
}, | ||
"files": [ | ||
"./libs", | ||
"install.js" | ||
] | ||
} |