@iyulab/router 0.6.0 โ†’ 0.6.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 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
@@ -145,10 +145,14 @@ export declare class OutletMissingError extends RouteError {
145
145
  constructor();
146
146
  }
147
147
 
148
+ /** ๋ Œ๋”๋ง ์˜ต์…˜ */
148
149
  declare interface RenderOption {
150
+ /** ๊ต์ฐจ ๋ Œ๋”๋ง ๋ฐฉ์ง€ ID */
149
151
  id?: string;
152
+ /** ๊ฐ•์ œ ๋ Œ๋”๋ง ์—ฌ๋ถ€ */
150
153
  force?: boolean;
151
- content: RenderResult;
154
+ /** ๋ Œ๋”๋งํ•  ๊ฐ’ */
155
+ value: RenderResult;
152
156
  }
153
157
 
154
158
  export declare type RenderResult = HTMLElement | ReactElement | TemplateResult<1> | false;
@@ -297,7 +301,7 @@ export declare class RouteProgressEvent extends RouteEvent {
297
301
  }
298
302
 
299
303
  /**
300
- * `lit-element`, `react`๋ฅผ ์ง€์›ํ•˜๋Š” ํด๋ผ์ด์–ธํŠธ ์‚ฌ์ด๋“œ ๋ผ์šฐํ„ฐ
304
+ * `lit-element`, `react`๋ฅผ ์ง€์›ํ•˜๋Š” SPA ํด๋ผ์ด์–ธํŠธ ๋ผ์šฐํ„ฐ ๊ฐ์ฒด์ž…๋‹ˆ๋‹ค.
301
305
  */
302
306
  export declare class Router {
303
307
  private readonly _rootElement;
@@ -325,7 +329,7 @@ export declare class Router {
325
329
  /** ๋ธŒ๋ผ์šฐ์ € ํžˆ์Šคํ† ๋ฆฌ ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒ์‹œ ๋ผ์šฐํŒ… ์ฒ˜๋ฆฌ */
326
330
  private handleWindowPopstate;
327
331
  /** ํด๋ฆญ ์ด๋ฒคํŠธ์—์„œ ๋ผ์šฐํ„ฐ๋กœ ์ฒ˜๋ฆฌํ•  ์•ต์ปค๋ฅผ ์ฐพ์•„ ํด๋ผ์ด์–ธํŠธ ๋ผ์šฐํŒ… ์ˆ˜ํ–‰ */
328
- private handleRootClick;
332
+ private handleDocumentClick;
329
333
  }
330
334
 
331
335
  /**
@@ -374,58 +378,61 @@ export declare interface RouterConfig {
374
378
  export declare class ULink extends LitElement {
375
379
  /** ์™ธ๋ถ€ ๋งํฌ ์—ฌ๋ถ€ */
376
380
  private isExternal;
377
- /** a ํƒœ๊ทธ์— ์ฃผ์ž…ํ•  href ๊ฐ’ */
378
- computedHref: string;
379
381
  /**
380
- * a ํƒœ๊ทธ target์„ ์ง€์›ํ•˜๊ณ  ์‹ถ์œผ๋ฉด ์—ด์–ด๋‘๋Š”๊ฒŒ ์ข‹์Šต๋‹ˆ๋‹ค.
381
- * - _blank ๋“ฑ์„ ์“ฐ๋ฉด ๋ฌด์กฐ๊ฑด ๋ธŒ๋ผ์šฐ์ € ๊ธฐ๋ณธ ๋™์ž‘์„ ๋”ฐ๋ฅด๋„๋ก ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.
382
+ * ๋งํฌ ๋Œ€์ƒ target ์†์„ฑ
383
+ *
384
+ * - `_self`: ํ˜„์žฌ ์ฐฝ์—์„œ ๋งํฌ ์—ด๊ธฐ (๊ธฐ๋ณธ๊ฐ’)
385
+ * - `_blank`: ์ƒˆ ํƒญ/์ฐฝ์—์„œ ๋งํฌ ์—ด๊ธฐ
386
+ * - `_parent`: ๋ถ€๋ชจ ํ”„๋ ˆ์ž„์—์„œ ๋งํฌ ์—ด๊ธฐ
387
+ * - `_top`: ์ตœ์ƒ์œ„ ํ”„๋ ˆ์ž„์—์„œ ๋งํฌ ์—ด๊ธฐ
382
388
  */
383
389
  target?: string;
384
390
  /**
385
- * - ์†์„ฑ์„ ์ •์˜ํ•˜์ง€ ์•Š์œผ๋ฉด basepath๋กœ ์ด๋™ํ•ฉ๋‹ˆ๋‹ค.
391
+ * ๋งํฌ ๋Œ€์ƒ URL, ๋‹ค์Œ ์‚ฌํ•ญ์— ๋”ฐ๋ผ SPA ๋ผ์šฐํŒ… ๋˜๋Š” ๋ธŒ๋ผ์šฐ์ € ๋„ค๋น„๊ฒŒ์ด์…˜์ด ๊ฒฐ์ •๋ฉ๋‹ˆ๋‹ค.
392
+ *
393
+ * - ์†์„ฑ์„ ์ •์˜ํ•˜์ง€ ์•Š์œผ๋ฉด ์„ค์ •์—์„œ ์ง€์ •ํ•œ `basepath`๋กœ SPA ๋ผ์šฐํŒ…ํ•ฉ๋‹ˆ๋‹ค.
386
394
  * - http(s)๋กœ ์‹œ์ž‘ํ•˜๋ฉด ์™ธ๋ถ€ ๋งํฌ๋กœ ๊ฐ„์ฃผํ•˜๊ณ  ๋ธŒ๋ผ์šฐ์ € ๋„ค๋น„๊ฒŒ์ด์…˜์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
387
- * - ์ ˆ๋Œ€๊ฒฝ๋กœ(/...)์ผ ๊ฒฝ์šฐ basepath๋กœ ์‹œ์ž‘ํ•˜๋ฉด SPA ๋ผ์šฐํŒ… ๋Œ€์ƒ์ด ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
388
- * - ์ƒ๋Œ€๊ฒฝ๋กœ๋Š” (basepath + ์ƒ๋Œ€๊ฒฝ๋กœ)๋กœ SPA ๋ผ์šฐํŒ… ๋Œ€์ƒ์ด ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
389
- * - ?๋กœ ์‹œ์ž‘ํ•˜๋ฉด ํ˜„์žฌ pathname์— ์ฟผ๋ฆฌ์ŠคํŠธ๋ง์„ ๋ถ™์—ฌ SPA ๋ผ์šฐํŒ…ํ•ฉ๋‹ˆ๋‹ค.
390
- * - #์œผ๋กœ ์‹œ์ž‘ํ•˜๋ฉด ํ˜„์žฌ URL์— ํ•ด์‹œ๋งŒ ๋ฐ”๊พธ๊ณ (๋ธŒ๋ผ์šฐ์ € ๊ธฐ๋ณธ) ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค.
395
+ * - ์ ˆ๋Œ€๊ฒฝ๋กœ(/...)์˜ ๊ฒฝ์šฐ `basepath`๋กœ ์‹œ์ž‘ํ•˜๋ฉด SPA ๋ผ์šฐํŒ…ํ•ฉ๋‹ˆ๋‹ค, ์ด์™ธ ๋ธŒ๋ผ์šฐ์ € ๋„ค๋น„๊ฒŒ์ด์…˜์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
396
+ * - ์ƒ๋Œ€๊ฒฝ๋กœ๋Š” (basepath + ์ƒ๋Œ€๊ฒฝ๋กœ)๋กœ ๊ฒฐํ•ฉํ•˜์—ฌ SPA ๋ผ์šฐํŒ…ํ•ฉ๋‹ˆ๋‹ค.
397
+ * - ?๋กœ ์‹œ์ž‘ํ•˜๋ฉด ํ˜„์žฌ ๊ฒฝ๋กœ์— ์ฟผ๋ฆฌ์ŠคํŠธ๋ง์„ ์ถ”๊ฐ€ํ•˜์—ฌ SPA ๋ผ์šฐํŒ…ํ•ฉ๋‹ˆ๋‹ค.
398
+ * - #์œผ๋กœ ์‹œ์ž‘ํ•˜๋ฉด ๋ธŒ๋ผ์šฐ์ € ๊ธฐ๋ณธ ๋™์ž‘์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
391
399
  */
392
400
  href?: string;
401
+ connectedCallback(): void;
402
+ disconnectedCallback(): void;
393
403
  protected willUpdate(changedProperties: PropertyValues): void;
394
404
  render(): TemplateResult_2<1>;
395
- /** basepath๋ฅผ state์—์„œ ๊บผ๋‚ด๋Š” ํ—ฌํผ */
396
- private getBasepath;
397
405
  /** a ํƒœ๊ทธ์— ์ฃผ์ž…ํ•  href ๊ฐ’์„ ๊ณ„์‚ฐํ•ฉ๋‹ˆ๋‹ค. */
398
- private computeHref;
406
+ private compute;
399
407
  /**
400
- * ํด๋ฆญ ๊ฐ€๋กœ์ฑ„๊ธฐ ๊ทœ์น™
408
+ * ํด๋ฆญ ๊ฐ€๋กœ์ฑ„๊ธฐ ํ•ธ๋“ค๋Ÿฌ
401
409
  * - ์ขŒํด๋ฆญ(0) + ๋ณด์กฐํ‚ค ์—†์Œ(ctrl/meta/shift/alt ์—†์Œ) + target์ด _self์ผ ๋•Œ๋งŒ SPA ๋ผ์šฐํŒ… ๊ณ ๋ ค
402
410
  * - ๊ทธ ์™ธ(์ค‘ํด๋ฆญ/์šฐํด๋ฆญ/๋ณด์กฐํ‚ค/target=_blank ๋“ฑ)๋Š” ๋ธŒ๋ผ์šฐ์ € ๊ธฐ๋ณธ ๋™์ž‘ ์œ ์ง€
403
411
  */
404
- private handleAnchorClick;
412
+ private handleClick;
405
413
  /** ํด๋ผ์ด์–ธํŠธ ๋ผ์šฐํŒ…์„ ์œ„ํ•ด popstate ์ด๋ฒคํŠธ๋ฅผ ๋ฐœ์ƒ์‹œํ‚ต๋‹ˆ๋‹ค. */
406
414
  private dispatchPopstate;
415
+ /** basepath๋ฅผ state์—์„œ ๊บผ๋‚ด๋Š” ํ—ฌํผ */
416
+ private getBasepath;
407
417
  static styles: CSSResult;
408
418
  }
