jong-router 0.1.13 → 0.1.14

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 CHANGED
@@ -1,30 +1,36 @@
1
- # JongRouter
1
+ [![NPM](https://nodei.co/npm/jong-router.svg?style=flat&data=v,d&color=red)](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
- - **Routing**: Define routes and associate them with components.
14
-
15
- - **Guards**: Implement route guards to control navigation based on conditions. ([Example](https://github.com/josnin/jong-router/tree/main/samples/guards))
16
-
17
- - **Nested Routes**: Create hierarchical routes for nested components. ([Example](https://github.com/josnin/jong-router/tree/main/samples/nestedroutes))
18
-
19
- - **Page Not Found**: Handle routes that do not match any defined route. ([Example](https://github.com/josnin/jong-router/tree/main/samples/page-not-found))
20
-
21
- - **Query Parameters**: Pass query parameters to components. ([Example](https://github.com/josnin/jong-router/tree/main/samples/query-params))
22
-
23
- - **Route Parameters**: Extract and pass route parameters to components. ([Example](https://github.com/josnin/jong-router/tree/main/samples/route-params))
24
-
25
- - **Route Data**: Include additional data associated with each route. ([Example](https://github.com/josnin/jong-router/tree/main/samples/route-data))
26
-
27
- - **Router Link**: Use attribute `router-link` to navigate without reloading the page. ([Example](https://github.com/josnin/jong-router/tree/main/samples/router-link))
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 router = new JongRouter([
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
- // Add more routes as needed
86
+ { pattern: '**', html: `<h2>Page not found</h2>` }
81
87
 
82
- ], document.getElementById('app') );
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 = '<h1>Home Component</h1>';
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,35 @@ 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
+ ```
140
153
 
154
+ 4. Programmatic Navigation
155
+ Components can navigate programmatically.
141
156
 
142
- 4. **Guards**
157
+ ```js
158
+ import { router } from "../router-instance"
159
+
160
+ router.navigateTo("/profile/admin")
161
+ ```
162
+
163
+ Example inside a component
164
+ ```js
165
+ this.shadowRoot
166
+ .getElementById("btn")
167
+ .addEventListener("click", () => {
168
+
169
+ router.navigateTo("/profile/admin")
170
+
171
+ })
172
+ ```
173
+
174
+
175
+ 5. **Guards**
143
176
 
144
177
 
145
178
 
@@ -170,9 +203,11 @@ const router = new JongRouter([
170
203
 
171
204
 
172
205
 
173
- function isAuthenticated() {
206
+ function isAuthenticated(ctx) {
174
207
 
175
208
  // Your authentication logic here
209
+ console.log(ctx.path)
210
+ console.log(ctx.params)
176
211
 
177
212
  return true;
178
213
 
@@ -182,44 +217,109 @@ function isAuthenticated() {
182
217
 
183
218
 
184
219
 
185
- 5. **Handle Route Parameters and Query Parameters:**
186
-
220
+ 6. **Handle Route Parameters and Query Parameters:**
187
221
 
188
222
 
189
- Access route parameters and query parameters in your components.
190
-
191
-
192
-
193
- ```javascript
194
-
195
- // Example: UserComponent.js
196
-
197
- class UserComponent extends HTMLElement {
198
-
199
- connectedCallback() {
200
-
201
- const routeParams = JSON.parse(this.getAttribute('route-params'));
223
+ Define dynamic routes Parameter:
224
+ ```js
225
+ {
226
+ pattern: "/profile/:username",
227
+ component: import("./ProfileComponent")
228
+ }
229
+ ```
202
230
 
203
- const queryParams = JSON.parse(this.getAttribute('query-params'));
231
+ Access inside the component
232
+ ```js
233
+ const params = JSON.parse(
234
+ this.getAttribute("route-params")
235
+ )
204
236
 
237
+ console.log(params.username)
238
+ ```
205
239
 
206
240
 
207
- this.innerHTML = `<h1>User Details</h1>
241
+ Define query parameter
242
+ ```js
243
+ /profile/jong?tab=settings
244
+ ```
208
245
 
209
- <p>User ID: ${routeParams.id}</p>
246
+ Inside component
247
+ ```js
248
+ const query = JSON.parse(
249
+ this.getAttribute("query-params")
250
+ )
210
251
 
211
- <p>Query Param: ${queryParams.example}</p>`;
252
+ console.log(query.tab)
253
+ ```
212
254
 
213
- }
255
+ 7. **Route Data**
256
+ Attach metadata to routes
214
257
 
258
+ ```js
259
+ {
260
+ pattern: "/profile/:username",
261
+ component: import("./ProfileComponent"),
262
+ data: { role: "admin" }
215
263
  }
264
+ ```
265
+ Access in component
266
+ ```js
267
+ const data = JSON.parse(
268
+ this.getAttribute("route-data")
269
+ )
270
+ ```
216
271
 
217
272
 
273
+ 8. **Nested Routes**
274
+ JongRouter supports child routers inside components.
218
275
 
219
- customElements.define('user-component', UserComponent);
276
+ Example
277
+ ```bash
278
+ /nested
279
+ ├─ /nested/c1
280
+ ├─ /nested/c2
281
+ └─ /nested/c3
282
+ ```
220
283
 
284
+ A parent component can create its own router instance
285
+ ```js
286
+ const childRouter = new JongRouter(
287
+ childRoutes,
288
+ this.shadowRoot.getElementById("outlet"),
289
+ "/nested",
290
+ true
291
+ )
292
+
293
+ childRouter.init()
221
294
  ```
222
295
 
296
+ ## Playground & Examples
297
+ Playground & Examples
298
+ The repository contains a Playground demonstrating:
299
+ * Basic routing
300
+ * Guarded routes
301
+ * Nested routes
302
+ * Query parameters
303
+ * Route parameters
304
+ * Programmatic navigation
305
+ * Buttons & router-link navigation
306
+
307
+
308
+ # Why JongRouter?
309
+ JongRouter focuses on simplicity and Web Component compatibility.
310
+ Unlike many routers, it:
311
+ * Works without frameworks
312
+ * Supports Shadow DOM
313
+ * Keeps the API extremely small
314
+ * Is easy to embed in micro-frontend architectures
315
+
316
+ Perfect for:
317
+ * Web Component apps
318
+ * Micro-frontends
319
+ * Lightweight SPAs
320
+ * Vanilla JS projects
321
+
322
+
223
323
  ## How to run development server?
224
324
  ```
225
325
  git clone git@github.com:josnin/jong-router.git
@@ -0,0 +1,4 @@
1
+ export declare const attachShadow: ShadowRootInit;
2
+ export default class About extends HTMLElement {
3
+ constructor();
4
+ }
@@ -0,0 +1,2 @@
1
+ import { GuardContext } from './jong-router';
2
+ export declare function authencationGuard(ctx?: GuardContext): boolean;
@@ -0,0 +1,4 @@
1
+ export declare const attachShadow: ShadowRootInit;
2
+ export default class Home extends HTMLElement {
3
+ constructor();
4
+ }
@@ -1,26 +1,30 @@
1
- interface IRoute {
1
+ export interface GuardContext {
2
+ path?: string;
3
+ params?: Record<string, string>;
4
+ query?: URLSearchParams;
5
+ data?: any;
6
+ }
7
+ export interface IRoute {
2
8
  pattern: string;
3
9
  component?: Promise<any>;
4
10
  html?: string;
5
- guards?: (() => boolean)[];
11
+ children?: IRoute[];
12
+ guards?: ((ctx?: any) => boolean)[];
6
13
  redirect?: string;
7
- data?: any;
14
+ data?: Record<string, any>;
8
15
  }
9
16
  declare class JongRouter {
10
17
  private routes;
11
18
  private outlet;
12
- private shadowRoot1;
13
- constructor(routes: IRoute[], outlet: HTMLElement, shadowRoot1?: ShadowRoot | undefined);
19
+ private basePath;
20
+ private isRoot;
21
+ constructor(routes: IRoute[], outlet: HTMLElement, basePath?: string, isRoot?: boolean);
14
22
  init(): void;
15
- private setupNavigation;
16
- private navigate;
17
- private loadContent;
18
- private loadComponent;
19
23
  private handleClick;
24
+ private navigate;
25
+ private renderRoute;
20
26
  private matchRoute;
21
- private extractQueryParams;
22
27
  private extractRouteParams;
23
28
  navigateTo(route: string): void;
24
29
  }
25
- export { IRoute };
26
30
  export default JongRouter;
@@ -1,2 +1,2 @@
1
- var a=class{routes;outlet;shadowRoot1;constructor(e,n,t){this.routes=e,this.outlet=n,this.shadowRoot1=t}init(){this.setupNavigation(),this.navigate()}setupNavigation(){window.addEventListener("popstate",()=>this.navigate()),document.addEventListener("click",e=>this.handleClick(e))}navigate(){let e=window.location.pathname,n=this.routes.find(o=>this.matchRoute(o.pattern,e));if(n){if(n.guards&&n.guards.every(s=>{let i=s.bind(this)();return i||(n.redirect?this.navigateTo(n.redirect):console.warn("Guard prevented navigation, and no redirect route specified!")),i===!0})===!1)return;n.component?this.loadComponent(n.component,this.extractRouteParams(n.pattern,e),n.data):n.html?this.loadContent(n.html):console.warn("no component or html route specified!");return}let t=this.routes.find(o=>o.pattern==="**");t&&(t.component?this.loadComponent(t.component,this.extractRouteParams(t.pattern,e),t.data):t.html&&this.loadContent(t.html))}loadContent(e){this.outlet.innerHTML=e}async loadComponent(e,n,t){e.then(o=>{let s=o.default,i=new s,r=this.extractQueryParams();n&&i.setAttribute("route-params",JSON.stringify(n)),t&&i.setAttribute("route-data",JSON.stringify(t)),r&&i.setAttribute("query-params",JSON.stringify(r)),i.router=this,this.outlet.innerHTML="",this.outlet.appendChild(i)}).catch(o=>{console.error(`Error loading component: ${o}`),this.outlet.innerHTML="Component not found"})}handleClick(e){let t=e.composedPath().includes(this.shadowRoot1)?e.composedPath()[0]:e.target;if((t instanceof HTMLAnchorElement||t instanceof HTMLButtonElement)&&t.hasAttribute("router-link")){e.preventDefault();let o=t.getAttribute("href");window.history.pushState({},"",o),this.navigate()}}matchRoute(e,n){let t=e.split("/").filter(s=>s!==""),o=n.split("/").filter(s=>s!=="");return t.length===o.length&&t.every((s,i)=>s.startsWith(":")||s===o[i])}extractQueryParams(){let e=window.location.search,n=new URLSearchParams(e),t={};return n.forEach((o,s)=>{t[s]=o}),t}extractRouteParams(e,n){return e.split("/").reduce((t,o,s)=>(o.startsWith(":")&&(t[o.slice(1)]=n.split("/")[s]),t),{})}navigateTo(e){window.history.pushState({},"",e),this.navigate()}},c=a;export{c as default};
1
+ var s=class c{routes;outlet;basePath;isRoot;constructor(t,a,i="",n=!1){this.routes=t,this.outlet=a,this.basePath=i,this.isRoot=n}init(){this.isRoot&&(window.addEventListener("popstate",()=>this.navigate()),document.addEventListener("click",t=>this.handleClick(t))),this.navigate()}handleClick(t){let i=t.composedPath().find(e=>(e instanceof HTMLAnchorElement||e instanceof HTMLButtonElement)&&e.hasAttribute("router-link"));if(!i)return;let n=i.getAttribute("href");!n||n.startsWith("http")||t instanceof MouseEvent&&(t.metaKey||t.ctrlKey||t.shiftKey||t.altKey)||(t.preventDefault(),this.navigateTo(n))}navigate(){let t=window.location.pathname,a=this.routes.find(n=>this.matchRoute(n.pattern,t));if(!a)return;let i=this.extractRouteParams(a.pattern,t);this.renderRoute(a,i)}async renderRoute(t,a){if(t.guards){let i={path:window.location.pathname,params:a,query:new URLSearchParams(window.location.search),data:t.data};if(!t.guards.every(e=>e(i))){t.redirect&&this.navigateTo(t.redirect);return}}if(t.component){let n=(await t.component).default,e=new n;if(e.setAttribute("route-params",JSON.stringify(a)),e.router=this,this.outlet.innerHTML="",this.outlet.appendChild(e),t.children){let o=e.shadowRoot?.querySelector("[router-outlet]")||e.querySelector("[router-outlet]");o&&new c(t.children,o,t.pattern).navigate()}}else t.html&&(this.outlet.innerHTML=t.html)}matchRoute(t,a){if(t==="**")return!0;let i=t.split("/").filter(Boolean),n=a.split("/").filter(Boolean);return i.length>n.length?!1:i.every((e,o)=>e.startsWith(":")||e===n[o])}extractRouteParams(t,a){let i={},n=t.split("/").filter(Boolean),e=a.split("/").filter(Boolean);return n.forEach((o,r)=>{o.startsWith(":")&&(i[o.slice(1)]=e[r])}),i}navigateTo(t){window.history.pushState({},"",t),this.navigate()}},h=s;export{h as default};
2
2
  //# sourceMappingURL=jong-router.min.js.map
@@ -0,0 +1,4 @@
1
+ export declare const attachShadow: ShadowRootInit;
2
+ export default class NotFound extends HTMLElement {
3
+ constructor();
4
+ }
@@ -0,0 +1,6 @@
1
+ export declare const attachShadow: ShadowRootInit;
2
+ export default class Profile extends HTMLElement {
3
+ router: any;
4
+ constructor();
5
+ connectedCallback(): void;
6
+ }
@@ -0,0 +1,5 @@
1
+ export declare const attachShadow: ShadowRootInit;
2
+ export default class Team extends HTMLElement {
3
+ constructor();
4
+ connectedCallback(): void;
5
+ }
package/package.json CHANGED
@@ -1,11 +1,25 @@
1
1
  {
2
2
  "name": "jong-router",
3
- "version": "0.1.13",
4
- "description": "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.",
3
+ "version": "0.1.14",
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,30 @@
1
+ // index.ts
2
+ import JongRouter, { IRoute } from './src/jong-router'
3
+ import { authencationGuard } from './src/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: [
19
+ { pattern: '/nested/c1', html: '<p>Nested C1 Page</p>' },
20
+ { pattern: '/nested/c2', html: '<p>Nested C2 Page</p>' },
21
+ { pattern: '/nested/c3', html: '<p>Nested C3 Page</p>' },
22
+ { pattern: '**', html: '<p>Nested 404</p>' }
23
+ ]
24
+ },
25
+ { pattern: '**', html: `<h2>404 Page</h2>` }
26
+ ]
27
+
28
+ export const router = new JongRouter(routes, document.getElementById('app')!, '', true)
29
+ router.init()
30
+