lightweight-router 1.0.0
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/LICENSE +2 -0
- package/README.md +13 -0
- package/package.json +12 -0
- package/package.tmp.json +0 -0
- package/src/index.js +7 -0
- package/src/router.js +96 -0
- package/src/utils.js +20 -0
package/LICENSE
ADDED
package/README.md
ADDED
package/package.json
ADDED
package/package.tmp.json
ADDED
|
File without changes
|
package/src/index.js
ADDED
package/src/router.js
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { fetchContent } from './utils';
|
|
2
|
+
|
|
3
|
+
let linkData = new Proxy({}, {
|
|
4
|
+
set: (target, property, value) => {
|
|
5
|
+
target[property] = value;
|
|
6
|
+
const router = document.querySelector("router");
|
|
7
|
+
const currentRoute = router?.querySelector(`route[path="${globalThis.location.pathname}"]`) ||
|
|
8
|
+
router?.querySelector(`route[path="/default"]`);
|
|
9
|
+
|
|
10
|
+
if (currentRoute && !currentRoute.innerHTML && property === globalThis.location.href) {
|
|
11
|
+
currentRoute.innerHTML = value;
|
|
12
|
+
if (typeof onRouteChange === 'function') onRouteChange(currentRoute);
|
|
13
|
+
}
|
|
14
|
+
return true;
|
|
15
|
+
},
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
let onRouteChange;
|
|
19
|
+
|
|
20
|
+
export const setRouteChangeHandler = (handler) => {
|
|
21
|
+
onRouteChange = handler;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const fetchAndSaveContent = async (link) => {
|
|
25
|
+
linkData[link.href] = "";
|
|
26
|
+
linkData[link.href] = await fetchContent(link.href);
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const handleLinkHover = async (event) => {
|
|
30
|
+
const link = event.target;
|
|
31
|
+
if (!linkData[link.href]) await fetchAndSaveContent(link);
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const handleLinkIntersection = (entries, observer) => {
|
|
35
|
+
entries.forEach(entry => {
|
|
36
|
+
if (entry.isIntersecting) {
|
|
37
|
+
const link = entry.target;
|
|
38
|
+
if (!linkData[link.href]) {
|
|
39
|
+
fetchAndSaveContent(link);
|
|
40
|
+
observer.unobserve(link);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const handlePopState = async () => {
|
|
47
|
+
const router = document.querySelector("router");
|
|
48
|
+
if (!router) return;
|
|
49
|
+
|
|
50
|
+
const currentRoute = router.querySelector(`route[path="${globalThis.location.pathname}"]`) ||
|
|
51
|
+
router.querySelector(`route[path="/default"]`);
|
|
52
|
+
|
|
53
|
+
router.querySelectorAll("route").forEach(route => (route.style.display = "none"));
|
|
54
|
+
currentRoute.style.display = "contents";
|
|
55
|
+
|
|
56
|
+
if (!currentRoute.innerHTML) {
|
|
57
|
+
currentRoute.innerHTML = await linkData[globalThis.location.href];
|
|
58
|
+
if (typeof onRouteChange === 'function') onRouteChange(currentRoute);
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const handleLinkClick = (e) => {
|
|
63
|
+
if (e.target.tagName === "A" && e.target.href) {
|
|
64
|
+
const href = new URL(e.target.href).pathname;
|
|
65
|
+
if (href.startsWith("/")) {
|
|
66
|
+
e.preventDefault();
|
|
67
|
+
globalThis.history.pushState(null, null, e.target.href);
|
|
68
|
+
globalThis.dispatchEvent(new Event("popstate"));
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
export const observeLinks = (observer) => {
|
|
74
|
+
const saveDataOn = navigator.connection && navigator.connection.saveData;
|
|
75
|
+
const links = document.querySelectorAll("a");
|
|
76
|
+
|
|
77
|
+
links.forEach(link => {
|
|
78
|
+
if (link.getAttribute("prefetch") !== "onHover" && !saveDataOn) observer.observe(link);
|
|
79
|
+
});
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
export const initializeRouter = () => {
|
|
83
|
+
if (typeof document === "undefined") return;
|
|
84
|
+
|
|
85
|
+
globalThis.addEventListener("popstate", handlePopState);
|
|
86
|
+
document.addEventListener("click", handleLinkClick);
|
|
87
|
+
|
|
88
|
+
document.body.addEventListener("mouseover", (event) => {
|
|
89
|
+
if (event.target.tagName === "A" && event.target.getAttribute("prefetch") === "onHover") {
|
|
90
|
+
handleLinkHover(event);
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
const observer = new IntersectionObserver(handleLinkIntersection, { root: null, threshold: 0.5 });
|
|
95
|
+
observeLinks(observer);
|
|
96
|
+
};
|
package/src/utils.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export const getPathname = () => {
|
|
2
|
+
const { pathname } = globalThis.location;
|
|
3
|
+
return pathname.startsWith("/") ? pathname.slice(1) : pathname;
|
|
4
|
+
};
|
|
5
|
+
|
|
6
|
+
export const fetchContent = async (url) => {
|
|
7
|
+
const response = await fetch(url, { method: "POST", body: "onlyRoute" });
|
|
8
|
+
if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
|
|
9
|
+
|
|
10
|
+
const reader = response.body.getReader();
|
|
11
|
+
const decoder = new TextDecoder("utf-8");
|
|
12
|
+
let content = "";
|
|
13
|
+
|
|
14
|
+
while (true) {
|
|
15
|
+
const { done, value } = await reader.read();
|
|
16
|
+
if (done) break;
|
|
17
|
+
content += decoder.decode(value);
|
|
18
|
+
}
|
|
19
|
+
return content;
|
|
20
|
+
};
|