409
419
 
410
420
  /**
411
- * ๋ผ์šฐํŠธ ์„ค์ •์— ๋”ฐ๋ผ LitElement ๋˜๋Š” React ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ Œ๋”๋งํ•ฉ๋‹ˆ๋‹ค.
421
+ * LitElement ๋˜๋Š” React ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ Œ๋”๋งํ•ด์ฃผ๋Š” ์›น์ปดํฌ๋„ŒํŠธ ์ž…๋‹ˆ๋‹ค.
412
422
  */
413
- export declare class UOutlet extends LitElement {
423
+ export declare class UOutlet extends HTMLElement {
424
+ /** ๊ต์ฐจ ๋ Œ๋”๋ง ๋ฐฉ์ง€ id */
414
425
  private routeId?;
415
- private container?;
416
- private content?;
417
- /** ์™ธ๋ถ€ ์Šคํƒ€์ผ์„ ์ ์šฉํ•˜๊ธฐ ์œ„ํ•ด ๋ผ์ดํŠธ ๋”์„์‚ฌ์šฉ ํ•ฉ๋‹ˆ๋‹ค. */
418
- protected createRenderRoot(): this;
419
- render(): TemplateResult<1>;
426
+ /** ์‹ค์ œ ๋ Œ๋”๋ง ์ปจํ…์ธ  */
427
+ private root?;
420
428
  /**
421
- * render ํ•จ์ˆ˜์˜ ๊ฒฐ๊ณผ๋ฅผ ๋ Œ๋”๋งํ•ฉ๋‹ˆ๋‹ค.
422
- * - HTMLElement, ReactElement, TemplateResult๋ฅผ ๋ชจ๋‘ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
429
+ * ์ฃผ์–ด์ง„ ๋ Œ๋”๋ง ์˜ต์…˜์— ๋”ฐ๋ผ ์ปจํ…์ธ ๋ฅผ ๋ Œ๋”๋งํ•ฉ๋‹ˆ๋‹ค.
423
430
  */
424
- renderContent({ id, content, force }: RenderOption): Promise<HTMLDivElement>;
431
+ render({ id, value, force }: RenderOption): void;
425
432
  /**
426
- * ๊ธฐ์กด DOM์„ ๋ผ์ดํ”„ ์‚ฌ์ดํด์— ๋งž๊ฒŒ ์ œ๊ฑฐํ•ฉ๋‹ˆ๋‹ค.
433
+ * ๊ธฐ์กด DOM์„ ์‚ญ์ œํ•˜์—ฌ, ์ดˆ๊ธฐ ์ƒํƒœ๋กœ ๋˜๋Œ๋ฆฝ๋‹ˆ๋‹ค.
427
434
  */
428
- clear(): void;
435
+ reset(): void;
429
436
  }
430
437
 
431
438
  export { }
package/dist/index.js CHANGED
@@ -1,8 +1,7 @@
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-6Zs4TunA.js";
2
+ import { b, U } from "./share-6Zs4TunA.js";
3
3
  import { css, LitElement, html } from "lit";
4
4
  import { property, customElement } from "lit/decorators.js";
5
- import { unsafeHTML } from "lit-html/directives/unsafe-html.js";
6
5
  class RouteError extends Error {
7
6
  constructor(code, message, original) {
8
7
  super(message);
@@ -74,51 +73,6 @@ class RouteErrorEvent extends RouteEvent {
74
73
  this.error = error;
75
74
  }
76
75
  }
77
- const ban = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">\n <path d="M15 8a6.97 6.97 0 0 0-1.71-4.584l-9.874 9.875A7 7 0 0 0 15 8M2.71 12.584l9.874-9.875a7 7 0 0 0-9.874 9.874ZM16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0"/>\n</svg>';
78
- const __vite_glob_0_0 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
79
- __proto__: null,
80
- default: ban
81
- }, Symbol.toStringTag, { value: "Module" }));
82
- const boxSeam = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">\n <path d="M8.186 1.113a.5.5 0 0 0-.372 0L1.846 3.5l2.404.961L10.404 2zm3.564 1.426L5.596 5 8 5.961 14.154 3.5zm3.25 1.7-6.5 2.6v7.922l6.5-2.6V4.24zM7.5 14.762V6.838L1 4.239v7.923zM7.443.184a1.5 1.5 0 0 1 1.114 0l7.129 2.852A.5.5 0 0 1 16 3.5v8.662a1 1 0 0 1-.629.928l-7.185 2.874a.5.5 0 0 1-.372 0L.63 13.09a1 1 0 0 1-.63-.928V3.5a.5.5 0 0 1 .314-.464z"/>\n</svg>';
83
- const __vite_glob_0_1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
84
- __proto__: null,
85
- default: boxSeam
86
- }, Symbol.toStringTag, { value: "Module" }));
87
- const exclamationTriangle = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">\n <path d="M7.938 2.016A.13.13 0 0 1 8.002 2a.13.13 0 0 1 .063.016.15.15 0 0 1 .054.057l6.857 11.667c.036.06.035.124.002.183a.2.2 0 0 1-.054.06.1.1 0 0 1-.066.017H1.146a.1.1 0 0 1-.066-.017.2.2 0 0 1-.054-.06.18.18 0 0 1 .002-.183L7.884 2.073a.15.15 0 0 1 .054-.057m1.044-.45a1.13 1.13 0 0 0-1.96 0L.165 13.233c-.457.778.091 1.767.98 1.767h13.713c.889 0 1.438-.99.98-1.767z"/>\n <path d="M7.002 12a1 1 0 1 1 2 0 1 1 0 0 1-2 0M7.1 5.995a.905.905 0 1 1 1.8 0l-.35 3.507a.552.552 0 0 1-1.1 0z"/>\n</svg>';
88
- const __vite_glob_0_2 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
89
- __proto__: null,
90
- default: exclamationTriangle
91
- }, Symbol.toStringTag, { value: "Module" }));
92
- const palette = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">\n <path d="M8 5a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3m4 3a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3M5.5 7a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0m.5 6a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3"/>\n <path d="M16 8c0 3.15-1.866 2.585-3.567 2.07C11.42 9.763 10.465 9.473 10 10c-.603.683-.475 1.819-.351 2.92C9.826 14.495 9.996 16 8 16a8 8 0 1 1 8-8m-8 7c.611 0 .654-.171.655-.176.078-.146.124-.464.07-1.119-.014-.168-.037-.37-.061-.591-.052-.464-.112-1.005-.118-1.462-.01-.707.083-1.61.704-2.314.369-.417.845-.578 1.272-.618.404-.038.812.026 1.16.104.343.077.702.186 1.025.284l.028.008c.346.105.658.199.953.266.653.148.904.083.991.024C14.717 9.38 15 9.161 15 8a7 7 0 1 0-7 7"/>\n</svg>';
93
- const __vite_glob_0_3 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
94
- __proto__: null,
95
- default: palette
96
- }, Symbol.toStringTag, { value: "Module" }));
97
- const personLock = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">\n <path d="M11 5a3 3 0 1 1-6 0 3 3 0 0 1 6 0M8 7a2 2 0 1 0 0-4 2 2 0 0 0 0 4m0 5.996V14H3s-1 0-1-1 1-4 6-4q.845.002 1.544.107a4.5 4.5 0 0 0-.803.918A11 11 0 0 0 8 10c-2.29 0-3.516.68-4.168 1.332-.678.678-.83 1.418-.832 1.664zM9 13a1 1 0 0 1 1-1v-1a2 2 0 1 1 4 0v1a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1h-4a1 1 0 0 1-1-1zm3-3a1 1 0 0 0-1 1v1h2v-1a1 1 0 0 0-1-1"/>\n</svg>';
98
- const __vite_glob_0_4 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
99
- __proto__: null,
100
- default: personLock
101
- }, Symbol.toStringTag, { value: "Module" }));
102
- const search = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">\n <path d="M11.742 10.344a6.5 6.5 0 1 0-1.397 1.398h-.001q.044.06.098.115l3.85 3.85a1 1 0 0 0 1.415-1.414l-3.85-3.85a1 1 0 0 0-.115-.1zM12 6.5a5.5 5.5 0 1 1-11 0 5.5 5.5 0 0 1 11 0"/>\n</svg>';
103
- const __vite_glob_0_5 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
104
- __proto__: null,
105
- default: search
106
- }, Symbol.toStringTag, { value: "Module" }));
107
- const stopwatch = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">\n <path d="M8.5 5.6a.5.5 0 1 0-1 0v2.9h-3a.5.5 0 0 0 0 1H8a.5.5 0 0 0 .5-.5z"/>\n <path d="M6.5 1A.5.5 0 0 1 7 .5h2a.5.5 0 0 1 0 1v.57c1.36.196 2.594.78 3.584 1.64l.012-.013.354-.354-.354-.353a.5.5 0 0 1 .707-.708l1.414 1.415a.5.5 0 1 1-.707.707l-.353-.354-.354.354-.013.012A7 7 0 1 1 7 2.071V1.5a.5.5 0 0 1-.5-.5M8 3a6 6 0 1 0 .001 12A6 6 0 0 0 8 3"/>\n</svg>';
108
- const __vite_glob_0_6 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
109
- __proto__: null,
110
- default: stopwatch
111
- }, Symbol.toStringTag, { value: "Module" }));
112
- const wifiOff = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">\n <path d="M10.706 3.294A12.6 12.6 0 0 0 8 3C5.259 3 2.723 3.882.663 5.379a.485.485 0 0 0-.048.736.52.52 0 0 0 .668.05A11.45 11.45 0 0 1 8 4q.946 0 1.852.148zM8 6c-1.905 0-3.68.56-5.166 1.526a.48.48 0 0 0-.063.745.525.525 0 0 0 .652.065 8.45 8.45 0 0 1 3.51-1.27zm2.596 1.404.785-.785q.947.362 1.785.907a.482.482 0 0 1 .063.745.525.525 0 0 1-.652.065 8.5 8.5 0 0 0-1.98-.932zM8 10l.933-.933a6.5 6.5 0 0 1 2.013.637c.285.145.326.524.1.75l-.015.015a.53.53 0 0 1-.611.09A5.5 5.5 0 0 0 8 10m4.905-4.905.747-.747q.886.451 1.685 1.03a.485.485 0 0 1 .047.737.52.52 0 0 1-.668.05 11.5 11.5 0 0 0-1.811-1.07M9.02 11.78c.238.14.236.464.04.66l-.707.706a.5.5 0 0 1-.707 0l-.707-.707c-.195-.195-.197-.518.04-.66A2 2 0 0 1 8 11.5c.374 0 .723.102 1.021.28zm4.355-9.905a.53.53 0 0 1 .75.75l-10.75 10.75a.53.53 0 0 1-.75-.75z"/>\n</svg>';
113
- const __vite_glob_0_7 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
114
- __proto__: null,
115
- default: wifiOff
116
- }, Symbol.toStringTag, { value: "Module" }));
117
- const wrenchAdjustable = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">\n <path d="M16 4.5a4.5 4.5 0 0 1-1.703 3.526L13 5l2.959-1.11q.04.3.041.61"/>\n <path d="M11.5 9c.653 0 1.273-.139 1.833-.39L12 5.5 11 3l3.826-1.53A4.5 4.5 0 0 0 7.29 6.092l-6.116 5.096a2.583 2.583 0 1 0 3.638 3.638L9.908 8.71A4.5 4.5 0 0 0 11.5 9m-1.292-4.361-.596.893.809-.27a.25.25 0 0 1 .287.377l-.596.893.809-.27.158.475-1.5.5a.25.25 0 0 1-.287-.376l.596-.893-.809.27a.25.25 0 0 1-.287-.377l.596-.893-.809.27-.158-.475 1.5-.5a.25.25 0 0 1 .287.376M3 14a1 1 0 1 1 0-2 1 1 0 0 1 0 2"/>\n</svg>';
118
- const __vite_glob_0_8 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
119
- __proto__: null,
120
- default: wrenchAdjustable
121
- }, Symbol.toStringTag, { value: "Module" }));
122
76
  var __defProp = Object.defineProperty;
