@ripetchor/r-router 1.0.0 → 1.0.2
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/README.md +168 -1
- package/dist/index.d.ts +83 -16
- package/dist/index.js +327 -268
- package/package.json +3 -1
package/README.md
CHANGED
|
@@ -1 +1,168 @@
|
|
|
1
|
-
#
|
|
1
|
+
# R-Router
|
|
2
|
+
|
|
3
|
+
HistoryAPI router.
|
|
4
|
+
|
|
5
|
+
**WARNING**: This router is primarily intended for personal use. It may contain bugs or unexpected behavior. Use at your own risk.
|
|
6
|
+
|
|
7
|
+
## Usage
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
import { createRouter, RouterOutlet, RouteContext } from '@ripetchor/r-router';
|
|
11
|
+
|
|
12
|
+
const routes = [
|
|
13
|
+
{
|
|
14
|
+
path: '/',
|
|
15
|
+
component: () => {
|
|
16
|
+
const div = document.createElement('div');
|
|
17
|
+
div.textContent = 'Home';
|
|
18
|
+
return div;
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
path: '/user/:id',
|
|
23
|
+
component: (ctx: RouteContext) => {
|
|
24
|
+
const div = document.createElement('div');
|
|
25
|
+
div.textContent = `User ID: ${ctx.params.id}`;
|
|
26
|
+
return div;
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
path: '/about',
|
|
31
|
+
lazy: async () => {
|
|
32
|
+
const { default: About } = await import('./About');
|
|
33
|
+
return About();
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
path: '/old-path',
|
|
38
|
+
redirectTo: '/new-path',
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
path: '*',
|
|
42
|
+
component: () => {
|
|
43
|
+
const div = document.createElement('div');
|
|
44
|
+
div.textContent = '404 Not Found';
|
|
45
|
+
return div;
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
];
|
|
49
|
+
|
|
50
|
+
const router = createRouter(routes, { defaultTitle: 'My App' });
|
|
51
|
+
|
|
52
|
+
async function init() {
|
|
53
|
+
const outlet: RouterOutlet = await router.initialize();
|
|
54
|
+
|
|
55
|
+
const header = document.createElement('header');
|
|
56
|
+
const nav = document.createElement('nav');
|
|
57
|
+
const links = [
|
|
58
|
+
{ text: 'Home', path: '/' },
|
|
59
|
+
{ text: 'About', path: '/about' },
|
|
60
|
+
{ text: 'User', path: '/user/1' },
|
|
61
|
+
];
|
|
62
|
+
|
|
63
|
+
links.forEach((link) => {
|
|
64
|
+
const a = document.createElement('a');
|
|
65
|
+
a.href = link.path;
|
|
66
|
+
a.textContent = link.text;
|
|
67
|
+
a.addEventListener('click', (e) => {
|
|
68
|
+
e.preventDefault();
|
|
69
|
+
router.push(link.path);
|
|
70
|
+
});
|
|
71
|
+
nav.appendChild(a);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
header.appendChild(nav);
|
|
75
|
+
|
|
76
|
+
const footer = document.createElement('footer');
|
|
77
|
+
|
|
78
|
+
document.body.appendChild(header);
|
|
79
|
+
document.body.appendChild(outlet);
|
|
80
|
+
document.body.appendChild(footer);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
init();
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## API
|
|
87
|
+
|
|
88
|
+
### `createRouter(routes: Route[], options?: RouterOptions): Router`
|
|
89
|
+
|
|
90
|
+
Creates a router instance.
|
|
91
|
+
|
|
92
|
+
- `routes`: Array of route definitions.
|
|
93
|
+
- `options`:
|
|
94
|
+
- `defaultTitle`: Fallback document title.
|
|
95
|
+
- `globalErrorComponent`: Custom error component.
|
|
96
|
+
- `sortRoutesBySpecificity`: Sort routes by specificity (default: `true`).
|
|
97
|
+
- `ignoreSameLocation`: Skip same-path navigation (default: `true`).
|
|
98
|
+
|
|
99
|
+
#### `initialize(): Promise<RouterOutlet>`
|
|
100
|
+
|
|
101
|
+
Sets up popstate listener, navigates to current path, returns root `RouterOutlet`(web-component). Throws an error if it has been initialized.
|
|
102
|
+
|
|
103
|
+
#### `push(path: string, state?: unknown): Promise<void>`
|
|
104
|
+
|
|
105
|
+
Navigates, adding to history.
|
|
106
|
+
|
|
107
|
+
#### `replace(path: string, state?: unknown): Promise<void>`
|
|
108
|
+
|
|
109
|
+
Navigates, replacing history entry.
|
|
110
|
+
|
|
111
|
+
### Route Types
|
|
112
|
+
|
|
113
|
+
- `SyncRoute`: Returns `HTMLElement` via `component(ctx: RouteContext)`.
|
|
114
|
+
- `AsyncRoute`: Returns `Promise<HTMLElement>` via `lazy(ctx: RouteContext)`.
|
|
115
|
+
- `RedirectRoute`: Redirects via `redirectTo`.
|
|
116
|
+
|
|
117
|
+
#### `Route.beforeEnter`
|
|
118
|
+
|
|
119
|
+
Runs before navigation. Return `false` to cancel, string to redirect, or `true` to proceed.
|
|
120
|
+
|
|
121
|
+
```typescript
|
|
122
|
+
{
|
|
123
|
+
path: '/protected',
|
|
124
|
+
beforeEnter: [(ctx: RouteContext) => isAuthenticated() ? true : '/login'],
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
#### `Route.afterEnter`
|
|
129
|
+
|
|
130
|
+
Runs after navigation.
|
|
131
|
+
|
|
132
|
+
```typescript
|
|
133
|
+
{
|
|
134
|
+
path: '/dashboard',
|
|
135
|
+
afterEnter: [(ctx: RouteContext) => console.log('Entered dashboard')],
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### Error Handling
|
|
140
|
+
|
|
141
|
+
Use `errorComponent` in routes or `globalErrorComponent` in options.
|
|
142
|
+
|
|
143
|
+
```typescript
|
|
144
|
+
const routes = [
|
|
145
|
+
{
|
|
146
|
+
path: '*',
|
|
147
|
+
errorComponent: (error, ctx) => {
|
|
148
|
+
const div = document.createElement('div');
|
|
149
|
+
div.textContent = `Error: ${error.message}`;
|
|
150
|
+
return div;
|
|
151
|
+
},
|
|
152
|
+
},
|
|
153
|
+
];
|
|
154
|
+
|
|
155
|
+
// or
|
|
156
|
+
|
|
157
|
+
const router = createRouter(routes, {
|
|
158
|
+
globalErrorComponent: (error) => {
|
|
159
|
+
const div = document.createElement('div');
|
|
160
|
+
div.textContent = 'Global Error';
|
|
161
|
+
return div;
|
|
162
|
+
},
|
|
163
|
+
});
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## License
|
|
167
|
+
|
|
168
|
+
MIT
|
package/dist/index.d.ts
CHANGED
|
@@ -1,58 +1,123 @@
|
|
|
1
1
|
export declare type AfterEnterHook = (ctx: RouteContext) => void | Promise<void>;
|
|
2
2
|
|
|
3
|
-
declare interface AsyncRoute extends BaseRoute {
|
|
4
|
-
redirectTo?: never;
|
|
5
|
-
component?(): never;
|
|
6
|
-
loadComponent(ctx: RouteContext): Promise<HTMLElement>;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
3
|
declare interface BaseRoute {
|
|
10
4
|
path: string;
|
|
11
5
|
title?: string | ((ctx: RouteContext) => string);
|
|
12
6
|
beforeEnter?: BeforeEnterHook[];
|
|
13
7
|
afterEnter?: AfterEnterHook[];
|
|
14
|
-
errorComponent?(error:
|
|
8
|
+
errorComponent?(error: Error, ctx: RouteContext | null): HTMLElement;
|
|
15
9
|
}
|
|
16
10
|
|
|
17
11
|
export declare type BeforeEnterHook = (ctx: RouteContext) => boolean | string | Promise<boolean | string>;
|
|
18
12
|
|
|
19
13
|
export declare function createRouter(routes: Route[], options?: RouterOptions): Router;
|
|
20
14
|
|
|
15
|
+
declare interface LazyRoute extends BaseRoute {
|
|
16
|
+
redirectTo?: never;
|
|
17
|
+
component?(): never;
|
|
18
|
+
lazy(ctx: RouteContext): Promise<HTMLElement>;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
21
|
declare interface RedirectRoute extends BaseRoute {
|
|
22
22
|
redirectTo: string;
|
|
23
23
|
component?(): never;
|
|
24
|
-
|
|
24
|
+
lazy?(): never;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
export declare type Route = SyncRoute |
|
|
27
|
+
export declare type Route = SyncRoute | LazyRoute | RedirectRoute;
|
|
28
28
|
|
|
29
|
-
declare interface RouteContext {
|
|
29
|
+
export declare interface RouteContext {
|
|
30
30
|
from: string | null;
|
|
31
31
|
to: string;
|
|
32
|
-
params: Record<string, string>;
|
|
32
|
+
params: Record<string, string | undefined>;
|
|
33
33
|
query: URLSearchParams;
|
|
34
34
|
signal: AbortSignal;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
export declare class Router {
|
|
38
|
+
static instance: Router | null;
|
|
39
|
+
private readonly dispatcher;
|
|
38
40
|
private readonly routerController;
|
|
39
|
-
private initialized;
|
|
40
41
|
private navigationController;
|
|
41
|
-
private
|
|
42
|
+
private initialized;
|
|
43
|
+
private readonly routeMatchers;
|
|
42
44
|
private readonly options;
|
|
43
|
-
private readonly
|
|
45
|
+
private readonly outlet;
|
|
44
46
|
private currentPath;
|
|
47
|
+
private pendingPath;
|
|
45
48
|
constructor(routes: Route[], options?: RouterOptions);
|
|
49
|
+
on<K extends keyof RouterEventMap>(event: K, handler: RouterDispatcherHandler<RouterEventMap[K]>): () => void;
|
|
50
|
+
private emit;
|
|
46
51
|
initialize(): Promise<Readonly<RouterOutlet>>;
|
|
52
|
+
private handlePopState;
|
|
47
53
|
destroy(): void;
|
|
48
54
|
private navigate;
|
|
55
|
+
private abortNavigation;
|
|
56
|
+
private cancelCurrentNavigation;
|
|
57
|
+
private finalizeNavigation;
|
|
49
58
|
push(path: string, state?: unknown): Promise<void>;
|
|
50
59
|
replace(path: string, state?: unknown): Promise<void>;
|
|
60
|
+
getCurrentPath(): string | null;
|
|
61
|
+
getPendingPath(): string | null;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
declare type RouterDispatcherHandler<T> = (payload: T) => void;
|
|
65
|
+
|
|
66
|
+
declare type RouterEventMap = {
|
|
67
|
+
navigationstart: {
|
|
68
|
+
from: string | null;
|
|
69
|
+
to: string;
|
|
70
|
+
mode: "push" | "replace" | "none";
|
|
71
|
+
};
|
|
72
|
+
navigating: {
|
|
73
|
+
from: string | null;
|
|
74
|
+
to: string;
|
|
75
|
+
depth: number;
|
|
76
|
+
};
|
|
77
|
+
navigationend: {
|
|
78
|
+
from: string | null;
|
|
79
|
+
to: string;
|
|
80
|
+
successful: boolean;
|
|
81
|
+
};
|
|
82
|
+
pathchanged: {
|
|
83
|
+
from: string | null;
|
|
84
|
+
to: string;
|
|
85
|
+
};
|
|
86
|
+
querychanged: {
|
|
87
|
+
from: URLSearchParams;
|
|
88
|
+
to: URLSearchParams;
|
|
89
|
+
};
|
|
90
|
+
navigationabort: {
|
|
91
|
+
from: string | null;
|
|
92
|
+
to: string;
|
|
93
|
+
reason: string;
|
|
94
|
+
};
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
export declare class RouterLink extends HTMLAnchorElement {
|
|
98
|
+
private isActive;
|
|
99
|
+
private isPending;
|
|
100
|
+
private unsubscribeFns;
|
|
101
|
+
private props;
|
|
102
|
+
constructor(props: RouterLinkProps);
|
|
103
|
+
connectedCallback(): void;
|
|
104
|
+
disconnectedCallback(): void;
|
|
105
|
+
private applyClass;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
declare interface RouterLinkProps {
|
|
109
|
+
to: string;
|
|
110
|
+
className?: string | ((params: {
|
|
111
|
+
isActive: boolean;
|
|
112
|
+
isPending: boolean;
|
|
113
|
+
}) => string);
|
|
114
|
+
textContent: string;
|
|
115
|
+
replace?: boolean;
|
|
51
116
|
}
|
|
52
117
|
|
|
53
118
|
declare interface RouterOptions {
|
|
54
119
|
defaultTitle?: string;
|
|
55
|
-
globalErrorComponent?: (error:
|
|
120
|
+
globalErrorComponent?: (error: Error) => HTMLElement;
|
|
56
121
|
sortRoutesBySpecificity?: boolean;
|
|
57
122
|
ignoreSameLocation?: boolean;
|
|
58
123
|
}
|
|
@@ -68,7 +133,9 @@ declare type RouterOutletProps = {
|
|
|
68
133
|
declare interface SyncRoute extends BaseRoute {
|
|
69
134
|
redirectTo?: never;
|
|
70
135
|
component(ctx: RouteContext): HTMLElement;
|
|
71
|
-
|
|
136
|
+
lazy?(): never;
|
|
72
137
|
}
|
|
73
138
|
|
|
139
|
+
export declare function withAbortSignal<T>(fn: () => Promise<T>, signal: AbortSignal): Promise<T>;
|
|
140
|
+
|
|
74
141
|
export { }
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
function
|
|
1
|
+
function z(e, t) {
|
|
2
2
|
const n = document.createElement("div");
|
|
3
3
|
n.style.cssText = `
|
|
4
4
|
position: fixed;
|
|
@@ -33,15 +33,15 @@ function P(t, e) {
|
|
|
33
33
|
color: var(--accent);
|
|
34
34
|
`;
|
|
35
35
|
const i = document.createElement("h2");
|
|
36
|
-
i.textContent =
|
|
36
|
+
i.textContent = e?.message || "An unexpected error occurred.", i.style.cssText = `
|
|
37
37
|
font-size: 1.1rem;
|
|
38
38
|
margin-bottom: 16px;
|
|
39
39
|
opacity: 0.9;
|
|
40
40
|
`;
|
|
41
|
-
const a = document.createElement("div"),
|
|
42
|
-
|
|
43
|
-
const
|
|
44
|
-
|
|
41
|
+
const a = document.createElement("div"), f = document.createElement("p"), l = document.createElement("p");
|
|
42
|
+
f.textContent = t?.from ?? "", l.textContent = t?.to ?? "", a.append(f, l);
|
|
43
|
+
const c = document.createElement("pre");
|
|
44
|
+
c.textContent = e?.stack || "", c.style.cssText = `
|
|
45
45
|
font-size: 0.9rem;
|
|
46
46
|
max-height: 30vh;
|
|
47
47
|
overflow: auto;
|
|
@@ -52,8 +52,8 @@ function P(t, e) {
|
|
|
52
52
|
white-space: pre-wrap;
|
|
53
53
|
margin-bottom: 24px;
|
|
54
54
|
`;
|
|
55
|
-
const
|
|
56
|
-
|
|
55
|
+
const s = document.createElement("button");
|
|
56
|
+
s.textContent = "Reload Page", s.style.cssText = `
|
|
57
57
|
cursor: pointer;
|
|
58
58
|
padding: 10px 20px;
|
|
59
59
|
border: none;
|
|
@@ -64,8 +64,8 @@ function P(t, e) {
|
|
|
64
64
|
color: var(--button-text);
|
|
65
65
|
transition: background-color 0.25s;
|
|
66
66
|
`;
|
|
67
|
-
const
|
|
68
|
-
|
|
67
|
+
const u = document.createElement("button");
|
|
68
|
+
u.innerHTML = "✕", u.setAttribute("aria-label", "Close error dialog"), u.style.cssText = `
|
|
69
69
|
position: absolute;
|
|
70
70
|
top: 12px;
|
|
71
71
|
right: 12px;
|
|
@@ -76,385 +76,444 @@ function P(t, e) {
|
|
|
76
76
|
cursor: pointer;
|
|
77
77
|
opacity: 0.7;
|
|
78
78
|
transition: opacity 0.25s;
|
|
79
|
-
`,
|
|
80
|
-
|
|
81
|
-
},
|
|
82
|
-
|
|
83
|
-
},
|
|
79
|
+
`, u.onmouseenter = () => {
|
|
80
|
+
u.style.opacity = "1";
|
|
81
|
+
}, u.onmouseleave = () => {
|
|
82
|
+
u.style.opacity = "0.7";
|
|
83
|
+
}, u.onclick = () => {
|
|
84
84
|
n.remove();
|
|
85
|
-
},
|
|
86
|
-
|
|
87
|
-
},
|
|
88
|
-
|
|
89
|
-
},
|
|
85
|
+
}, s.onmouseenter = () => {
|
|
86
|
+
s.style.backgroundColor = "var(--button-bg-hover)";
|
|
87
|
+
}, s.onmouseleave = () => {
|
|
88
|
+
s.style.backgroundColor = "var(--button-bg)";
|
|
89
|
+
}, s.onclick = () => {
|
|
90
90
|
location.reload();
|
|
91
|
-
}, n.appendChild(o), o.append(
|
|
92
|
-
const
|
|
93
|
-
|
|
91
|
+
}, n.appendChild(o), o.append(u, r, i, a), e.stack && o.append(c), o.append(s);
|
|
92
|
+
const w = (h) => {
|
|
93
|
+
h.key === "Escape" && n.remove();
|
|
94
94
|
};
|
|
95
|
-
window.addEventListener("keydown",
|
|
96
|
-
const
|
|
97
|
-
document.body.contains(n) || (window.removeEventListener("keydown",
|
|
95
|
+
window.addEventListener("keydown", w);
|
|
96
|
+
const b = new MutationObserver(() => {
|
|
97
|
+
document.body.contains(n) || (window.removeEventListener("keydown", w), b.disconnect());
|
|
98
98
|
});
|
|
99
|
-
|
|
100
|
-
const
|
|
101
|
-
const
|
|
102
|
-
o.style.setProperty("--bg",
|
|
99
|
+
b.observe(document.body, { childList: !0 });
|
|
100
|
+
const v = () => {
|
|
101
|
+
const h = window.matchMedia("(prefers-color-scheme: dark)").matches;
|
|
102
|
+
o.style.setProperty("--bg", h ? "#2b2b2b" : "#fff"), o.style.setProperty("--text", h ? "#eee" : "#222"), o.style.setProperty("--accent", h ? "#ff6b6b" : "#e74c3c"), o.style.setProperty(
|
|
103
103
|
"--code-bg",
|
|
104
|
-
|
|
105
|
-
), o.style.setProperty("--button-bg",
|
|
104
|
+
h ? "rgba(255,255,255,0.05)" : "rgba(0,0,0,0.05)"
|
|
105
|
+
), o.style.setProperty("--button-bg", h ? "#3498db55" : "#3498db"), o.style.setProperty(
|
|
106
106
|
"--button-bg-hover",
|
|
107
|
-
|
|
107
|
+
h ? "#3498db88" : "#2980b9"
|
|
108
108
|
), o.style.setProperty("--button-text", "#fff");
|
|
109
109
|
};
|
|
110
|
-
window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change",
|
|
111
|
-
const
|
|
112
|
-
return
|
|
110
|
+
window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", v), v();
|
|
111
|
+
const E = document.createElement("style");
|
|
112
|
+
return E.textContent = `
|
|
113
113
|
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
|
|
114
114
|
@keyframes scaleIn { from { transform: scale(0.95); opacity: 0; } to { transform: scale(1); opacity: 1; } }
|
|
115
|
-
`, document.head.appendChild(
|
|
115
|
+
`, document.head.appendChild(E), n;
|
|
116
116
|
}
|
|
117
|
-
function
|
|
118
|
-
|
|
117
|
+
function g(e, t) {
|
|
118
|
+
const { promise: n, resolve: o, reject: r } = Promise.withResolvers(), i = new DOMException("Aborted", "AbortError");
|
|
119
|
+
if (t.aborted)
|
|
120
|
+
return r(i), n;
|
|
121
|
+
function a() {
|
|
122
|
+
r(i);
|
|
123
|
+
}
|
|
124
|
+
return t.addEventListener("abort", a, { once: !0 }), e().then(o, r), n;
|
|
125
|
+
}
|
|
126
|
+
function R(e) {
|
|
127
|
+
const t = typeof e == "string" ? e : "Unknown error";
|
|
128
|
+
return e instanceof Error ? e : new Error(t);
|
|
129
|
+
}
|
|
130
|
+
function S(e, t, n) {
|
|
131
|
+
e.title ? document.title = typeof e.title == "function" ? e.title(t) : e.title : document.title = n ?? "";
|
|
119
132
|
}
|
|
120
|
-
function
|
|
121
|
-
const o =
|
|
133
|
+
function y(e, t, n) {
|
|
134
|
+
const o = t ?? null;
|
|
122
135
|
switch (n) {
|
|
123
136
|
case "push":
|
|
124
|
-
window.history.pushState(o, "",
|
|
137
|
+
window.history.pushState(o, "", e);
|
|
125
138
|
break;
|
|
126
139
|
case "replace":
|
|
127
|
-
window.history.replaceState(o, "",
|
|
140
|
+
window.history.replaceState(o, "", e);
|
|
128
141
|
break;
|
|
129
142
|
}
|
|
130
143
|
}
|
|
131
|
-
function T(
|
|
132
|
-
|
|
133
|
-
}
|
|
134
|
-
function h(t, e, n, o, r) {
|
|
135
|
-
const i = n?.errorComponent?.(e, o) ?? r?.(e) ?? P(e, o);
|
|
136
|
-
t.replaceChildren(i);
|
|
137
|
-
}
|
|
138
|
-
const A = "*";
|
|
139
|
-
function L({ path: t }) {
|
|
140
|
-
return t.includes(":");
|
|
144
|
+
function T(e, t) {
|
|
145
|
+
e.replaceChildren(t);
|
|
141
146
|
}
|
|
142
|
-
function
|
|
143
|
-
const
|
|
144
|
-
|
|
147
|
+
function m(e, t, n, o, r) {
|
|
148
|
+
const i = R(t), a = n?.errorComponent?.(i, o) ?? r?.(i) ?? z(i, o);
|
|
149
|
+
e.replaceChildren(a);
|
|
145
150
|
}
|
|
146
|
-
function
|
|
147
|
-
const
|
|
148
|
-
|
|
149
|
-
(n, o) => `(?<${o}>[^/]+)`
|
|
150
|
-
);
|
|
151
|
-
return new RegExp(`^${e}$`);
|
|
151
|
+
function L(e) {
|
|
152
|
+
const [, t] = e.split("?");
|
|
153
|
+
return new URLSearchParams(t || "");
|
|
152
154
|
}
|
|
153
|
-
function
|
|
154
|
-
const
|
|
155
|
+
function A(e) {
|
|
156
|
+
const t = new URLPattern({ pathname: e.path });
|
|
155
157
|
return {
|
|
156
|
-
route:
|
|
157
|
-
redirectTo:
|
|
158
|
+
route: e,
|
|
159
|
+
redirectTo: e.redirectTo || null,
|
|
158
160
|
match(n) {
|
|
159
161
|
const o = n.split("?")[0];
|
|
160
|
-
return
|
|
162
|
+
return t.test({ pathname: o });
|
|
161
163
|
},
|
|
162
164
|
getParams(n) {
|
|
163
|
-
return e.exec(n)?.groups ?? {};
|
|
164
|
-
},
|
|
165
|
-
getQuery: x
|
|
166
|
-
};
|
|
167
|
-
}
|
|
168
|
-
function z({ path: t }) {
|
|
169
|
-
return t === A ? new RegExp("^.*$") : new RegExp(`^${t}$`);
|
|
170
|
-
}
|
|
171
|
-
function I(t) {
|
|
172
|
-
const e = z(t);
|
|
173
|
-
return {
|
|
174
|
-
route: t,
|
|
175
|
-
redirectTo: t.redirectTo || null,
|
|
176
|
-
match(n) {
|
|
177
165
|
const o = n.split("?")[0];
|
|
178
|
-
return
|
|
179
|
-
},
|
|
180
|
-
getParams() {
|
|
181
|
-
return {};
|
|
166
|
+
return t.exec({ pathname: o })?.pathname.groups ?? {};
|
|
182
167
|
},
|
|
183
|
-
getQuery:
|
|
168
|
+
getQuery: L
|
|
184
169
|
};
|
|
185
170
|
}
|
|
186
|
-
|
|
187
|
-
|
|
171
|
+
class d {
|
|
172
|
+
static instance;
|
|
173
|
+
static getInstance() {
|
|
174
|
+
return d.instance || (d.instance = new d()), d.instance;
|
|
175
|
+
}
|
|
176
|
+
listeners = /* @__PURE__ */ new Map();
|
|
177
|
+
on(t, n) {
|
|
178
|
+
let o = this.listeners.get(t);
|
|
179
|
+
return o || (o = /* @__PURE__ */ new Set(), this.listeners.set(t, o)), o.add(n), () => o.delete(n);
|
|
180
|
+
}
|
|
181
|
+
emit(t, n) {
|
|
182
|
+
this.listeners.get(t)?.forEach((o) => o(n));
|
|
183
|
+
}
|
|
184
|
+
clear() {
|
|
185
|
+
this.listeners.clear();
|
|
186
|
+
}
|
|
188
187
|
}
|
|
189
|
-
function
|
|
190
|
-
for (let n = 0; n <
|
|
191
|
-
if (
|
|
192
|
-
return
|
|
188
|
+
function N(e, t) {
|
|
189
|
+
for (let n = 0; n < t.length; n++)
|
|
190
|
+
if (t[n].match(e))
|
|
191
|
+
return t[n];
|
|
193
192
|
return null;
|
|
194
193
|
}
|
|
195
|
-
async function
|
|
196
|
-
return typeof
|
|
194
|
+
async function M(e, t) {
|
|
195
|
+
return typeof e.component == "function" ? e.component(t) : e.lazy && typeof e.lazy == "function" ? await e.lazy(t) : new Error("Failed to get component for route: " + e.path);
|
|
197
196
|
}
|
|
198
|
-
async function
|
|
199
|
-
const n =
|
|
197
|
+
async function $(e, t) {
|
|
198
|
+
const n = e.afterEnter;
|
|
200
199
|
if (!(!n || n.length === 0))
|
|
201
200
|
for (const o of n)
|
|
202
|
-
await o(
|
|
201
|
+
await o(t);
|
|
203
202
|
}
|
|
204
|
-
async function
|
|
205
|
-
const n =
|
|
203
|
+
async function U(e, t) {
|
|
204
|
+
const n = e.beforeEnter;
|
|
206
205
|
if (!n || n.length === 0)
|
|
207
206
|
return null;
|
|
208
207
|
for (const o of n) {
|
|
209
|
-
|
|
208
|
+
if (t.signal.aborted)
|
|
209
|
+
return !1;
|
|
210
|
+
const r = await o(t);
|
|
210
211
|
if (r === !1 || typeof r == "string")
|
|
211
212
|
return r;
|
|
212
213
|
}
|
|
213
214
|
return null;
|
|
214
215
|
}
|
|
215
|
-
function
|
|
216
|
-
return
|
|
217
|
-
}
|
|
218
|
-
class C extends HTMLElement {
|
|
219
|
-
constructor(e) {
|
|
220
|
-
super(), this.setAttribute("outlet-id", e.outletId);
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
customElements.define("router-outlet", C);
|
|
224
|
-
async function m(t, e) {
|
|
225
|
-
if (e.aborted)
|
|
226
|
-
throw new DOMException("Aborted", "AbortError");
|
|
227
|
-
return new Promise((n, o) => {
|
|
228
|
-
const r = () => o(new DOMException("Aborted", "AbortError"));
|
|
229
|
-
e.addEventListener("abort", r), t().then(n).catch(o).finally(() => {
|
|
230
|
-
e.removeEventListener("abort", r);
|
|
231
|
-
});
|
|
232
|
-
});
|
|
216
|
+
function C(e) {
|
|
217
|
+
return e instanceof DOMException && e.name === "AbortError";
|
|
233
218
|
}
|
|
234
|
-
function
|
|
235
|
-
const
|
|
236
|
-
if (
|
|
219
|
+
function x(e) {
|
|
220
|
+
const t = e.split("/").filter(Boolean);
|
|
221
|
+
if (t.length === 0)
|
|
237
222
|
return 0;
|
|
238
223
|
let n = 0;
|
|
239
|
-
for (let o = 0; o <
|
|
240
|
-
const r =
|
|
224
|
+
for (let o = 0; o < t.length; o++) {
|
|
225
|
+
const r = t[o];
|
|
241
226
|
r === "*" ? n -= 100 : r.startsWith(":") ? n += 5 : n += 10;
|
|
242
227
|
}
|
|
243
|
-
return n +=
|
|
228
|
+
return n += t.length * 0.1, n;
|
|
244
229
|
}
|
|
245
|
-
function
|
|
246
|
-
return
|
|
230
|
+
function B(e) {
|
|
231
|
+
return e.toSorted((t, n) => x(n.path) - x(t.path));
|
|
247
232
|
}
|
|
248
|
-
function
|
|
249
|
-
if (
|
|
233
|
+
function I(e, t) {
|
|
234
|
+
if (e === null)
|
|
250
235
|
return !1;
|
|
251
|
-
const n = new URL(
|
|
236
|
+
const n = new URL(e, location.origin), o = new URL(t, location.origin);
|
|
252
237
|
return !(n.pathname !== o.pathname || n.search !== o.search);
|
|
253
238
|
}
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
239
|
+
class P extends HTMLElement {
|
|
240
|
+
constructor(t) {
|
|
241
|
+
super(), this.setAttribute("outlet-id", t.outletId);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
customElements.define("router-outlet", P);
|
|
245
|
+
function k(e) {
|
|
246
|
+
if (typeof e != "string")
|
|
247
|
+
throw new Error(`Path must be a string, got ${typeof e}`);
|
|
248
|
+
if (e.trim() === "")
|
|
258
249
|
throw new Error("Path cannot be an empty string");
|
|
259
|
-
if (
|
|
260
|
-
throw new Error(`Path must start with '/': "${
|
|
261
|
-
if (
|
|
262
|
-
throw new Error(`Path must not end with '/': "${
|
|
263
|
-
if (
|
|
264
|
-
throw new Error(
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
throw new Error(
|
|
271
|
-
`Path contains invalid characters: "${e.join("")}" in "${t}"`
|
|
272
|
-
);
|
|
250
|
+
if (e !== "*" && !e.startsWith("/"))
|
|
251
|
+
throw new Error(`Path must start with '/': "${e}"`);
|
|
252
|
+
if (e.length > 1 && e.endsWith("/"))
|
|
253
|
+
throw new Error(`Path must not end with '/': "${e}"`);
|
|
254
|
+
if (e.includes("//"))
|
|
255
|
+
throw new Error(`Path must not contain multiple consecutive slashes: "${e}"`);
|
|
256
|
+
if (e === "*")
|
|
257
|
+
return;
|
|
258
|
+
const t = e.match(/[^A-Za-z0-9\-._~:/]/g);
|
|
259
|
+
if (t)
|
|
260
|
+
throw new Error(`Path contains invalid characters: "${t.join("")}" in "${e}"`);
|
|
273
261
|
}
|
|
274
|
-
function
|
|
275
|
-
const
|
|
276
|
-
for (let n = 0; n <
|
|
277
|
-
const o =
|
|
278
|
-
if (
|
|
262
|
+
function q(e) {
|
|
263
|
+
const t = /* @__PURE__ */ new Set();
|
|
264
|
+
for (let n = 0; n < e.length; n++) {
|
|
265
|
+
const o = e[n].path;
|
|
266
|
+
if (t.has(o))
|
|
279
267
|
throw new Error(`Duplicate root route path: "${o}"`);
|
|
280
|
-
|
|
268
|
+
t.add(o);
|
|
281
269
|
}
|
|
282
270
|
}
|
|
283
|
-
function
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
271
|
+
function W(e, t) {
|
|
272
|
+
const n = e.split("/"), o = t.split("/");
|
|
273
|
+
return n.length !== o.length ? !1 : n.every((r, i) => {
|
|
274
|
+
const a = o[i];
|
|
275
|
+
return r === a || r.startsWith(":") || a.startsWith(":");
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
function F(e) {
|
|
279
|
+
for (let t = 0; t < e.length; t++)
|
|
280
|
+
for (let n = t + 1; n < e.length; n++)
|
|
281
|
+
if (W(e[t].path, e[n].path))
|
|
282
|
+
throw new Error(`Conflicting routes: "${e[t].path}" and "${e[n].path}"`);
|
|
283
|
+
}
|
|
284
|
+
function H(e) {
|
|
285
|
+
if (!(e.redirectTo === null || e.redirectTo == null) && typeof e.redirectTo == "string") {
|
|
286
|
+
const t = e.redirectTo;
|
|
287
|
+
if (k(t), t === e.path)
|
|
288
|
+
throw new Error(`Route "${e.path}" cannot redirect to itself`);
|
|
289
|
+
if (e.path === "*")
|
|
289
290
|
throw new Error('Catch-all route "*" cannot have a redirect');
|
|
290
291
|
}
|
|
291
292
|
}
|
|
292
|
-
function
|
|
293
|
-
const
|
|
294
|
-
if (
|
|
295
|
-
throw new Error(
|
|
296
|
-
`Redirect route "${t.path}" must not define components`
|
|
297
|
-
);
|
|
293
|
+
function Q(e) {
|
|
294
|
+
const t = typeof e.redirectTo == "string", n = typeof e.component == "function", o = typeof e.lazy == "function";
|
|
295
|
+
if (t && (n || o))
|
|
296
|
+
throw new Error(`Redirect route "${e.path}" must not define components`);
|
|
298
297
|
if (n && o)
|
|
299
|
-
throw new Error(
|
|
300
|
-
|
|
301
|
-
);
|
|
302
|
-
if (!e && !n && !o)
|
|
303
|
-
throw new Error(
|
|
304
|
-
`Route "${t.path}" must define either "component" or "loadComponent"`
|
|
305
|
-
);
|
|
298
|
+
throw new Error(`Route "${e.path}" cannot have both "component" and "lazy"`);
|
|
299
|
+
if (!t && !n && !o)
|
|
300
|
+
throw new Error(`Route "${e.path}" must define either "component" or "lazy"`);
|
|
306
301
|
}
|
|
307
|
-
function
|
|
308
|
-
|
|
302
|
+
function D(e) {
|
|
303
|
+
if (e.path === "*") return;
|
|
304
|
+
const t = e.path.match(/:([^/]+)/g);
|
|
305
|
+
if (!t) return;
|
|
306
|
+
const n = t.map((r) => r.slice(1));
|
|
307
|
+
if (new Set(n).size !== n.length)
|
|
308
|
+
throw new Error(`Duplicate parameter names in path "${e.path}"`);
|
|
309
309
|
}
|
|
310
|
-
function
|
|
311
|
-
Q(
|
|
312
|
-
for (let e = 0; e < t.length; e++) {
|
|
313
|
-
const n = t[e];
|
|
314
|
-
F(n);
|
|
315
|
-
}
|
|
310
|
+
function O(e) {
|
|
311
|
+
k(e.path), H(e), Q(e), D(e);
|
|
316
312
|
}
|
|
317
|
-
|
|
313
|
+
function j(e) {
|
|
314
|
+
q(e), F(e);
|
|
315
|
+
for (let t = 0; t < e.length; t++)
|
|
316
|
+
O(e[t]);
|
|
317
|
+
}
|
|
318
|
+
class p {
|
|
319
|
+
static instance = null;
|
|
320
|
+
dispatcher = d.getInstance();
|
|
318
321
|
routerController = new AbortController();
|
|
319
|
-
initialized = !1;
|
|
320
322
|
navigationController = null;
|
|
321
|
-
|
|
323
|
+
initialized = !1;
|
|
324
|
+
routeMatchers;
|
|
322
325
|
options;
|
|
323
|
-
|
|
326
|
+
outlet = new P({ outletId: "root" });
|
|
324
327
|
currentPath = null;
|
|
325
|
-
|
|
326
|
-
|
|
328
|
+
pendingPath = null;
|
|
329
|
+
constructor(t, n) {
|
|
330
|
+
j(t), this.options = {
|
|
327
331
|
defaultTitle: document.title,
|
|
328
332
|
sortRoutesBySpecificity: !0,
|
|
333
|
+
ignoreSameLocation: !0,
|
|
329
334
|
...n
|
|
330
335
|
};
|
|
331
|
-
const o = this.options.sortRoutesBySpecificity ?
|
|
332
|
-
this.
|
|
336
|
+
const o = this.options.sortRoutesBySpecificity ? B(t) : t;
|
|
337
|
+
this.routeMatchers = o.map(A);
|
|
338
|
+
}
|
|
339
|
+
on(t, n) {
|
|
340
|
+
return this.dispatcher.on(t, n);
|
|
341
|
+
}
|
|
342
|
+
emit(t, n) {
|
|
343
|
+
this.dispatcher.emit(t, n);
|
|
333
344
|
}
|
|
334
345
|
async initialize() {
|
|
335
346
|
if (this.initialized)
|
|
336
347
|
throw new Error("Router already initialized");
|
|
337
|
-
|
|
338
|
-
const r = window.location.pathname + window.location.search;
|
|
339
|
-
await this.navigate(r, o.state, "none");
|
|
340
|
-
};
|
|
341
|
-
window.addEventListener(
|
|
348
|
+
p.instance = this, window.addEventListener(
|
|
342
349
|
"popstate",
|
|
343
|
-
(
|
|
344
|
-
|
|
350
|
+
(n) => {
|
|
351
|
+
this.handlePopState(n).catch(console.error);
|
|
345
352
|
},
|
|
346
|
-
{
|
|
353
|
+
{
|
|
354
|
+
signal: this.routerController.signal
|
|
355
|
+
}
|
|
347
356
|
);
|
|
357
|
+
const t = window.location.pathname + window.location.search;
|
|
358
|
+
return await this.navigate(t, null, "none"), this.initialized = !0, this.outlet;
|
|
359
|
+
}
|
|
360
|
+
async handlePopState(t) {
|
|
348
361
|
const n = window.location.pathname + window.location.search;
|
|
349
|
-
|
|
362
|
+
await this.navigate(n, t.state, "none");
|
|
350
363
|
}
|
|
351
364
|
destroy() {
|
|
352
365
|
if (!this.initialized)
|
|
353
366
|
throw new Error("Router not initialized");
|
|
354
|
-
this.routerController.abort(), this.navigationController
|
|
367
|
+
this.routerController.abort(), this.navigationController?.abort(), this.navigationController = null, this.dispatcher.clear(), this.initialized = !1, p.instance = null;
|
|
355
368
|
}
|
|
356
|
-
async navigate(
|
|
357
|
-
if (r > 5)
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
369
|
+
async navigate(t, n, o, r = 0) {
|
|
370
|
+
if (r === 0 && (this.emit("navigationstart", { from: this.currentPath, to: t, mode: o }), this.pendingPath = t), this.emit("navigating", { from: this.currentPath, to: t, depth: r }), r > 5)
|
|
371
|
+
return this.abortNavigation(t, "too many redirects", new Error("Too many redirects"));
|
|
372
|
+
if (this.options.ignoreSameLocation && I(this.currentPath, t))
|
|
373
|
+
return this.pendingPath = null, this.abortNavigation(t, "same location");
|
|
374
|
+
this.cancelCurrentNavigation(), this.navigationController = new AbortController();
|
|
375
|
+
const i = this.navigationController.signal;
|
|
376
|
+
i.addEventListener(
|
|
377
|
+
"abort",
|
|
378
|
+
() => this.emit("navigationabort", {
|
|
379
|
+
from: this.currentPath,
|
|
380
|
+
to: t,
|
|
381
|
+
reason: String(i.reason ?? "aborted")
|
|
382
|
+
})
|
|
383
|
+
);
|
|
384
|
+
const a = N(t, this.routeMatchers), f = this.currentPath, l = {
|
|
385
|
+
from: f,
|
|
386
|
+
to: t,
|
|
387
|
+
params: a?.getParams(t) ?? {},
|
|
388
|
+
query: a?.getQuery(t) ?? new URLSearchParams(),
|
|
375
389
|
signal: i
|
|
376
390
|
};
|
|
377
391
|
try {
|
|
378
|
-
if (!a)
|
|
379
|
-
|
|
380
|
-
this.
|
|
392
|
+
if (!a)
|
|
393
|
+
return y(t, n, o), m(
|
|
394
|
+
this.outlet,
|
|
381
395
|
new Error('Route not found. Add "*" catch-all route'),
|
|
382
396
|
null,
|
|
383
|
-
|
|
397
|
+
l,
|
|
384
398
|
this.options.globalErrorComponent
|
|
385
|
-
), this.
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
if (a.redirectTo) {
|
|
389
|
-
await this.navigate(a.redirectTo, n, "replace", r + 1);
|
|
390
|
-
return;
|
|
391
|
-
}
|
|
399
|
+
), this.finalizeNavigation(t, f, !1);
|
|
400
|
+
if (a.redirectTo)
|
|
401
|
+
return this.navigate(a.redirectTo, n, "replace", r + 1);
|
|
392
402
|
if (i.aborted)
|
|
393
403
|
return;
|
|
394
|
-
const c = await
|
|
395
|
-
() =>
|
|
404
|
+
const c = await g(
|
|
405
|
+
() => U(a.route, l),
|
|
396
406
|
i
|
|
397
407
|
);
|
|
398
408
|
if (c === !1)
|
|
399
409
|
return;
|
|
400
|
-
if (typeof c == "string")
|
|
401
|
-
|
|
402
|
-
return;
|
|
403
|
-
}
|
|
410
|
+
if (typeof c == "string")
|
|
411
|
+
return this.navigate(c, n, "replace", r + 1);
|
|
404
412
|
if (i.aborted)
|
|
405
413
|
return;
|
|
406
|
-
const
|
|
407
|
-
() =>
|
|
414
|
+
const s = await g(
|
|
415
|
+
() => M(a.route, l),
|
|
408
416
|
i
|
|
409
417
|
);
|
|
410
|
-
if (
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
p(e, n, o), h(
|
|
414
|
-
this.rootOutletInternal,
|
|
415
|
-
d,
|
|
416
|
-
a.route,
|
|
418
|
+
if (s instanceof Error)
|
|
419
|
+
return C(s) ? void 0 : (y(t, n, o), m(
|
|
420
|
+
this.outlet,
|
|
417
421
|
s,
|
|
422
|
+
a.route,
|
|
423
|
+
l,
|
|
418
424
|
this.options.globalErrorComponent
|
|
419
|
-
), this.
|
|
420
|
-
return;
|
|
421
|
-
}
|
|
425
|
+
), this.finalizeNavigation(t, f, !1));
|
|
422
426
|
if (i.aborted)
|
|
423
427
|
return;
|
|
424
|
-
T(this.
|
|
428
|
+
T(this.outlet, s), y(t, n, o), S(a.route, l, this.options.defaultTitle), this.finalizeNavigation(t, f, !0), await g(() => $(a.route, l), i);
|
|
425
429
|
} catch (c) {
|
|
426
|
-
if (
|
|
430
|
+
if (C(c))
|
|
427
431
|
return;
|
|
428
|
-
|
|
429
|
-
this.
|
|
432
|
+
m(
|
|
433
|
+
this.outlet,
|
|
430
434
|
c instanceof Error ? c : new Error("Unknown error"),
|
|
431
435
|
a?.route ?? null,
|
|
432
|
-
|
|
436
|
+
l,
|
|
433
437
|
this.options.globalErrorComponent
|
|
434
|
-
);
|
|
438
|
+
), this.emit("navigationend", { from: this.currentPath, to: t, successful: !1 });
|
|
435
439
|
} finally {
|
|
436
440
|
this.navigationController?.signal === i && (this.navigationController = null);
|
|
437
441
|
}
|
|
438
442
|
}
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
await this.navigate(e, n, "push");
|
|
442
|
-
} catch (o) {
|
|
443
|
-
console.error("[Router.push] Error:", o);
|
|
444
|
-
}
|
|
443
|
+
abortNavigation(t, n, o) {
|
|
444
|
+
this.pendingPath = null, this.emit("navigationabort", { from: this.currentPath, to: t, reason: n }), o && m(this.outlet, o, null, null, this.options.globalErrorComponent);
|
|
445
445
|
}
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
}
|
|
446
|
+
cancelCurrentNavigation() {
|
|
447
|
+
this.navigationController && (this.navigationController.abort("navigation canceled"), this.navigationController = null);
|
|
448
|
+
}
|
|
449
|
+
finalizeNavigation(t, n, o) {
|
|
450
|
+
const r = n ? new URLSearchParams(n.split("?")[1] ?? "") : null, i = new URLSearchParams(t.split("?")[1] ?? "");
|
|
451
|
+
this.currentPath = t, this.pendingPath = null, this.emit("pathchanged", { from: n, to: t }), (!r || r?.toString() !== i.toString()) && this.emit("querychanged", { from: r ?? new URLSearchParams(), to: i }), this.emit("navigationend", { from: n, to: t, successful: o });
|
|
452
|
+
}
|
|
453
|
+
async push(t, n) {
|
|
454
|
+
await this.navigate(t, n, "push");
|
|
455
|
+
}
|
|
456
|
+
async replace(t, n) {
|
|
457
|
+
await this.navigate(t, n, "replace");
|
|
458
|
+
}
|
|
459
|
+
getCurrentPath() {
|
|
460
|
+
return this.currentPath;
|
|
461
|
+
}
|
|
462
|
+
getPendingPath() {
|
|
463
|
+
return this.pendingPath;
|
|
452
464
|
}
|
|
453
465
|
}
|
|
454
|
-
function G(
|
|
455
|
-
return new
|
|
466
|
+
function G(e, t) {
|
|
467
|
+
return new p(e, t);
|
|
468
|
+
}
|
|
469
|
+
class K extends HTMLAnchorElement {
|
|
470
|
+
isActive = !1;
|
|
471
|
+
isPending = !1;
|
|
472
|
+
unsubscribeFns = [];
|
|
473
|
+
props;
|
|
474
|
+
constructor(t) {
|
|
475
|
+
super(), this.props = t, this.href = t.to, this.textContent = t.textContent, this.addEventListener("click", (n) => {
|
|
476
|
+
n.preventDefault();
|
|
477
|
+
const o = p.instance;
|
|
478
|
+
t.replace ? o?.replace(t.to) : o?.push(t.to);
|
|
479
|
+
});
|
|
480
|
+
}
|
|
481
|
+
connectedCallback() {
|
|
482
|
+
const t = p.instance;
|
|
483
|
+
if (!t) return;
|
|
484
|
+
const n = () => {
|
|
485
|
+
const o = t.getCurrentPath() || "", r = this.props.to;
|
|
486
|
+
this.isActive = o.split("?")[0] === r.split("?")[0], this.isPending = t.getPendingPath() === r, this.applyClass();
|
|
487
|
+
};
|
|
488
|
+
n(), this.unsubscribeFns.push(
|
|
489
|
+
t.on("navigationstart", n),
|
|
490
|
+
t.on("navigating", n),
|
|
491
|
+
t.on("navigationend", n),
|
|
492
|
+
t.on("navigationabort", n),
|
|
493
|
+
t.on("pathchanged", n),
|
|
494
|
+
t.on("querychanged", n)
|
|
495
|
+
);
|
|
496
|
+
}
|
|
497
|
+
disconnectedCallback() {
|
|
498
|
+
this.unsubscribeFns.forEach((t) => t()), this.unsubscribeFns = [];
|
|
499
|
+
}
|
|
500
|
+
applyClass() {
|
|
501
|
+
if (this.props.className) {
|
|
502
|
+
if (typeof this.props.className == "string") {
|
|
503
|
+
this.className = this.props.className;
|
|
504
|
+
return;
|
|
505
|
+
}
|
|
506
|
+
this.className = this.props.className({
|
|
507
|
+
isActive: this.isActive,
|
|
508
|
+
isPending: this.isPending
|
|
509
|
+
});
|
|
510
|
+
}
|
|
511
|
+
}
|
|
456
512
|
}
|
|
513
|
+
customElements.define("router-link", K, { extends: "a" });
|
|
457
514
|
export {
|
|
458
|
-
|
|
459
|
-
|
|
515
|
+
p as Router,
|
|
516
|
+
K as RouterLink,
|
|
517
|
+
G as createRouter,
|
|
518
|
+
g as withAbortSignal
|
|
460
519
|
};
|
package/package.json
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ripetchor/r-router",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"type": "module",
|
|
5
|
+
"license": "MIT",
|
|
5
6
|
"files": [
|
|
6
7
|
"dist"
|
|
7
8
|
],
|
|
@@ -23,6 +24,7 @@
|
|
|
23
24
|
},
|
|
24
25
|
"devDependencies": {
|
|
25
26
|
"@eslint/js": "^9.37.0",
|
|
27
|
+
"@types/node": "^25.0.3",
|
|
26
28
|
"eslint": "^9.37.0",
|
|
27
29
|
"typescript": "~5.9.3",
|
|
28
30
|
"typescript-eslint": "^8.45.0",
|