@iyulab/router 0.6.0 โ†’ 0.6.1

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
@@ -6,13 +6,9 @@ A modern, lightweight client-side router for web applications with support for b
6
6
 
7
7
  - ๐Ÿš€ **Modern URLPattern-based routing** - Uses native URLPattern API for powerful path matching
8
8
  - ๐Ÿ”ง **Unified Framework Support** - Works with both Lit and React components using render functions
9
- - ๐Ÿ“ฑ **Client-side Navigation** - History API integration with browser back/forward support
9
+ - ๐Ÿ“ฑ **Client-Side Navigation** - History API integration with browser back/forward support
10
10
  - ๐ŸŽฏ **Nested Routing** - Support for deeply nested route hierarchies with index and path routes
11
- - ๐Ÿ”— **Smart Link Component** - Automatic external link detection and handling
12
11
  - ๐Ÿ“Š **Route Events** - Track navigation progress with route-begin, route-done, and route-error events
13
- - ๐ŸŽจ **Flexible Outlet System** - Unified rendering with renderContent method
14
- - ๐ŸŒ **Global Route Access** - Access current route information anywhere via `window.route`
15
- - ๐Ÿ”„ **Force Re-rendering** - Control component re-rendering on route changes
16
12
  - โš ๏ธ **Enhanced Error Handling** - Built-in ErrorPage component with improved styling
17
13
 
18
14
  ## Installation
@@ -30,43 +26,18 @@ import { Router } from '@iyulab/router';
30
26
  import { html } from 'lit';
31
27
 
32
28
  const router = new Router({
33
- root: document.getElementById('app')!,
34
- basepath: '/app',
29
+ basepath: '/',
35
30
  routes: [
36
31
  {
37
32
  index: true,
38
33
  render: () => html`<home-page></home-page>`
39
34
  },
40
35
  {
41
- path: '/user/:id',
36
+ path: '/user/:id', // URLPattern route
42
37
  render: (routeInfo) => html`<user-page .userId=${routeInfo.params.id}></user-page>`
43
38
  }
44
- ]
39
+ ],
45
40
  });
46
-
47
- // Start routing
48
- router.go(window.location.href);
49
- ```
50
-
51
- ### Nested Routes
52
-
53
- ```typescript
54
- const routes = [
55
- {
56
- path: '/dashboard',
57
- render: () => html`<dashboard-layout><u-outlet></u-outlet></dashboard-layout>`,
58
- children: [
59
- {
60
- index: true, // Matches /dashboard exactly
61
- render: () => html`<dashboard-home></dashboard-home>`
62
- },
63
- {
64
- path: 'settings',
65
- render: () => html`<dashboard-settings></dashboard-settings>`
66
- }
67
- ]
68
- }
69
- ];
70
41
  ```
71
42
 
72
43
  ### Mixed Framework Support
@@ -78,12 +49,16 @@ const routes = [
78
49
  // Lit component
79
50
  {
80
51
  path: '/lit-page',
81
- render: (routeInfo) => html`<my-lit-component .routeInfo=${routeInfo}></my-lit-component>`
52
+ render: (routeInfo) => {
53
+ return html`<my-lit-component .routeInfo=${routeInfo}></my-lit-component>`
54
+ }
82
55
  },
83
56
  // React component
84
57
  {
85
58
  path: '/react-page',
86
- render: (routeInfo) => React.createElement(MyReactComponent, { routeInfo })
59
+ render: (routeInfo) => {
60
+ return ( <MyComponent></MyComponent> )
61
+ }
87
62
  },
88
63
  // HTML element
89
64
  {
@@ -97,14 +72,39 @@ const routes = [
97
72
  ];
98
73
  ```
99
74
 
75
+ ### Nested Routes
76
+
77
+ ```typescript
78
+ import { RouteConfig } from '@iyulab/router';
79
+
80
+ const routes: RouteConfig[] = [
81
+ {
82
+ path: '/dashboard',
83
+ render: () => html`<dashboard-layout><u-outlet></u-outlet></dashboard-layout>`,
84
+ children: [
85
+ {
86
+ index: true, // Matches '/dashboard'
87
+ render: () => html`<dashboard-home></dashboard-home>`
88
+ },
89
+ {
90
+ path: 'settings', // Matches '/dashboard/settings'
91
+ render: () => html`<dashboard-settings></dashboard-settings>`
92
+ }
93
+ ]
94
+ }
95
+ ];
96
+ ```
97
+
100
98
  ## Usage Examples
101
99
 
102
- ### Using with Lit Components
100
+ ### Using with Lit Elements
103
101
 