123
77
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
124
78
  var __decorateClass = (decorators, target, key, kind) => {
@@ -129,27 +83,12 @@ var __decorateClass = (decorators, target, key, kind) => {
129
83
  if (kind && result) __defProp(target, key, result);
130
84
  return result;
131
85
  };
132
- const icons = Object.entries(/* @__PURE__ */ Object.assign({
133
- "../assets/ban.svg": __vite_glob_0_0,
134
- "../assets/box-seam.svg": __vite_glob_0_1,
135
- "../assets/exclamation-triangle.svg": __vite_glob_0_2,
136
- "../assets/palette.svg": __vite_glob_0_3,
137
- "../assets/person-lock.svg": __vite_glob_0_4,
138
- "../assets/search.svg": __vite_glob_0_5,
139
- "../assets/stopwatch.svg": __vite_glob_0_6,
140
- "../assets/wifi-off.svg": __vite_glob_0_7,
141
- "../assets/wrench-adjustable.svg": __vite_glob_0_8
142
- })).reduce((acc, [path, content]) => {
143
- const name = path.split("/").pop()?.replace(".svg", "") || "";
144
- acc[name] = content.default;
145
- return acc;
146
- }, {});
147
86
  let UErrorPage = class extends LitElement {
148
87
  render() {
149
88
  const error = this.error || this.getDefaultError();
150
89
  const icon = this.getErrorIcon(error.code);
151
90
  return html`
152
- <div class="icon">${unsafeHTML(icon)}</div>
91
+ <div class="icon">${icon}</div>
153
92
  <div class="code">${error.code}</div>
154
93
  <div class="message">${error.message}</div>
155
94
  `;
@@ -164,55 +103,55 @@ let UErrorPage = class extends LitElement {
164
103
  const numericCode = typeof code === "string" ? parseInt(code) : code;
165
104
  switch (codeStr) {
166
105
  case "OUTLET_NOT_FOUND":
167
- return icons["box-seam"] || "๐Ÿ“ฆ";
106
+ return "๐Ÿ“ฆ";
168
107
  case "CONTENT_LOAD_FAILED":
169
- return icons["wifi-off"] || "๐Ÿ“ก";
108
+ return "๐Ÿ“ก";
170
109
  case "RENDER_FAILED":
171
- return icons["palette"] || "๐ŸŽจ";
110
+ return "๐ŸŽจ";
172
111
  }
173
112
  switch (numericCode) {
174
113
  case 404:
175
- return icons["search"] || "๐Ÿ”";
114
+ return "๐Ÿ”";
176
115
  case 403:
177
- return icons["ban"] || "๐Ÿšซ";
116
+ return "๐Ÿšซ";
178
117
  case 401:
179
- return icons["person-lock"] || "๐Ÿ”";
118
+ return "๐Ÿ”";
180
119
  case 429:
181
- return icons["stopwatch"] || "โฑ๏ธ";
120
+ return "โฑ๏ธ";
182
121
  case 503:
183
- return icons["wrench-adjustable"] || "๐Ÿ› ๏ธ";
122
+ return "๐Ÿ› ๏ธ";
184
123
  default:
185
- return icons["exclamation-triangle"] || "โš ๏ธ";
124
+ return "โš ๏ธ";
186
125
  }
187
126
  }
188
127
  };
189
128
  UErrorPage.styles = css`
190
129
  :host {
191
- --route-icon-color: #4a5568;
192
- --route-code-color: #1a202c;
193
- --route-message-color: #718096;
130
+ --error-icon-color: #4a5568;
131
+ --error-code-color: #1a202c;
132
+ --error-message-color: #718096;
194
133
  }
195
134
  :host-context([theme="dark"]) {
196
- --route-icon-color: #a0aec0;
197
- --route-code-color: #f7fafc;
198
- --route-message-color: #cbd5e0;
135
+ --error-icon-color: #a0aec0;
136
+ --error-code-color: #f7fafc;
137
+ --error-message-color: #cbd5e0;
199
138
  }
200
139
 
201
140
  @media (prefers-color-scheme: dark) {
202
141
  :host {
203
- --route-icon-color: #a0aec0;
204
- --route-code-color: #f7fafc;
205
- --route-message-color: #cbd5e0;
142
+ --error-icon-color: #a0aec0;
143
+ --error-code-color: #f7fafc;
144
+ --error-message-color: #cbd5e0;
206
145
  }
207
- };
146
+ }
208
147
 
209
148
  :host {
210
149
  display: flex;
211
150
  flex-direction: column;
212
151
  justify-content: center;
213
152
  align-items: center;
214
- min-height: 100vh;
215
153
  width: 100%;
154
+ height: 100%;
216
155
  text-align: center;
217
156
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
218
157
  overflow: auto;
@@ -220,31 +159,24 @@ UErrorPage.styles = css`
220
159
  }
221
160
 
222
161
  .icon {
223
- display: contents;
162
+ color: var(--error-icon-color);
224
163
  font-size: 6rem;
225
- color: var(--route-icon-color);
226
164
  opacity: 0.85;
227
165
  }
228
166
 
229
- svg {
230
- width: 1em;
231
- height: 1em;
232
- fill: currentColor;
233
- }
234
-
235
167
  .code {
168
+ color: var(--error-code-color);
236
169
  font-size: 2rem;
237
170
  font-weight: 700;
238
- margin: 1rem 0;
239
- color: var(--route-code-color);
240
171
  letter-spacing: -0.5px;
172
+ margin: 1rem 0;
241
173
  }
242
174
 
243
175
  .message {
176
+ color: var(--error-message-color);
244
177
  font-size: 1rem;
245
- color: var(--route-message-color);
246
- max-width: 600px;
247
178
  line-height: 1.6;
179
+ max-width: 600px;
248
180
  }
249
181
  `;
250
182
  __decorateClass([
@@ -257,6 +189,7 @@ function getRandomID() {
257
189
  return window.isSecureContext ? window.crypto.randomUUID() : window.crypto.getRandomValues(new Uint32Array(1))[0].toString(16);
258
190
  }
259
191
  function findOutlet(element) {
192
+ if (!element) return void 0;
260
193
  let outlet = void 0;
261
194
  if (element.shadowRoot) {
262
195
  outlet = element.shadowRoot.querySelector("u-outlet");
@@ -282,18 +215,26 @@ function findOutletOrThrow(element) {
282
215
  }
283
216
  return outlet;
284
217
  }
285
- function findAnchorFromEvent(e) {
286
- const targets = e.composedPath() || [];
218
+ async function waitOutlet(element, timeout = 1e4) {
219
+ const start = performance.now();
220
+ while (performance.now() - start < timeout) {
221
+ const outlet = findOutlet(element);
222
+ if (outlet) return outlet;
223
+ await new Promise((r) => setTimeout(r, 50));
224
+ }
225
+ throw new Error("Timed out waiting for u-outlet.");
226
+ }
227
+ function findAnchorFrom(event) {
228
+ const targets = event.composedPath() || [];
287
229
  if (targets && targets.length) {
288
230
  for (const node of targets) {
289
231
  if (!(node instanceof Element)) continue;
290
232
  if (node.tagName === "A") return node;
291
233
  }
292
234
  }
293
- const tgt = e.target;
235
+ const tgt = event.target;
294
236
  if (!tgt) return null;
295
- const anchor = tgt.closest("a");
296
- return anchor;
237
+ return tgt.closest("a");
297
238
  }
298
239
  function setRoutes(routes, basepath) {
299
240
  for (const route of routes) {
@@ -327,10 +268,10 @@ function setRoutes(routes, basepath) {
327
268
  }
328
269
  return routes;
329
270
  }
330
- function getRoutes(pathname, routes) {
271
+ function getRoutes(routes, pathname) {
331
272
  for (const route of routes) {
332
273
  if (route.index !== true && route.children && route.children.length > 0) {
333
- const childRoutes = getRoutes(pathname, route.children);
274
+ const childRoutes = getRoutes(route.children, pathname);
334
275
  if (childRoutes.length > 0) {
335
276
  return [route, ...childRoutes];
336
277
  }
@@ -346,15 +287,15 @@ function getRoutes(pathname, routes) {
346
287
  }
347
288
  class Router {
348
289
  constructor(config) {
349
- this.handleWindowPopstate = async () => {
290
+ this.handleWindowPopstate = async (_) => {
350
291
  const href = window.location.href;
351
292
  await this.go(href);
352
293
  };
353
- this.handleRootClick = (e) => {
294
+ this.handleDocumentClick = async (e) => {
354
295
  try {
355
296
  if (e.defaultPrevented) return;
356
297
  if (e.button !== 0 || e.metaKey || e.ctrlKey || e.shiftKey) return;
357
- const anchor = findAnchorFromEvent(e);
298
+ const anchor = findAnchorFrom(e);
358
299
  if (!anchor) return;
359
300
  const href = anchor.getAttribute("href") || anchor.href;
360
301
  if (!href) return;
@@ -363,28 +304,29 @@ class Router {
363
304
  if (anchor.getAttribute("rel") === "external") return;
364
305
  if (anchor.target && anchor.target !== "") return;
365
306
  e.preventDefault();
366
- void this.go(anchor.href);
307
+ await this.go(anchor.href);
367
308
  } catch {
368
309
  }
369
310
  };
311
+ this.destroy();
370
312
  this._rootElement = config.root;
371
313
  this._basepath = absolutePath(config.basepath || "/");
372
314
  this._routes = setRoutes(config.routes || [], this._basepath);
373
315
  this._fallback = config.fallback;
374
- window.removeEventListener("popstate", this.handleWindowPopstate);
375
316
  window.addEventListener("popstate", this.handleWindowPopstate);
376
317
  if (config.useIntercept !== false) {
377
- this._rootElement.removeEventListener("click", this.handleRootClick);
378
- this._rootElement.addEventListener("click", this.handleRootClick);
318
+ document.addEventListener("click", this.handleDocumentClick);
379
319
  }
380
320
  if (config.initialLoad !== false) {
381
- void this.go(window.location.href);
321
+ void waitOutlet(this._rootElement).then(() => {
322
+ this.go(window.location.href);
323
+ });
382
324
  }
383
325
  }
384
326
  /** ๊ฐ์ฒด๋ฅผ ์ •๋ฆฌํ•˜๊ณ  ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ๋ฅผ ์ œ๊ฑฐํ•ฉ๋‹ˆ๋‹ค. */
385
327
  destroy() {
386
328
  window.removeEventListener("popstate", this.handleWindowPopstate);
387
- this._rootElement.removeEventListener("click", this.handleRootClick);
329
+ document.removeEventListener("click", this.handleDocumentClick);
388
330
  this._requestID = void 0;
389
331
  this._context = void 0;
390
332
  }
@@ -423,7 +365,7 @@ class Router {
423
365
  try {
424
366
  if (this._requestID !== requestID) return;
425
367
  window.dispatchEvent(new RouteBeginEvent(context));
426
- const routes = getRoutes(context.pathname, this._routes);
368
+ const routes = getRoutes(this._routes, context.pathname);
427
369
  const lastRoute = routes[routes.length - 1];
428
370
  if (lastRoute && lastRoute.path instanceof URLPattern) {
429
371
  context.params = lastRoute.path.exec({ pathname: context.pathname })?.pathname.groups || {};
@@ -432,7 +374,6 @@ class Router {
432
374
  outlet = findOutletOrThrow(this._rootElement);
433
375
  let title = void 0;
434
376
  let content = null;
435
- let element = null;
436
377
  if (routes.length === 0) {
437
378
  throw new NotFoundError(context.href);
438
379
  }
@@ -448,11 +389,11 @@ class Router {
448
389
  throw new ContentLoadError(LoadError);
449
390
  }
450
391
  try {
451
- element = await outlet.renderContent({ id: route.id, content, force: route.force });
392
+ outlet.render({ id: route.id, value: content, force: route.force });
452
393
  } catch (renderError) {
453
394
  throw new ContentRenderError(renderError);
454
395
  }
455
- outlet = findOutlet(element) || outlet;
396
+ outlet = findOutlet(outlet) || outlet;
456
397
  title = route.title || title;
457
398
  }
458
399
  document.title = title || document.title;
@@ -468,13 +409,13 @@ class Router {
468
409
  try {
469
410
  if (this._fallback && this._fallback.render && outlet) {
470
411
  const fallbackContent = await this._fallback.render({ ...context, error: routeError });
471
- outlet.renderContent({ id: "#fallback", content: fallbackContent, force: true });
412
+ outlet.render({ id: "#fallback", value: fallbackContent, force: true });
472
413
  document.title = this._fallback.title || document.title;
473
414
  } else {
474
415
  const errorContent = new UErrorPage();
475
416
  errorContent.error = error;
476
417
  if (outlet) {
477
- outlet.renderContent({ id: "#error", content: errorContent, force: true });
418
+ outlet.render({ id: "#error", value: errorContent, force: true });
478
419
  } else {
479
420
  document.body.innerHTML = "";
480
421
  document.body.appendChild(errorContent);
package/dist/react.d.ts CHANGED
@@ -6,10 +6,14 @@ import { ReactWebComponent } from '@lit/react';
6
6
  import { TemplateResult } from 'lit-html';
7
7
  import { TemplateResult as TemplateResult_2 } from 'lit';
8
8
 
9
+ /** ๋ Œ๋”๋ง ์˜ต์…˜ */
9
10
  declare interface RenderOption {
11
+ /** ๊ต์ฐจ ๋ Œ๋”๋ง ๋ฐฉ์ง€ ID */
10
12
  id?: string;
13
+ /** ๊ฐ•์ œ ๋ Œ๋”๋ง ์—ฌ๋ถ€ */
11
14
  force?: boolean;
12
- content: RenderResult;
15
+ /** ๋ Œ๋”๋งํ•  ๊ฐ’ */
16
+ value: RenderResult;
13
17
  }
14
18
 
15
19
  declare type RenderResult = HTMLElement | ReactElement | TemplateResult_2<1> | false;
@@ -27,36 +31,42 @@ export declare const ULink: ReactWebComponent<ULink_2, {}>;
27
31
  declare class ULink_2 extends LitElement {
28
32
  /** ์™ธ๋ถ€ ๋งํฌ ์—ฌ๋ถ€ */
29
33
  private isExternal;
30
- /** a ํƒœ๊ทธ์— ์ฃผ์ž…ํ•  href ๊ฐ’ */
31
- computedHref: string;
32
34
  /**
33
- * a ํƒœ๊ทธ target์„ ์ง€์›ํ•˜๊ณ  ์‹ถ์œผ๋ฉด ์—ด์–ด๋‘๋Š”๊ฒŒ ์ข‹์Šต๋‹ˆ๋‹ค.
34
- * - _blank ๋“ฑ์„ ์“ฐ๋ฉด ๋ฌด์กฐ๊ฑด ๋ธŒ๋ผ์šฐ์ € ๊ธฐ๋ณธ ๋™์ž‘์„ ๋”ฐ๋ฅด๋„๋ก ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.
35
+ * ๋งํฌ ๋Œ€์ƒ target ์†์„ฑ
36
+ *
37
+ * - `_self`: ํ˜„์žฌ ์ฐฝ์—์„œ ๋งํฌ ์—ด๊ธฐ (๊ธฐ๋ณธ๊ฐ’)
38
+ * - `_blank`: ์ƒˆ ํƒญ/์ฐฝ์—์„œ ๋งํฌ ์—ด๊ธฐ
39
+ * - `_parent`: ๋ถ€๋ชจ ํ”„๋ ˆ์ž„์—์„œ ๋งํฌ ์—ด๊ธฐ
40
+ * - `_top`: ์ตœ์ƒ์œ„ ํ”„๋ ˆ์ž„์—์„œ ๋งํฌ ์—ด๊ธฐ
35
41
  */
36
42
  target?: string;
37
43
  /**
38
- * - ์†์„ฑ์„ ์ •์˜ํ•˜์ง€ ์•Š์œผ๋ฉด basepath๋กœ ์ด๋™ํ•ฉ๋‹ˆ๋‹ค.
44
+ * ๋งํฌ ๋Œ€์ƒ URL, ๋‹ค์Œ ์‚ฌํ•ญ์— ๋”ฐ๋ผ SPA ๋ผ์šฐํŒ… ๋˜๋Š” ๋ธŒ๋ผ์šฐ์ € ๋„ค๋น„๊ฒŒ์ด์…˜์ด ๊ฒฐ์ •๋ฉ๋‹ˆ๋‹ค.
45
+ *
46
+ * - ์†์„ฑ์„ ์ •์˜ํ•˜์ง€ ์•Š์œผ๋ฉด ์„ค์ •์—์„œ ์ง€์ •ํ•œ `basepath`๋กœ SPA ๋ผ์šฐํŒ…ํ•ฉ๋‹ˆ๋‹ค.
39
47
  * - http(s)๋กœ ์‹œ์ž‘ํ•˜๋ฉด ์™ธ๋ถ€ ๋งํฌ๋กœ ๊ฐ„์ฃผํ•˜๊ณ  ๋ธŒ๋ผ์šฐ์ € ๋„ค๋น„๊ฒŒ์ด์…˜์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
40
- * - ์ ˆ๋Œ€๊ฒฝ๋กœ(/...)์ผ ๊ฒฝ์šฐ basepath๋กœ ์‹œ์ž‘ํ•˜๋ฉด SPA ๋ผ์šฐํŒ… ๋Œ€์ƒ์ด ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
41
- * - ์ƒ๋Œ€๊ฒฝ๋กœ๋Š” (basepath + ์ƒ๋Œ€๊ฒฝ๋กœ)๋กœ SPA ๋ผ์šฐํŒ… ๋Œ€์ƒ์ด ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
42
- * - ?๋กœ ์‹œ์ž‘ํ•˜๋ฉด ํ˜„์žฌ pathname์— ์ฟผ๋ฆฌ์ŠคํŠธ๋ง์„ ๋ถ™์—ฌ SPA ๋ผ์šฐํŒ…ํ•ฉ๋‹ˆ๋‹ค.
43
- * - #์œผ๋กœ ์‹œ์ž‘ํ•˜๋ฉด ํ˜„์žฌ URL์— ํ•ด์‹œ๋งŒ ๋ฐ”๊พธ๊ณ (๋ธŒ๋ผ์šฐ์ € ๊ธฐ๋ณธ) ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค.
48
+ * - ์ ˆ๋Œ€๊ฒฝ๋กœ(/...)์˜ ๊ฒฝ์šฐ `basepath`๋กœ ์‹œ์ž‘ํ•˜๋ฉด SPA ๋ผ์šฐํŒ…ํ•ฉ๋‹ˆ๋‹ค, ์ด์™ธ ๋ธŒ๋ผ์šฐ์ € ๋„ค๋น„๊ฒŒ์ด์…˜์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
49
+ * - ์ƒ๋Œ€๊ฒฝ๋กœ๋Š” (basepath + ์ƒ๋Œ€๊ฒฝ๋กœ)๋กœ ๊ฒฐํ•ฉํ•˜์—ฌ SPA ๋ผ์šฐํŒ…ํ•ฉ๋‹ˆ๋‹ค.
50
+ * - ?๋กœ ์‹œ์ž‘ํ•˜๋ฉด ํ˜„์žฌ ๊ฒฝ๋กœ์— ์ฟผ๋ฆฌ์ŠคํŠธ๋ง์„ ์ถ”๊ฐ€ํ•˜์—ฌ SPA ๋ผ์šฐํŒ…ํ•ฉ๋‹ˆ๋‹ค.
51
+ * - #์œผ๋กœ ์‹œ์ž‘ํ•˜๋ฉด ๋ธŒ๋ผ์šฐ์ € ๊ธฐ๋ณธ ๋™์ž‘์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
44
52
  */
45
53
  href?: string;
54
+ connectedCallback(): void;
55
+ disconnectedCallback(): void;
46
56
  protected willUpdate(changedProperties: PropertyValues): void;
47
57
  render(): TemplateResult<1>;
48
- /** basepath๋ฅผ state์—์„œ ๊บผ๋‚ด๋Š” ํ—ฌํผ */
49
- private getBasepath;
50
58
  /** a ํƒœ๊ทธ์— ์ฃผ์ž…ํ•  href ๊ฐ’์„ ๊ณ„์‚ฐํ•ฉ๋‹ˆ๋‹ค. */
51
- private computeHref;
59
+ private compute;
52
60
  /**
53
- * ํด๋ฆญ ๊ฐ€๋กœ์ฑ„๊ธฐ ๊ทœ์น™
61
+ * ํด๋ฆญ ๊ฐ€๋กœ์ฑ„๊ธฐ ํ•ธ๋“ค๋Ÿฌ
54
62
  * - ์ขŒํด๋ฆญ(0) + ๋ณด์กฐํ‚ค ์—†์Œ(ctrl/meta/shift/alt ์—†์Œ) + target์ด _self์ผ ๋•Œ๋งŒ SPA ๋ผ์šฐํŒ… ๊ณ ๋ ค
55
63
  * - ๊ทธ ์™ธ(์ค‘ํด๋ฆญ/์šฐํด๋ฆญ/๋ณด์กฐํ‚ค/target=_blank ๋“ฑ)๋Š” ๋ธŒ๋ผ์šฐ์ € ๊ธฐ๋ณธ ๋™์ž‘ ์œ ์ง€
56
64
  */
57
- private handleAnchorClick;
65
+ private handleClick;
58
66
  /** ํด๋ผ์ด์–ธํŠธ ๋ผ์šฐํŒ…์„ ์œ„ํ•ด popstate ์ด๋ฒคํŠธ๋ฅผ ๋ฐœ์ƒ์‹œํ‚ต๋‹ˆ๋‹ค. */
59
67
  private dispatchPopstate;
68
+ /** basepath๋ฅผ state์—์„œ ๊บผ๋‚ด๋Š” ํ—ฌํผ */
69
+ private getBasepath;
60
70
  static styles: CSSResult;
61
71
  }
62
72
 
@@ -66,24 +76,21 @@ declare class ULink_2 extends LitElement {
66
76
  export declare const UOutlet: ReactWebComponent<UOutlet_2, {}>;
67
77
 
68
78
  /**
69
- * ๋ผ์šฐํŠธ ์„ค์ •์— ๋”ฐ๋ผ LitElement ๋˜๋Š” React ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ Œ๋”๋งํ•ฉ๋‹ˆ๋‹ค.
79
+ * LitElement ๋˜๋Š” React ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ Œ๋”๋งํ•ด์ฃผ๋Š” ์›น์ปดํฌ๋„ŒํŠธ ์ž…๋‹ˆ๋‹ค.
70
80
  */
71
- declare class UOutlet_2 extends LitElement {
81
+ declare class UOutlet_2 extends HTMLElement {
82
+ /** ๊ต์ฐจ ๋ Œ๋”๋ง ๋ฐฉ์ง€ id */
72
83
  private routeId?;
73
- private container?;
74
- private content?;
75
- /** ์™ธ๋ถ€ ์Šคํƒ€์ผ์„ ์ ์šฉํ•˜๊ธฐ ์œ„ํ•ด ๋ผ์ดํŠธ ๋”์„์‚ฌ์šฉ ํ•ฉ๋‹ˆ๋‹ค. */
76
- protected createRenderRoot(): this;
77
- render(): TemplateResult_2<1>;
84
+ /** ์‹ค์ œ ๋ Œ๋”๋ง ์ปจํ…์ธ  */
85
+ private root?;
78
86
  /**
79
- * render ํ•จ์ˆ˜์˜ ๊ฒฐ๊ณผ๋ฅผ ๋ Œ๋”๋งํ•ฉ๋‹ˆ๋‹ค.
80
- * - HTMLElement, ReactElement, TemplateResult๋ฅผ ๋ชจ๋‘ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
87
+ * ์ฃผ์–ด์ง„ ๋ Œ๋”๋ง ์˜ต์…˜์— ๋”ฐ๋ผ ์ปจํ…์ธ ๋ฅผ ๋ Œ๋”๋งํ•ฉ๋‹ˆ๋‹ค.
81
88
  */
82
- renderContent({ id, content, force }: RenderOption): Promise<HTMLDivElement>;
89
+ render({ id, value, force }: RenderOption): void;
83
90
  /**
84
- * ๊ธฐ์กด DOM์„ ๋ผ์ดํ”„ ์‚ฌ์ดํด์— ๋งž๊ฒŒ ์ œ๊ฑฐํ•ฉ๋‹ˆ๋‹ค.
91
+ * ๊ธฐ์กด DOM์„ ์‚ญ์ œํ•˜์—ฌ, ์ดˆ๊ธฐ ์ƒํƒœ๋กœ ๋˜๋Œ๋ฆฝ๋‹ˆ๋‹ค.
85
92
  */
86
- clear(): void;
93
+ reset(): void;
87
94
  }
88
95
 
89
96
  export { }
package/dist/react.js CHANGED
@@ -1,38 +1,13 @@
1
1
  import React from "react";
2
- import { b as ULink$1, U as UOutlet$1 } from "./share-ZrQFmsur.js";
3
- const e = /* @__PURE__ */ new Set(["children", "localName", "ref", "style", "className"]), n = /* @__PURE__ */ new WeakMap(), t = (e2, t2, o2, l, a) => {
4
- const s = a?.[t2];
5
- void 0 === s ? (e2[t2] = o2, null == o2 && t2 in HTMLElement.prototype && e2.removeAttribute(t2)) : o2 !== l && ((e3, t3, o3) => {
6
- let l2 = n.get(e3);
7
- void 0 === l2 && n.set(e3, l2 = /* @__PURE__ */ new Map());
8
- let a2 = l2.get(t3);
9
- void 0 !== o3 ? void 0 === a2 ? (l2.set(t3, a2 = { handleEvent: o3 }), e3.addEventListener(t3, a2)) : a2.handleEvent = o3 : void 0 !== a2 && (l2.delete(t3), e3.removeEventListener(t3, a2));
10
- })(e2, s, o2);
11
- }, o = ({ react: n2, tagName: o2, elementClass: l, events: a, displayName: s }) => {
12
- const c = new Set(Object.keys(a ?? {})), r = n2.forwardRef(((s2, r2) => {
13
- const i = n2.useRef(/* @__PURE__ */ new Map()), d = n2.useRef(null), f = {}, u = {};
14
- for (const [n3, t2] of Object.entries(s2)) e.has(n3) ? f["className" === n3 ? "class" : n3] = t2 : c.has(n3) || n3 in l.prototype ? u[n3] = t2 : f[n3] = t2;
15
- return n2.useLayoutEffect((() => {
16
- if (null === d.current) return;
17
- const e2 = /* @__PURE__ */ new Map();
18
- for (const n3 in u) t(d.current, n3, s2[n3], i.current.get(n3), a), i.current.delete(n3), e2.set(n3, s2[n3]);
19
- for (const [e3, n3] of i.current) t(d.current, e3, void 0, n3, a);
20
- i.current = e2;
21
- })), n2.useLayoutEffect((() => {
22
- d.current?.removeAttribute("defer-hydration");
23
- }), []), f.suppressHydrationWarning = true, n2.createElement(o2, { ...f, ref: n2.useCallback(((e2) => {
24
- d.current = e2, "function" == typeof r2 ? r2(e2) : null !== r2 && (r2.current = e2);
25
- }), [r2]) });
26
- }));
27
- return r.displayName = s ?? l.name, r;
28
- };
29
- const ULink = o({
2
+ import { createComponent } from "@lit/react";
3
+ import { b as ULink$1, U as UOutlet$1 } from "./share-6Zs4TunA.js";
4
+ const ULink = createComponent({
30
5
  react: React,
31
6
  tagName: "u-link",
32
7
  elementClass: ULink$1,
33
8
  events: {}
34
9
  });
35
- const UOutlet = o({
10
+ const UOutlet = createComponent({
36
11
  react: React,
37
12
  tagName: "u-outlet",
38
13
  elementClass: UOutlet$1,
@@ -1,81 +1,54 @@
1
- import { LitElement, html, render, css } from "lit";
2
- import { customElement, state, property } from "lit/decorators.js";
1
+ import { render, css, LitElement, html } from "lit";
3
2
  import { createRoot } from "react-dom/client";
4
- var __getOwnPropDesc$1 = Object.getOwnPropertyDescriptor;
5
- var __decorateClass$1 = (decorators, target, key, kind) => {
6
- var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$1(target, key) : target;
7
- for (var i = decorators.length - 1, decorator; i >= 0; i--)
8
- if (decorator = decorators[i])
9
- result = decorator(result) || result;
10
- return result;
11
- };
12
- let UOutlet = class extends LitElement {
13
- /** ์™ธ๋ถ€ ์Šคํƒ€์ผ์„ ์ ์šฉํ•˜๊ธฐ ์œ„ํ•ด ๋ผ์ดํŠธ ๋”์„์‚ฌ์šฉ ํ•ฉ๋‹ˆ๋‹ค. */
14
- createRenderRoot() {
15
- return this;
16
- }
17
- render() {
18
- return html`${this.container}`;
19
- }
3
+ import { property, customElement } from "lit/decorators.js";
4
+ import { ifDefined } from "lit/directives/if-defined.js";
5
+ class UOutlet extends HTMLElement {
20
6
  /**
21
- * render ํ•จ์ˆ˜์˜ ๊ฒฐ๊ณผ๋ฅผ ๋ Œ๋”๋งํ•ฉ๋‹ˆ๋‹ค.
22
- * - HTMLElement, ReactElement, TemplateResult๋ฅผ ๋ชจ๋‘ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
7
+ * ์ฃผ์–ด์ง„ ๋ Œ๋”๋ง ์˜ต์…˜์— ๋”ฐ๋ผ ์ปจํ…์ธ ๋ฅผ ๋ Œ๋”๋งํ•ฉ๋‹ˆ๋‹ค.
23
8
  */
24
- async renderContent({ id, content, force }) {
25
- if (this.routeId === id && force === false && this.container) {
26
- return this.container;
27
- }
9
+ render({ id, value, force }) {
10
+ if (this.routeId === id && force === false) return;
28
11
  this.routeId = id;
29
- this.clear();
30
- if (!this.container) {
31
- throw new Error("Outlet container is not initialized.");
32
- }
33
- if (typeof content !== "object") {
12
+ this.reset();
13
+ if (typeof value !== "object") {
34
14
  throw new Error("Content is not a valid renderable object.");
35
15
  }
36
- if (content instanceof HTMLElement) {
37
- this.container.appendChild(content);
38
- } else if ("_$litType$" in content) {
39
- this.content = render(content, this.container);
40
- } else if ("$$typeof" in content) {
41
- this.content = createRoot(this.container);
42
- this.content.render(content);
16
+ if (value instanceof HTMLElement) {
17
+ this.replaceChildren(value);
18
+ this.root = void 0;
19
+ } else if ("_$litType$" in value) {
20
+ this.root = render(value, this);
21
+ } else if ("$$typeof" in value) {
22
+ this.root = createRoot(this);
23
+ this.root.render(value);
43
24
  } else {
44
25
  throw new Error("not supported content type for Outlet rendering.");
45
26
  }
46
- this.requestUpdate();
47
- await this.updateComplete;
48
- return this.container;
49
27
  }
50
28
  /**
51
- * ๊ธฐ์กด DOM์„ ๋ผ์ดํ”„ ์‚ฌ์ดํด์— ๋งž๊ฒŒ ์ œ๊ฑฐํ•ฉ๋‹ˆ๋‹ค.
29
+ * ๊ธฐ์กด DOM์„ ์‚ญ์ œํ•˜์—ฌ, ์ดˆ๊ธฐ ์ƒํƒœ๋กœ ๋˜๋Œ๋ฆฝ๋‹ˆ๋‹ค.
52
30
  */
53
- clear() {
54
- if (this.content) {
55
- if ("unmount" in this.content) {
56
- this.content.unmount();
57
- }
58
- if ("setConnected" in this.content) {
59
- this.content.setConnected(false);
60
- }
61
- this.content = void 0;
31
+ reset() {
32
+ if (this.root && "_$litPart$" in this) {
33
+ delete this._$litPart$;
62
34
  }
63
- this.container = document.createElement("div");
64
- this.container.style.display = "contents";
35
+ if (this.root && "unmount" in this.root) {
36
+ this.root.unmount();
37
+ }
38
+ this.root = void 0;
39
+ this.innerHTML = "";
65
40
  }
66
- };
67
- UOutlet = __decorateClass$1([
68
- customElement("u-outlet")
69
- ], UOutlet);
41
+ }
42
+ customElements.define("u-outlet", UOutlet);
70
43
  function isExternalUrl(url) {
71
44
  if (!url) return false;
72
- const s = url.trim();
73
- if (/^(?:mailto:|tel:|javascript:)/i.test(s)) return true;
74
- if (s.startsWith("//")) return true;
45
+ url = url.trim();
46
+ if (/^(?:mailto:|tel:|javascript:)/i.test(url)) return true;
47
+ if (url.startsWith("//")) return true;
75
48
  try {
76
49
  const base = typeof window !== "undefined" ? window.location.origin : "http://localhost";
77
- const parsed = new URL(s, base);
78
- if (/^(?:ftp:|ftps:|data:|ws:|wss:)/i.test(parsed.protocol)) return true;
50
+ const parsed = new URL(url, base);
51
+ if (/^(?:ftp:|ftps:|ws:|wss:)/i.test(parsed.protocol)) return true;
79
52
  return parsed.origin !== new URL(base).origin;
80
53
  } catch {
81
54
  return false;
@@ -83,7 +56,7 @@ function isExternalUrl(url) {
83
56
  }
84
57
  function parseUrl(url, basepath) {
85
58
  let urlObj;
86
- basepath = catchBasePath(basepath);
59
+ basepath = catchBasepath(basepath);
87
60
  if (url.startsWith("http")) {
88
61
  urlObj = new URL(url);
89
62
  } else if (url.startsWith("/")) {
@@ -113,7 +86,7 @@ function absolutePath(...paths) {
113
86
  if (paths.length === 0) return "/";
114
87
  return "/" + paths.join("/");
115
88
  }
116
- function catchBasePath(basepath) {
89
+ function catchBasepath(basepath) {
117
90
  if (basepath === "/") return basepath;
118
91
  let pattern = new URLPattern({ pathname: basepath + "/*" });
119
92
  let match = pattern.exec({ pathname: window.location.pathname });
@@ -143,13 +116,11 @@ let ULink = class extends LitElement {
143
116
  constructor() {
144
117
  super(...arguments);
145
118
  this.isExternal = false;
146
- this.computedHref = "#";
147
- this.handleAnchorClick = (event) => {
119
+ this.handleClick = (event) => {
148
120
  if (event.defaultPrevented) return;
149
121
  if (event.button !== 0) return;
150
122
  if (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
151
- const target = (this.target ?? "").trim();
152
- if (target && target.toLowerCase() !== "_self") return;
123
+ if (this.target && this.target.toLowerCase() !== "_self") return;
153
124
  const basepath = this.getBasepath();
154
125
  if (!this.href) {
155
126
  event.preventDefault();
@@ -176,37 +147,34 @@ let ULink = class extends LitElement {
176
147
  this.dispatchPopstate(basepath, url);
177
148
  };
178
149
  }
150
+ connectedCallback() {
151
+ super.connectedCallback();
152
+ this.addEventListener("click", this.handleClick);
153
+ }
154
+ disconnectedCallback() {
155
+ this.removeEventListener("click", this.handleClick);
156
+ super.disconnectedCallback();
157
+ }
179
158
  willUpdate(changedProperties) {
180
159
  super.willUpdate(changedProperties);
181
160
  if (changedProperties.has("href")) {
182
161
  this.isExternal = isExternalUrl(this.href || "");
183
- this.computedHref = this.computeHref(this.href);
184
162
  }
185
163
  }
186
164
  render() {
187
165
  return html`
188
- <a
189
- target=${this.target ?? "_self"}
190
- href=${this.computedHref}
191
- @click=${this.handleAnchorClick}
192
- >
166
+ <a target=${ifDefined(this.target)} href=${this.compute(this.href)}>
193
167
  <slot></slot>
194
168
  </a>
195
169
  `;
196
170
  }
197
- /** basepath๋ฅผ state์—์„œ ๊บผ๋‚ด๋Š” ํ—ฌํผ */
198
- getBasepath() {
199
- return window.history.state?.basepath || "";
200
- }
201
171
  /** a ํƒœ๊ทธ์— ์ฃผ์ž…ํ•  href ๊ฐ’์„ ๊ณ„์‚ฐํ•ฉ๋‹ˆ๋‹ค. */
202
- computeHref(href) {
172
+ compute(href) {
203
173
  const basepath = this.getBasepath();
204
- if (!href) {
205
- return window.location.origin + basepath;
206
- }
174
+ if (!href) return window.location.origin + basepath;
207
175
  if (this.isExternal) return href;
208
- if (href.startsWith("#") || href.startsWith("?")) return href;
209
176
  if (href.startsWith("/")) return href;
177
+ if (href.startsWith("#") || href.startsWith("?")) return href;
210
178
  return absolutePath(basepath, href);
211
179
  }
212
180
  /** ํด๋ผ์ด์–ธํŠธ ๋ผ์šฐํŒ…์„ ์œ„ํ•ด popstate ์ด๋ฒคํŠธ๋ฅผ ๋ฐœ์ƒ์‹œํ‚ต๋‹ˆ๋‹ค. */
@@ -214,15 +182,17 @@ let ULink = class extends LitElement {
214
182
  window.history.pushState({ basepath }, "", url);
215
183
  window.dispatchEvent(new PopStateEvent("popstate"));
216
184
  }
185
+ /** basepath๋ฅผ state์—์„œ ๊บผ๋‚ด๋Š” ํ—ฌํผ */
186
+ getBasepath() {
187
+ return window.history.state?.basepath || "";
188
+ }
217
189
  };
218
190
  ULink.styles = css`
219
191
  :host {
220
- display: inline-flex;
221
192
  cursor: pointer;
222
193
  }
223
194
 
224
195
  a {
225
- display: contents;
226
196
  text-decoration: none;
227
197
 
228
198
  font-size: inherit;
@@ -232,9 +202,6 @@ ULink.styles = css`
232
202
  cursor: inherit;
233
203
  }
234
204
  `;
235
- __decorateClass([
236
- state()
237
- ], ULink.prototype, "computedHref", 2);
238
205
  __decorateClass([
239
206
  property({ type: String })
240
207
  ], 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.2",
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,17 +36,18 @@
36
36
  }
37
37
  },
38
38
  "scripts": {
39
+ "test": "vite",
39
40
  "build": "vite build"
40
41
  },
41
42
  "dependencies": {
43
+ "@lit/react": "^1.0.8",
42
44
  "lit": "^3.3.2",
43
45
  "react": "^19.2.3",
44
46
  "react-dom": "^19.2.3"
45
47
  },
46
48
  "devDependencies": {
47
- "@lit/react": "^1.0.8",
48
- "@types/node": "^25.0.8",
49
- "@types/react": "^19.2.8",
49
+ "@types/node": "^25.0.9",
50
+ "@types/react": "^19.2.9",
50
51
  "@types/react-dom": "^19.2.3",
51
52
  "typescript": "^5.9.3",
52
53
  "vite": "^7.3.1",