-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.mjs
122 lines (109 loc) · 2.88 KB
/
index.mjs
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
const generateVariablesAndRegularExpression = (path) => {
const regex = /{[a-zA-Z]*}/g;
// Get variable names.
const variableNames = [...path.matchAll(regex)].map((match) => {
return match[0].replace(/[{|}]/g, '');
});
// Get matcher.
const regularExpression = new RegExp(
path.replaceAll(regex, `([a-zA-Z1-9\\-]*)`).replaceAll(/\//g, '/')
);
return {
variableNames,
regex: regularExpression,
};
};
const getVariables = (path, { variables, regex }) => {
const variableValues = [...path.match(regex)].filter((value) => value !== '');
variableValues.shift();
return variableValues.map((value, index) => ({
name: variables[index],
value,
}));
};
const createRouteIndex = (route) => {
const { variableNames, regex } = generateVariablesAndRegularExpression(
route.path
);
return {
...route,
variables: variableNames,
regex,
};
};
const createRouteIndexes = (routes) => routes.map(createRouteIndex);
export class RouterElement extends HTMLElement {
constructor() {
super();
if (this.constructor == RouterElement) {
throw new Error("Abstract classes can't be instantiated.");
}
}
indexRoutes() {
if (!this.routes)
throw new Error(
'Router element needs routes defined in the connected callback.'
);
this._indexedRoutes = createRouteIndexes(
this.routes.sort((a, b) => {
return a.path.length < b.path.length ? 1 : -1;
})
);
}
get indexedRoutes() {
return this._indexedRoutes;
}
onNavigate() {
const path = window.location.pathname;
if (path !== this.currentPath) {
this.currentPath = path;
this.render();
}
}
notFound() {
this.innerHTML = `
<h1>Unable to find page</h1>
<div>
I'm sorry the page you're looking for was unable to be found.
</div>
`;
}
connectedCallback() {
this.indexRoutes();
this.currentPath = window.location.pathname;
this.render();
window.addEventListener('navigate', this.onNavigate.bind(this));
}
disconnectedCallback() {
window.removeEventListener('navigate', this.onNavigate.bind(this));
}
attributeChangedCallback(name, oldValue, newValue) {
if (oldValue !== newValue) {
this.indexRoutes();
this.render();
}
}
async render() {
const route = this.indexedRoutes.find((indexedRoute) =>
indexedRoute.regex.test(this.currentPath)
);
if (
!route ||
(route.path === '' && this.currentPath !== '' && this.currentPath !== '/')
) {
this.notFound();
return;
}
const variables = getVariables(this.currentPath, route);
const template = document.createElement('template');
try {
template.innerHTML = await route.go({
variables,
});
this.innerHTML = '';
this.appendChild(template.content.cloneNode(true));
} catch (e) {
throw new Error(e);
}
}
}