jong-router 0.1.13 → 0.1.15
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 +184 -45
- package/dist/about-component.d.ts +4 -0
- package/dist/guard.d.ts +2 -0
- package/dist/home-component.d.ts +4 -0
- package/dist/jong-router.d.ts +42 -11
- package/dist/jong-router.min.js +1 -1
- package/dist/notfound-component.d.ts +4 -0
- package/dist/profile-component.d.ts +6 -0
- package/dist/team-component.d.ts +5 -0
- package/package.json +17 -3
- package/router-instance.ts +27 -0
package/README.md
CHANGED
|
@@ -1,30 +1,36 @@
|
|
|
1
|
-
|
|
1
|
+
[](https://nodei.co/npm/jong-router/)
|
|
2
2
|
|
|
3
|
+
# JongRouter
|
|
3
4
|
|
|
4
5
|
|
|
5
|
-
A lightweight and simple-to-use web components router in Vanilla JavaScript with support for guards, nested routes, page not found, passing query parameters to components, passing route parameters to components, passing route data to components, and a router link for single-page application navigation without reloading the page.
|
|
6
6
|
|
|
7
|
+
JongRouter is a lightweight client-side router for Web Components, built with Vanilla JavaScript.
|
|
8
|
+
It enables Single Page Application (SPA) navigation without page reloads while staying framework-agnostic.
|
|
9
|
+
Designed for developers who want a simple router for Web Components without bringing in a heavy framework.
|
|
7
10
|
|
|
8
11
|
|
|
9
12
|
## Features
|
|
10
13
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
14
|
+
* SPA Navigation
|
|
15
|
+
Navigate without reloading the page using the `router-link` attribute.
|
|
16
|
+
* Route Guards
|
|
17
|
+
Protect routes with custom guard logic before navigation.
|
|
18
|
+
* Nested Routing
|
|
19
|
+
Easily compose child routers inside Web Components.
|
|
20
|
+
* Shadow DOM Support
|
|
21
|
+
Works seamlessly with Web Components using Shadow DOM.
|
|
22
|
+
* Route Parameters
|
|
23
|
+
Dynamic routes like `/profile/:username`.
|
|
24
|
+
* Query Parameters
|
|
25
|
+
Access query strings like `/profile/jong?tab=settings`.
|
|
26
|
+
* Route Data
|
|
27
|
+
Pass static metadata to components.
|
|
28
|
+
* Programmatic Navigation
|
|
29
|
+
Navigate using `router.navigateTo()`.
|
|
30
|
+
* 404 Page Handling
|
|
31
|
+
Built-in fallback route using **.
|
|
32
|
+
* Buttons & Links Support
|
|
33
|
+
Works with both <a> and <button> elements.
|
|
28
34
|
|
|
29
35
|
|
|
30
36
|
|
|
@@ -71,19 +77,23 @@ npm i jong-router
|
|
|
71
77
|
|
|
72
78
|
```javascript
|
|
73
79
|
|
|
74
|
-
const
|
|
80
|
+
const routes = [
|
|
75
81
|
|
|
76
82
|
{ pattern: '/', component: import('./components/HomeComponent') },
|
|
77
83
|
|
|
78
84
|
{ pattern: '/about', component: import('./components/AboutComponent') },
|
|
79
85
|
|
|
80
|
-
|
|
86
|
+
{ pattern: '**', html: `<h2>Page not found</h2>` }
|
|
81
87
|
|
|
82
|
-
]
|
|
88
|
+
]
|
|
83
89
|
|
|
90
|
+
const router = new JongRouter(
|
|
91
|
+
routes,
|
|
92
|
+
document.getElementById('app')
|
|
93
|
+
)
|
|
84
94
|
|
|
95
|
+
router.init()
|
|
85
96
|
|
|
86
|
-
router.init();
|
|
87
97
|
|
|
88
98
|
```
|
|
89
99
|
|
|
@@ -99,22 +109,19 @@ Create your web components for each route.
|
|
|
99
109
|
|
|
100
110
|
```javascript
|
|
101
111
|
|
|
102
|
-
// Example: HomeComponent.js
|
|
103
|
-
|
|
104
112
|
class HomeComponent extends HTMLElement {
|
|
105
113
|
|
|
106
114
|
connectedCallback() {
|
|
107
115
|
|
|
108
|
-
this.innerHTML =
|
|
116
|
+
this.innerHTML = `<h1>Home Page</h1>`
|
|
109
117
|
|
|
110
118
|
}
|
|
111
119
|
|
|
112
120
|
}
|
|
113
121
|
|
|
122
|
+
customElements.define('home-component', HomeComponent)
|
|
114
123
|
|
|
115
124
|
|
|
116
|
-
customElements.define('home-component', HomeComponent);
|
|
117
|
-
|
|
118
125
|
```
|
|
119
126
|
|
|
120
127
|
|
|
@@ -137,9 +144,37 @@ Use the `router-link` attribute to create navigation links.
|
|
|
137
144
|
|
|
138
145
|
```
|
|
139
146
|
|
|
147
|
+
Buttons also work with router-link
|
|
148
|
+
```html
|
|
149
|
+
<button router-link href="/about">
|
|
150
|
+
Go to About
|
|
151
|
+
</button>
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
4. Programmatic Navigation
|
|
155
|
+
Components can navigate programmatically.
|
|
156
|
+
|
|
157
|
+
Example outside component
|
|
158
|
+
|
|
159
|
+
```js
|
|
160
|
+
import { router } from "../router-instance"
|
|
161
|
+
|
|
162
|
+
router.navigateTo("/profile/admin")
|
|
163
|
+
```
|
|
140
164
|
|
|
165
|
+
Example inside a component
|
|
166
|
+
```js
|
|
167
|
+
this.shadowRoot
|
|
168
|
+
.getElementById("btn")
|
|
169
|
+
.addEventListener("click", () => {
|
|
170
|
+
|
|
171
|
+
this.navigateTo("/profile/admin")
|
|
172
|
+
|
|
173
|
+
})
|
|
174
|
+
```
|
|
141
175
|
|
|
142
|
-
|
|
176
|
+
|
|
177
|
+
5. **Guards**
|
|
143
178
|
|
|
144
179
|
|
|
145
180
|
|
|
@@ -147,7 +182,7 @@ Implement guards for route conditions
|
|
|
147
182
|
|
|
148
183
|
|
|
149
184
|
|
|
150
|
-
```
|
|
185
|
+
```js
|
|
151
186
|
|
|
152
187
|
const router = new JongRouter([
|
|
153
188
|
|
|
@@ -170,9 +205,11 @@ const router = new JongRouter([
|
|
|
170
205
|
|
|
171
206
|
|
|
172
207
|
|
|
173
|
-
function isAuthenticated() {
|
|
208
|
+
function isAuthenticated(ctx) {
|
|
174
209
|
|
|
175
210
|
// Your authentication logic here
|
|
211
|
+
console.log(ctx.path)
|
|
212
|
+
console.log(ctx.params)
|
|
176
213
|
|
|
177
214
|
return true;
|
|
178
215
|
|
|
@@ -182,44 +219,146 @@ function isAuthenticated() {
|
|
|
182
219
|
|
|
183
220
|
|
|
184
221
|
|
|
185
|
-
|
|
222
|
+
6. **Handle Route Parameters and Query Parameters:**
|
|
186
223
|
|
|
187
224
|
|
|
225
|
+
Define dynamic routes Parameter:
|
|
226
|
+
```js
|
|
227
|
+
{
|
|
228
|
+
pattern: "/profile/:username",
|
|
229
|
+
component: import("./ProfileComponent")
|
|
230
|
+
}
|
|
231
|
+
```
|
|
188
232
|
|
|
189
|
-
Access
|
|
233
|
+
Access inside the component
|
|
234
|
+
```js
|
|
235
|
+
const params = JSON.parse(
|
|
236
|
+
this.getAttribute("route-params")
|
|
237
|
+
)
|
|
190
238
|
|
|
239
|
+
console.log(params.username)
|
|
240
|
+
```
|
|
191
241
|
|
|
192
242
|
|
|
193
|
-
|
|
243
|
+
Define query parameter
|
|
244
|
+
```js
|
|
245
|
+
/profile/jong?tab=settings
|
|
246
|
+
```
|
|
194
247
|
|
|
195
|
-
|
|
248
|
+
Inside component
|
|
249
|
+
```js
|
|
250
|
+
const query = JSON.parse(
|
|
251
|
+
this.getAttribute("query-params")
|
|
252
|
+
)
|
|
196
253
|
|
|
197
|
-
|
|
254
|
+
console.log(query.tab)
|
|
255
|
+
```
|
|
198
256
|
|
|
199
|
-
|
|
257
|
+
7. **Route Data**
|
|
258
|
+
Attach metadata to routes
|
|
200
259
|
|
|
201
|
-
|
|
260
|
+
```js
|
|
261
|
+
{
|
|
262
|
+
pattern: "/profile/:username",
|
|
263
|
+
component: import("./ProfileComponent"),
|
|
264
|
+
data: { role: "admin" }
|
|
265
|
+
}
|
|
266
|
+
```
|
|
267
|
+
Access in component
|
|
268
|
+
```js
|
|
269
|
+
const data = JSON.parse(
|
|
270
|
+
this.getAttribute("route-data")
|
|
271
|
+
)
|
|
272
|
+
```
|
|
202
273
|
|
|
203
|
-
const queryParams = JSON.parse(this.getAttribute('query-params'));
|
|
204
274
|
|
|
275
|
+
8. **Nested Routes & Sub-Outlest**
|
|
205
276
|
|
|
277
|
+
JongRouter supports hierarchical routing. This allows you to render a "Shell" or "Layout" component and then inject sub-pages into it dynamically.
|
|
206
278
|
|
|
207
|
-
|
|
279
|
+
Step 1: Define Child Routes
|
|
280
|
+
Create a separate file for your sub-navigation.
|
|
208
281
|
|
|
209
|
-
|
|
282
|
+
Example
|
|
283
|
+
```ts
|
|
284
|
+
// samples/nested/nested-routes.ts
|
|
285
|
+
import { IRoute } from "../../src/jong-router"
|
|
210
286
|
|
|
211
|
-
|
|
287
|
+
const routes: IRoute[] = [
|
|
288
|
+
{ pattern: "/nested/c1", html: "<p>Child Page 1</p>" },
|
|
289
|
+
{ pattern: "/nested/c2", html: "<p>Child Page 2</p>" },
|
|
290
|
+
{ pattern: "/nested/c3", html: "<p>Child Page 3</p>" }
|
|
291
|
+
]
|
|
212
292
|
|
|
213
|
-
|
|
293
|
+
export default routes
|
|
214
294
|
|
|
215
|
-
|
|
295
|
+
```
|
|
216
296
|
|
|
297
|
+
Step 2: Prepare the Parent Component
|
|
298
|
+
Your parent component must contain an element with the router-outlet attribute. This is where the child routes will be rendered.
|
|
299
|
+
|
|
300
|
+
A parent component can create its own router instance w children routes
|
|
301
|
+
```ts
|
|
302
|
+
// samples/nested/sample-nested.ts
|
|
303
|
+
export default class SampleNested extends HTMLElement {
|
|
304
|
+
connectedCallback() {
|
|
305
|
+
this.innerHTML = `
|
|
306
|
+
<div class="layout">
|
|
307
|
+
<nav>
|
|
308
|
+
<a href="/nested/c1" router-link>Go to C1</a>
|
|
309
|
+
<a href="/nested/c2" router-link>Go to C2</a>
|
|
310
|
+
</nav>
|
|
311
|
+
<!-- Child routes will appear here -->
|
|
312
|
+
<div router-outlet></div>
|
|
313
|
+
</div>
|
|
314
|
+
`;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
217
317
|
|
|
318
|
+
```
|
|
218
319
|
|
|
219
|
-
|
|
320
|
+
Step 3: Register Nested Routes
|
|
321
|
+
Pass the children to the parent route. You can use a static array or a function for Lazy Loading.
|
|
322
|
+
```ts
|
|
323
|
+
const routes: IRoute[] = [
|
|
324
|
+
{
|
|
325
|
+
pattern: '/nested',
|
|
326
|
+
component: import('./samples/nested/sample-nested'),
|
|
327
|
+
// Lazy-load the child route definitions
|
|
328
|
+
children: () => import("./samples/nested/nested-routes")
|
|
329
|
+
}
|
|
330
|
+
]
|
|
220
331
|
|
|
221
332
|
```
|
|
222
333
|
|
|
334
|
+
|
|
335
|
+
## Playground & Examples
|
|
336
|
+
Playground & Examples
|
|
337
|
+
The repository contains a Playground demonstrating:
|
|
338
|
+
* Basic routing
|
|
339
|
+
* Guarded routes
|
|
340
|
+
* Nested routes
|
|
341
|
+
* Query parameters
|
|
342
|
+
* Route parameters
|
|
343
|
+
* Programmatic navigation
|
|
344
|
+
* Buttons & router-link navigation
|
|
345
|
+
|
|
346
|
+
|
|
347
|
+
# Why JongRouter?
|
|
348
|
+
JongRouter focuses on simplicity and Web Component compatibility.
|
|
349
|
+
Unlike many routers, it:
|
|
350
|
+
* Works without frameworks
|
|
351
|
+
* Supports Shadow DOM
|
|
352
|
+
* Keeps the API extremely small
|
|
353
|
+
* Is easy to embed in micro-frontend architectures
|
|
354
|
+
|
|
355
|
+
Perfect for:
|
|
356
|
+
* Web Component apps
|
|
357
|
+
* Micro-frontends
|
|
358
|
+
* Lightweight SPAs
|
|
359
|
+
* Vanilla JS projects
|
|
360
|
+
|
|
361
|
+
|
|
223
362
|
## How to run development server?
|
|
224
363
|
```
|
|
225
364
|
git clone git@github.com:josnin/jong-router.git
|
package/dist/guard.d.ts
ADDED
package/dist/jong-router.d.ts
CHANGED
|
@@ -1,26 +1,57 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Context passed to Route Guards to decide if a user can access a route.
|
|
3
|
+
*/
|
|
4
|
+
export interface GuardContext {
|
|
5
|
+
path?: string;
|
|
6
|
+
params?: Record<string, string>;
|
|
7
|
+
query?: URLSearchParams;
|
|
8
|
+
data?: any;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Definition of a single Route.
|
|
12
|
+
*/
|
|
13
|
+
export interface IRoute {
|
|
2
14
|
pattern: string;
|
|
3
15
|
component?: Promise<any>;
|
|
4
16
|
html?: string;
|
|
5
|
-
|
|
17
|
+
children?: IRoute[] | (() => Promise<{
|
|
18
|
+
default: IRoute[];
|
|
19
|
+
}>);
|
|
20
|
+
guards?: ((ctx?: any) => boolean)[];
|
|
6
21
|
redirect?: string;
|
|
7
|
-
data?: any
|
|
22
|
+
data?: Record<string, any>;
|
|
8
23
|
}
|
|
9
24
|
declare class JongRouter {
|
|
10
25
|
private routes;
|
|
11
26
|
private outlet;
|
|
12
|
-
|
|
13
|
-
|
|
27
|
+
constructor(routes: IRoute[], outlet: HTMLElement);
|
|
28
|
+
/**
|
|
29
|
+
* Starts the router by listening to browser events and initial navigation.
|
|
30
|
+
*/
|
|
14
31
|
init(): void;
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
private loadComponent;
|
|
32
|
+
/**
|
|
33
|
+
* Intercepts clicks to handle internal navigation without refreshing the page.
|
|
34
|
+
*/
|
|
19
35
|
private handleClick;
|
|
36
|
+
/**
|
|
37
|
+
* Matches the current browser URL against the route table and triggers rendering.
|
|
38
|
+
*/
|
|
39
|
+
private navigate;
|
|
40
|
+
/**
|
|
41
|
+
* Handles Guards, Component instantiation, and Nested routes.
|
|
42
|
+
*/
|
|
43
|
+
private renderRoute;
|
|
44
|
+
/**
|
|
45
|
+
* Basic string matching. Supports ':param' wildcards and '**' catch-all.
|
|
46
|
+
*/
|
|
20
47
|
private matchRoute;
|
|
21
|
-
|
|
48
|
+
/**
|
|
49
|
+
* Maps URL values to parameter names based on the pattern.
|
|
50
|
+
*/
|
|
22
51
|
private extractRouteParams;
|
|
52
|
+
/**
|
|
53
|
+
* Programmatic navigation (e.g., router.navigateTo('/dashboard'))
|
|
54
|
+
*/
|
|
23
55
|
navigateTo(route: string): void;
|
|
24
56
|
}
|
|
25
|
-
export { IRoute };
|
|
26
57
|
export default JongRouter;
|
package/dist/jong-router.min.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var
|
|
1
|
+
var l=class{routes;outlet;constructor(t,i){this.routes=t,this.outlet=i}init(){window.addEventListener("popstate",()=>this.navigate()),document.addEventListener("click",t=>this.handleClick(t)),this.navigate()}handleClick(t){let a=t.composedPath().find(n=>(n instanceof HTMLAnchorElement||n instanceof HTMLButtonElement)&&n.hasAttribute("router-link"));if(!a)return;let e=a.getAttribute("href");!e||e.startsWith("http")||t instanceof MouseEvent&&(t.metaKey||t.ctrlKey||t.shiftKey||t.altKey)||(t.preventDefault(),this.navigateTo(e))}navigate(){let t=window.location.pathname,i=this.routes.sort((e,n)=>n.pattern.length-e.pattern.length).find(e=>this.matchRoute(e.pattern,t));if(!i)return;let a=this.extractRouteParams(i.pattern,t);this.renderRoute(i,a)}async renderRoute(t,i,a=this.outlet){if(t.guards){let n={path:window.location.pathname,params:i,query:new URLSearchParams(window.location.search),data:t.data};if(!t.guards.every(o=>o(n))){t.redirect&&this.navigateTo(t.redirect);return}}let e=null;if(t.component){let r=(await t.component).default;e=new r,e.setAttribute("route-params",JSON.stringify(i)),e.router=this,a.innerHTML="",a.appendChild(e)}else t.html&&(a.innerHTML=t.html);if(t.children){let n;typeof t.children=="function"?n=(await t.children()).default:n=t.children;let r=e?.shadowRoot?.querySelector("[router-outlet]")||e?.querySelector("[router-outlet]")||a,o=window.location.pathname,c=n.sort((s,h)=>h.pattern.length-s.pattern.length).find(s=>this.matchRoute(s.pattern,o));if(c){let s=this.extractRouteParams(c.pattern,o);await this.renderRoute(c,s,r)}}}matchRoute(t,i){if(t==="**")return!0;let a=t.split("/").filter(Boolean),e=i.split("/").filter(Boolean);return a.length>e.length?!1:a.every((n,r)=>n.startsWith(":")||n===e[r])}extractRouteParams(t,i){let a={},e=t.split("/").filter(Boolean),n=i.split("/").filter(Boolean);return e.forEach((r,o)=>{r.startsWith(":")&&(a[r.slice(1)]=n[o])}),a}navigateTo(t){window.history.pushState({},"",t),this.navigate()}},u=l;export{u as default};
|
|
2
2
|
//# sourceMappingURL=jong-router.min.js.map
|
package/package.json
CHANGED
|
@@ -1,11 +1,25 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jong-router",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.1.15",
|
|
4
|
+
"description": "JongRouter is a lightweight client-side router for Web Components, built with Vanilla JavaScript. It enables Single Page Application (SPA) navigation without page reloads while staying framework-agnostic.Designed for developers who want a simple router for Web Components without bringing in a heavy framework",
|
|
5
5
|
"keywords": [
|
|
6
|
+
"redgin",
|
|
7
|
+
"ScalpelJS",
|
|
6
8
|
"jong-router",
|
|
9
|
+
"web-components",
|
|
7
10
|
"web components router",
|
|
8
|
-
"custom elements"
|
|
11
|
+
"custom elements",
|
|
12
|
+
"vanilla javascript",
|
|
13
|
+
"spa",
|
|
14
|
+
"single-page application",
|
|
15
|
+
"client-side router",
|
|
16
|
+
"frontend router",
|
|
17
|
+
"nested routes",
|
|
18
|
+
"route guards",
|
|
19
|
+
"query parameters",
|
|
20
|
+
"route parameters",
|
|
21
|
+
"javascript router",
|
|
22
|
+
"microfrontend"
|
|
9
23
|
],
|
|
10
24
|
"main": "dist/jong-router.min.js",
|
|
11
25
|
"types": "dist/jong-router.d.ts",
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// index.ts
|
|
2
|
+
import JongRouter, { IRoute } from './src/jong-router'
|
|
3
|
+
import { authencationGuard } from './samples/guards/guard'
|
|
4
|
+
|
|
5
|
+
const routes: IRoute[] = [
|
|
6
|
+
{ pattern: '/about', html: `<h2>About Page</h2>` },
|
|
7
|
+
{ pattern: '/profile/:username', component: import('./samples/profile/profile-component') },
|
|
8
|
+
{ pattern: '/tryguard1/:teamId', component: import('./samples/guards/team-component'), guards: [authencationGuard], redirect: '/login' },
|
|
9
|
+
{ pattern: '/login', html: `<h2>Login Page</h2>` },
|
|
10
|
+
{
|
|
11
|
+
pattern: "/programmatic",
|
|
12
|
+
component: import("./samples/programmatic/programmatic-navigation"),
|
|
13
|
+
},
|
|
14
|
+
|
|
15
|
+
{
|
|
16
|
+
pattern: '/nested',
|
|
17
|
+
component: import('./samples/nested/sample-nested'),
|
|
18
|
+
children: () => import("./samples/nested/nested-routes")
|
|
19
|
+
},
|
|
20
|
+
{ pattern: '**', html: `<h2>404 Page</h2>` }
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
export const router = new JongRouter(routes, document.getElementById('app')!)
|
|
24
|
+
router.init()
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|