@iyulab/router 0.11.1 → 0.11.3
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/CHANGELOG.md +26 -0
- package/dist/components/UErrorPage.d.ts +9 -0
- package/dist/components/ULink.d.ts +14 -0
- package/dist/index.js +8 -4
- package/dist/react.js +1 -1
- package/dist/{share-Dl02vMhW.js → share-iJLbxFbJ.js} +27 -2
- package/package.json +2 -1
- package/skills/iyulab-router/references/components.md +20 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,31 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.11.3] - 2026-08-31
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- **`waitOutlet()` could hang forever in a backgrounded tab.** The wait loop
|
|
8
|
+
only re-checked for the outlet after each `requestAnimationFrame`
|
|
9
|
+
resolved, and a fully suspended tab can stop firing `rAF` entirely (not
|
|
10
|
+
just throttle it), so the loop never exited. It now races each `rAF`
|
|
11
|
+
wait against a `setTimeout` for the remaining budget, and does one more
|
|
12
|
+
outlet check immediately before throwing — the deadline can pass in the
|
|
13
|
+
exact frame the outlet became ready, and without that final check that
|
|
14
|
+
read as a false timeout.
|
|
15
|
+
|
|
16
|
+
## [0.11.2] - 2026-08-25
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
|
|
20
|
+
- **`aria-current`/`aria-label` set on `<u-link>` never reached the accessibility tree.** The
|
|
21
|
+
host attribute was present, but the actual interactive node exposed to assistive technology
|
|
22
|
+
is the native `<a>` rendered inside the shadow root — ARIA content attributes on a shadow
|
|
23
|
+
host do not cross the shadow boundary to label or mark current a descendant. Navigation
|
|
24
|
+
links relying on `aria-current="page"` to announce the active item, or on `aria-label` for
|
|
25
|
+
their accessible name, were exposed with neither. `render()` now forwards both host
|
|
26
|
+
attributes onto the internal `<a>`, and changes made after connection are observed and
|
|
27
|
+
re-rendered.
|
|
28
|
+
|
|
3
29
|
## [0.11.1] - 2026-08-20
|
|
4
30
|
|
|
5
31
|
### Fixed
|
|
@@ -3,6 +3,15 @@ import { RouteError } from '../types/RouteError.js';
|
|
|
3
3
|
import { TemplateResult } from 'lit-html';
|
|
4
4
|
/**
|
|
5
5
|
* 라우팅 중 발생한 에러 정보를 사용자에게 전달하기 위한 기본 컴포넌트 입니다.
|
|
6
|
+
*
|
|
7
|
+
* `Router`가 `fallback`을 지정하지 않았을 때 내부적으로 그리는 기본 화면이라
|
|
8
|
+
* `src/index.ts`에서 공개 export되지 않는다 — 직접 import해 쓰는 컴포넌트가
|
|
9
|
+
* 아니다. 다만 라우팅 실패 시 실제 DOM에 렌더되므로, 기본 화면의 색만 가볍게
|
|
10
|
+
* 맞추고 싶은 소비자를 위해 세 색상 훅을 남겨 둔다(전체 교체는 `fallback.render`).
|
|
11
|
+
*
|
|
12
|
+
* @cssprop --error-icon-color - 아이콘 색. 기본값 없음(미지정 시 상속된 색 사용)
|
|
13
|
+
* @cssprop --error-code-color - 에러 코드 텍스트 색. 기본값 없음(미지정 시 상속된 색 사용)
|
|
14
|
+
* @cssprop --error-message-color - 에러 메시지 텍스트 색. 기본값 없음(미지정 시 상속된 색 사용)
|
|
6
15
|
*/
|
|
7
16
|
export declare class UErrorPage extends LitElement {
|
|
8
17
|
constructor(error?: RouteError);
|
|
@@ -61,6 +61,20 @@ export declare class ULink extends LitElement {
|
|
|
61
61
|
navigate?: "router" | "document";
|
|
62
62
|
connectedCallback(): void;
|
|
63
63
|
disconnectedCallback(): void;
|
|
64
|
+
/**
|
|
65
|
+
* 호스트에 세팅된 `aria-current`/`aria-label`은 실제 접근 가능한(포커스 대상)
|
|
66
|
+
* 엘리먼트가 아니라 — 그 안쪽 shadow DOM 의 네이티브 `<a>`다. 섀도우 경계를
|
|
67
|
+
* 넘지 않으므로 접근성 트리에 자동 반영되지 않는다(docket #45 실측 — 속성은
|
|
68
|
+
* 붙어 있는데 접근성 트리의 `aria-current`는 계속 비어 있음). `render()`가 이
|
|
69
|
+
* 값을 읽어 내부 `<a>`에 직접 옮긴다.
|
|
70
|
+
*
|
|
71
|
+
* 둘 다 Lit 리액티브 프로퍼티로 선언돼 있지 않아 `observedAttributes`에 없다 —
|
|
72
|
+
* 그 목록에 없는 속성은 `attributeChangedCallback` 자체가 호출되지 않는다
|
|
73
|
+
* (커스텀 엘리먼트 표준 동작). 초기 렌더는 되지만 연결 후 동적 변경은 반영되지
|
|
74
|
+
* 않았다 — 목록에 명시적으로 추가해야 한다.
|
|
75
|
+
*/
|
|
76
|
+
static get observedAttributes(): string[];
|
|
77
|
+
attributeChangedCallback(name: string, old: string | null, value: string | null): void;
|
|
64
78
|
protected willUpdate(changedProperties: PropertyValues): void;
|
|
65
79
|
render(): TemplateResult<1>;
|
|
66
80
|
/** a 태그에 주입할 href 값을 계산합니다. */
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as isExternalUrl, i as absolutePath, n as __decorate, o as parseUrl, r as __decorateMetadata, s as UOutlet, t as ULink } from "./share-
|
|
1
|
+
import { a as isExternalUrl, i as absolutePath, n as __decorate, o as parseUrl, r as __decorateMetadata, s as UOutlet, t as ULink } from "./share-iJLbxFbJ.js";
|
|
2
2
|
import { LitElement, css, html } from "lit";
|
|
3
3
|
import { customElement, property } from "lit/decorators.js";
|
|
4
4
|
//#region src/types/RouteError.ts
|
|
@@ -260,14 +260,18 @@ function findOutletOrThrow(element, skip = false) {
|
|
|
260
260
|
* @returns 준비된 `u-outlet` 엘리먼트
|
|
261
261
|
*/
|
|
262
262
|
async function waitOutlet(element, timeout = 1e4, skip = false) {
|
|
263
|
-
const
|
|
264
|
-
while (performance.now()
|
|
263
|
+
const deadline = performance.now() + timeout;
|
|
264
|
+
while (performance.now() < deadline) {
|
|
265
265
|
const outlet = findOutlet(element, skip);
|
|
266
266
|
if (outlet) return outlet;
|
|
267
267
|
if (element.localName.includes("-")) await customElements.whenDefined(element.localName);
|
|
268
268
|
if ("updateComplete" in element) await element.updateComplete;
|
|
269
|
-
|
|
269
|
+
const remaining = deadline - performance.now();
|
|
270
|
+
if (remaining <= 0) break;
|
|
271
|
+
await Promise.race([new Promise((resolve) => requestAnimationFrame(() => resolve())), new Promise((resolve) => setTimeout(resolve, remaining))]);
|
|
270
272
|
}
|
|
273
|
+
const outlet = findOutlet(element, skip);
|
|
274
|
+
if (outlet) return outlet;
|
|
271
275
|
throw new Error(`Timed out waiting for <u-outlet> inside <${element.tagName.toLowerCase()}>. Ensure that the router root element contains a <u-outlet> child.`);
|
|
272
276
|
}
|
|
273
277
|
/**
|
package/dist/react.js
CHANGED
|
@@ -1023,12 +1023,12 @@ function catchBasepath(basepath) {
|
|
|
1023
1023
|
return basepath;
|
|
1024
1024
|
}
|
|
1025
1025
|
//#endregion
|
|
1026
|
-
//#region \0@oxc-project+runtime@0.
|
|
1026
|
+
//#region \0@oxc-project+runtime@0.147.0/helpers/esm/decorateMetadata.js
|
|
1027
1027
|
function __decorateMetadata(k, v) {
|
|
1028
1028
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
1029
1029
|
}
|
|
1030
1030
|
//#endregion
|
|
1031
|
-
//#region \0@oxc-project+runtime@0.
|
|
1031
|
+
//#region \0@oxc-project+runtime@0.147.0/helpers/esm/decorate.js
|
|
1032
1032
|
function __decorate(decorators, target, key, desc) {
|
|
1033
1033
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
1034
1034
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
@@ -1081,6 +1081,29 @@ var ULink = class ULink extends LitElement {
|
|
|
1081
1081
|
this.removeEventListener("click", this.handleClick);
|
|
1082
1082
|
super.disconnectedCallback();
|
|
1083
1083
|
}
|
|
1084
|
+
/**
|
|
1085
|
+
* 호스트에 세팅된 `aria-current`/`aria-label`은 실제 접근 가능한(포커스 대상)
|
|
1086
|
+
* 엘리먼트가 아니라 — 그 안쪽 shadow DOM 의 네이티브 `<a>`다. 섀도우 경계를
|
|
1087
|
+
* 넘지 않으므로 접근성 트리에 자동 반영되지 않는다(docket #45 실측 — 속성은
|
|
1088
|
+
* 붙어 있는데 접근성 트리의 `aria-current`는 계속 비어 있음). `render()`가 이
|
|
1089
|
+
* 값을 읽어 내부 `<a>`에 직접 옮긴다.
|
|
1090
|
+
*
|
|
1091
|
+
* 둘 다 Lit 리액티브 프로퍼티로 선언돼 있지 않아 `observedAttributes`에 없다 —
|
|
1092
|
+
* 그 목록에 없는 속성은 `attributeChangedCallback` 자체가 호출되지 않는다
|
|
1093
|
+
* (커스텀 엘리먼트 표준 동작). 초기 렌더는 되지만 연결 후 동적 변경은 반영되지
|
|
1094
|
+
* 않았다 — 목록에 명시적으로 추가해야 한다.
|
|
1095
|
+
*/
|
|
1096
|
+
static get observedAttributes() {
|
|
1097
|
+
return [
|
|
1098
|
+
...super.observedAttributes,
|
|
1099
|
+
"aria-current",
|
|
1100
|
+
"aria-label"
|
|
1101
|
+
];
|
|
1102
|
+
}
|
|
1103
|
+
attributeChangedCallback(name, old, value) {
|
|
1104
|
+
super.attributeChangedCallback(name, old, value);
|
|
1105
|
+
if (name === "aria-current" || name === "aria-label") this.requestUpdate();
|
|
1106
|
+
}
|
|
1084
1107
|
willUpdate(changedProperties) {
|
|
1085
1108
|
super.willUpdate(changedProperties);
|
|
1086
1109
|
if (changedProperties.has("href")) this.isExternal = isExternalUrl(this.href || "");
|
|
@@ -1092,6 +1115,8 @@ var ULink = class ULink extends LitElement {
|
|
|
1092
1115
|
target=${ifDefined(this.target)}
|
|
1093
1116
|
rel=${ifDefined(this.rel)}
|
|
1094
1117
|
data-navigate=${ifDefined(this.navigate)}
|
|
1118
|
+
aria-current=${ifDefined(this.getAttribute("aria-current") ?? void 0)}
|
|
1119
|
+
aria-label=${ifDefined(this.getAttribute("aria-label") ?? void 0)}
|
|
1095
1120
|
>
|
|
1096
1121
|
<slot></slot>
|
|
1097
1122
|
</a>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@iyulab/router",
|
|
3
|
-
"version": "0.11.
|
|
3
|
+
"version": "0.11.3",
|
|
4
4
|
"description": "A modern client-side router for web applications with support for Lit and React components",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"lit",
|
|
@@ -47,6 +47,7 @@
|
|
|
47
47
|
}
|
|
48
48
|
},
|
|
49
49
|
"scripts": {
|
|
50
|
+
"preversion": "node ../../scripts/preversion-check.mjs",
|
|
50
51
|
"test": "vitest run",
|
|
51
52
|
"build": "npm run typecheck && vite build",
|
|
52
53
|
"test:watch": "vitest",
|
|
@@ -41,3 +41,23 @@ export function AppRoot() {
|
|
|
41
41
|
## Nested Outlet Rule
|
|
42
42
|
|
|
43
43
|
A parent route must render `<u-outlet>` to host child route content.
|
|
44
|
+
|
|
45
|
+
## Default Error Page Styling
|
|
46
|
+
|
|
47
|
+
`Router` renders `<u-error-page>` internally when a route fails and no custom
|
|
48
|
+
`fallback` was configured. It is not exported for direct import, but three CSS
|
|
49
|
+
custom properties are available to lightly restyle it without replacing
|
|
50
|
+
`fallback.render` entirely:
|
|
51
|
+
|
|
52
|
+
| Custom Property | Description | Default |
|
|
53
|
+
| --- | --- | --- |
|
|
54
|
+
| `--error-icon-color` | Icon color | none — inherits the ambient text color |
|
|
55
|
+
| `--error-code-color` | Error code text color | none — inherits the ambient text color |
|
|
56
|
+
| `--error-message-color` | Error message text color | none — inherits the ambient text color |
|
|
57
|
+
|
|
58
|
+
```css
|
|
59
|
+
u-error-page {
|
|
60
|
+
--error-icon-color: #b91c1c;
|
|
61
|
+
--error-code-color: #b91c1c;
|
|
62
|
+
}
|
|
63
|
+
```
|