104
102
  ```typescript
105
103
  import { LitElement, html } from 'lit';
106
104
  import { customElement } from 'lit/decorators.js';
107
105
 
106
+ import "@iyulab/router";
107
+
108
108
  @customElement('app-root')
109
109
  export class AppRoot extends LitElement {
110
110
  render() {
@@ -146,4 +146,4 @@ export function AppRoot() {
146
146
 
147
147
  ## License
148
148
 
149
- MIT License - see [LICENSE](LICENSE) file for details.
149
+ MIT License - see [LICENSE](LICENSE) file for details.
package/dist/index.d.ts CHANGED
@@ -325,7 +325,7 @@ export declare class Router {
325
325
  /** ๋ธŒ๋ผ์šฐ์ € ํžˆ์Šคํ† ๋ฆฌ ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒ์‹œ ๋ผ์šฐํŒ… ์ฒ˜๋ฆฌ */
326
326
  private handleWindowPopstate;
327
327
  /** ํด๋ฆญ ์ด๋ฒคํŠธ์—์„œ ๋ผ์šฐํ„ฐ๋กœ ์ฒ˜๋ฆฌํ•  ์•ต์ปค๋ฅผ ์ฐพ์•„ ํด๋ผ์ด์–ธํŠธ ๋ผ์šฐํŒ… ์ˆ˜ํ–‰ */
328
- private handleRootClick;
328
+ private handleDocumentClick;
329
329
  }
330
330
 
331
331
  /**
@@ -374,8 +374,6 @@ export declare interface RouterConfig {
374
374
  export declare class ULink extends LitElement {
375
375
  /** ์™ธ๋ถ€ ๋งํฌ ์—ฌ๋ถ€ */
376
376
  private isExternal;
377
- /** a ํƒœ๊ทธ์— ์ฃผ์ž…ํ•  href ๊ฐ’ */
378
- computedHref: string;
379
377
  /**
380
378
  * a ํƒœ๊ทธ target์„ ์ง€์›ํ•˜๊ณ  ์‹ถ์œผ๋ฉด ์—ด์–ด๋‘๋Š”๊ฒŒ ์ข‹์Šต๋‹ˆ๋‹ค.
381
379
  * - _blank ๋“ฑ์„ ์“ฐ๋ฉด ๋ฌด์กฐ๊ฑด ๋ธŒ๋ผ์šฐ์ € ๊ธฐ๋ณธ ๋™์ž‘์„ ๋”ฐ๋ฅด๋„๋ก ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.
@@ -416,6 +414,8 @@ export declare class UOutlet extends LitElement {
416
414
  private content?;
417
415
  /** ์™ธ๋ถ€ ์Šคํƒ€์ผ์„ ์ ์šฉํ•˜๊ธฐ ์œ„ํ•ด ๋ผ์ดํŠธ ๋”์„์‚ฌ์šฉ ํ•ฉ๋‹ˆ๋‹ค. */
418
416
  protected createRenderRoot(): this;
417
+ /** ์ปดํฌ๋„ŒํŠธ๊ฐ€ DOM์— ์—ฐ๊ฒฐ๋  ๋•Œ ์ดˆ๊ธฐํ™” ์ž‘์—… ์ˆ˜ํ–‰ */
418
+ connectedCallback(): void;
419
419
  render(): TemplateResult<1>;
420
420
  /**
421
421
  * render ํ•จ์ˆ˜์˜ ๊ฒฐ๊ณผ๋ฅผ ๋ Œ๋”๋งํ•ฉ๋‹ˆ๋‹ค.
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
- import { a as absolutePath, i as isExternalUrl, p as parseUrl } from "./share-ZrQFmsur.js";
2
- import { b, U } from "./share-ZrQFmsur.js";
1
+ import { a as absolutePath, i as isExternalUrl, p as parseUrl } from "./share-SPj-xl6y.js";
2
+ import { b, U } from "./share-SPj-xl6y.js";
3
3
  import { css, LitElement, html } from "lit";
4
4
  import { property, customElement } from "lit/decorators.js";
5
5
  import { unsafeHTML } from "lit-html/directives/unsafe-html.js";
@@ -204,7 +204,7 @@ UErrorPage.styles = css`
204
204
  --route-code-color: #f7fafc;
205
205
  --route-message-color: #cbd5e0;
206
206
  }
207
- };
207
+ }
208
208
 
209
209
  :host {
210
210
  display: flex;
@@ -282,15 +282,24 @@ function findOutletOrThrow(element) {
282
282
  }
283
283
  return outlet;
284
284
  }
285
- function findAnchorFromEvent(e) {
286
- const targets = e.composedPath() || [];
285
+ async function waitOutlet(element, timeout = 1e4) {
286
+ const start = performance.now();
287
+ while (performance.now() - start < timeout) {
288
+ const outlet = findOutlet(element);
289
+ if (outlet) return outlet;
290
+ await new Promise((r) => setTimeout(r, 50));
291
+ }
292
+ throw new Error("Timed out waiting for u-outlet.");
293
+ }
294
+ function findAnchorFrom(event) {
295
+ const targets = event.composedPath() || [];
287
296
  if (targets && targets.length) {
288
297
  for (const node of targets) {
289
298
  if (!(node instanceof Element)) continue;
290
299
  if (node.tagName === "A") return node;
291
300
  }
292
301
  }
293
- const tgt = e.target;
302
+ const tgt = event.target;
294
303
  if (!tgt) return null;
295
304
  const anchor = tgt.closest("a");
296
305
  return anchor;
@@ -350,11 +359,11 @@ class Router {
350
359
  const href = window.location.href;
351
360
  await this.go(href);
352
361
  };
353
- this.handleRootClick = (e) => {
362
+ this.handleDocumentClick = (e) => {
354
363
  try {
355
364
  if (e.defaultPrevented) return;
356
365
  if (e.button !== 0 || e.metaKey || e.ctrlKey || e.shiftKey) return;
357
- const anchor = findAnchorFromEvent(e);
366
+ const anchor = findAnchorFrom(e);
358
367
  if (!anchor) return;
359
368
  const href = anchor.getAttribute("href") || anchor.href;
360
369
  if (!href) return;
@@ -374,17 +383,19 @@ class Router {
374
383
  window.removeEventListener("popstate", this.handleWindowPopstate);
375
384
  window.addEventListener("popstate", this.handleWindowPopstate);
376
385
  if (config.useIntercept !== false) {
377
- this._rootElement.removeEventListener("click", this.handleRootClick);
378
- this._rootElement.addEventListener("click", this.handleRootClick);
386
+ document.removeEventListener("click", this.handleDocumentClick);
387
+ document.addEventListener("click", this.handleDocumentClick);
379
388
  }
380
389
  if (config.initialLoad !== false) {
381
- void this.go(window.location.href);
390
+ void waitOutlet(this._rootElement).then(() => {
391
+ this.go(window.location.href);
392
+ });
382
393
  }
383
394
  }
384
395
  /** ๊ฐ์ฒด๋ฅผ ์ •๋ฆฌํ•˜๊ณ  ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ๋ฅผ ์ œ๊ฑฐํ•ฉ๋‹ˆ๋‹ค. */
385
396
  destroy() {
386
397
  window.removeEventListener("popstate", this.handleWindowPopstate);
387
- this._rootElement.removeEventListener("click", this.handleRootClick);
398
+ document.removeEventListener("click", this.handleDocumentClick);
388
399
  this._requestID = void 0;
389
400
  this._context = void 0;
390
401
  }
package/dist/react.d.ts CHANGED
@@ -27,8 +27,6 @@ export declare const ULink: ReactWebComponent<ULink_2, {}>;
27
27
  declare class ULink_2 extends LitElement {
28
28
  /** ์™ธ๋ถ€ ๋งํฌ ์—ฌ๋ถ€ */
29
29
  private isExternal;
30
- /** a ํƒœ๊ทธ์— ์ฃผ์ž…ํ•  href ๊ฐ’ */
31
- computedHref: string;
32
30
  /**
33
31
  * a ํƒœ๊ทธ target์„ ์ง€์›ํ•˜๊ณ  ์‹ถ์œผ๋ฉด ์—ด์–ด๋‘๋Š”๊ฒŒ ์ข‹์Šต๋‹ˆ๋‹ค.
34
32
  * - _blank ๋“ฑ์„ ์“ฐ๋ฉด ๋ฌด์กฐ๊ฑด ๋ธŒ๋ผ์šฐ์ € ๊ธฐ๋ณธ ๋™์ž‘์„ ๋”ฐ๋ฅด๋„๋ก ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.
@@ -74,6 +72,8 @@ declare class UOutlet_2 extends LitElement {
74
72
  private content?;
75
73
  /** ์™ธ๋ถ€ ์Šคํƒ€์ผ์„ ์ ์šฉํ•˜๊ธฐ ์œ„ํ•ด ๋ผ์ดํŠธ ๋”์„์‚ฌ์šฉ ํ•ฉ๋‹ˆ๋‹ค. */
76
74
  protected createRenderRoot(): this;
75
+ /** ์ปดํฌ๋„ŒํŠธ๊ฐ€ DOM์— ์—ฐ๊ฒฐ๋  ๋•Œ ์ดˆ๊ธฐํ™” ์ž‘์—… ์ˆ˜ํ–‰ */
76
+ connectedCallback(): void;
77
77
  render(): TemplateResult_2<1>;
78
78
  /**
79
79
  * render ํ•จ์ˆ˜์˜ ๊ฒฐ๊ณผ๋ฅผ ๋ Œ๋”๋งํ•ฉ๋‹ˆ๋‹ค.
package/dist/react.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import React from "react";
2
- import { b as ULink$1, U as UOutlet$1 } from "./share-ZrQFmsur.js";
2
+ import { b as ULink$1, U as UOutlet$1 } from "./share-SPj-xl6y.js";
3
3
  const e = /* @__PURE__ */ new Set(["children", "localName", "ref", "style", "className"]), n = /* @__PURE__ */ new WeakMap(), t = (e2, t2, o2, l, a) => {
4
4
  const s = a?.[t2];
5
5
  void 0 === s ? (e2[t2] = o2, null == o2 && t2 in HTMLElement.prototype && e2.removeAttribute(t2)) : o2 !== l && ((e3, t3, o3) => {
@@ -1,5 +1,5 @@
1
1
  import { LitElement, html, render, css } from "lit";
2
- import { customElement, state, property } from "lit/decorators.js";
2
+ import { customElement, property } from "lit/decorators.js";
3
3
  import { createRoot } from "react-dom/client";
4
4
  var __getOwnPropDesc$1 = Object.getOwnPropertyDescriptor;
5
5
  var __decorateClass$1 = (decorators, target, key, kind) => {
@@ -14,6 +14,14 @@ let UOutlet = class extends LitElement {
14
14
  createRenderRoot() {
15
15
  return this;
16
16
  }
17
+ /** ์ปดํฌ๋„ŒํŠธ๊ฐ€ DOM์— ์—ฐ๊ฒฐ๋  ๋•Œ ์ดˆ๊ธฐํ™” ์ž‘์—… ์ˆ˜ํ–‰ */
18
+ connectedCallback() {
19
+ super.connectedCallback();
20
+ this.dispatchEvent(new CustomEvent("outlet-load", {
21
+ bubbles: true,
22
+ composed: true
23
+ }));
24
+ }
17
25
  render() {
18
26
  return html`${this.container}`;
19
27
  }
@@ -143,7 +151,6 @@ let ULink = class extends LitElement {
143
151
  constructor() {
144
152
  super(...arguments);
145
153
  this.isExternal = false;
146
- this.computedHref = "#";
147
154
  this.handleAnchorClick = (event) => {
148
155
  if (event.defaultPrevented) return;
149
156
  if (event.button !== 0) return;
@@ -180,16 +187,14 @@ let ULink = class extends LitElement {
180
187
  super.willUpdate(changedProperties);
181
188
  if (changedProperties.has("href")) {
182
189
  this.isExternal = isExternalUrl(this.href || "");
183
- this.computedHref = this.computeHref(this.href);
184
190
  }
185
191
  }
186
192
  render() {
187
193
  return html`
188
194
  <a
189
195
  target=${this.target ?? "_self"}
190
- href=${this.computedHref}
191
- @click=${this.handleAnchorClick}
192
- >
196
+ href=${this.computeHref(this.href)}
197
+ @click=${this.handleAnchorClick}>
193
198
  <slot></slot>
194
199
  </a>
195
200
  `;
@@ -232,9 +237,6 @@ ULink.styles = css`
232
237
  cursor: inherit;
233
238
  }
234
239
  `;
235
- __decorateClass([
236
- state()
237
- ], ULink.prototype, "computedHref", 2);
238
240
  __decorateClass([
239
241
  property({ type: String })
240
242
  ], ULink.prototype, "target", 2);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@iyulab/router",
3
- "version": "0.6.0",
3
+ "version": "0.6.1",
4
4
  "description": "A modern client-side router for web applications with support for Lit and React components",
5
5
  "keywords": [
6
6
  "lit",
@@ -36,6 +36,7 @@
36
36
  }
37
37
  },
38
38
  "scripts": {
39
+ "test": "vite",
39
40
  "build": "vite build"
40
41
  },
41
42
  "dependencies": {
@@ -45,7 +46,7 @@
45
46
  },
46
47
  "devDependencies": {
47
48
  "@lit/react": "^1.0.8",
48
- "@types/node": "^25.0.8",
49
+ "@types/node": "^25.0.9",
49
50
  "@types/react": "^19.2.8",
50
51
  "@types/react-dom": "^19.2.3",
51
52
  "typescript": "^5.9.3",