@iyulab/router 0.13.0 → 0.14.0
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 +38 -0
- package/README.md +30 -0
- package/dist/components/UOutlet.d.ts +1 -0
- package/dist/index.js +1 -1
- package/dist/react.js +1 -1
- package/dist/{share-A3Fla8oW.js → share-tRZgJ1SR.js} +55 -0
- package/package.json +3 -2
- package/skills/iyulab-router/SKILL.md +1 -1
- package/skills/iyulab-router/references/components.md +30 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,43 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.14.0] - 2026-09-16
|
|
4
|
+
|
|
5
|
+
### Changed
|
|
6
|
+
|
|
7
|
+
- **`<u-outlet>` now declares its own `display: block`.** A custom element's UA default is
|
|
8
|
+
`inline`, and the outlet had no styles of its own — so a container meant to hold a route's
|
|
9
|
+
block-level screen generated an inline box. Consumers had to restate `u-outlet { display: block }`
|
|
10
|
+
in every application that cared, which is a rule only the package that defines the element can
|
|
11
|
+
reasonably own.
|
|
12
|
+
|
|
13
|
+
Two independent applications reported this from opposite directions on the same day — one
|
|
14
|
+
through printing (a short document gaining a blank trailing page), one through screen layout
|
|
15
|
+
(a table not reaching the bottom of the viewport). Both traced it to the same missing
|
|
16
|
+
declaration.
|
|
17
|
+
|
|
18
|
+
The declaration is `display: block; height: 100%`. The height half is not cosmetic: changing
|
|
19
|
+
`display` alone also changes which box a percentage height resolves against. While the outlet
|
|
20
|
+
was inline it was not a block container, so a screen's `height: 100%` resolved against the
|
|
21
|
+
block above it; making the outlet a block moves that reference onto the outlet itself, whose
|
|
22
|
+
height is `auto`, which silently voids the percentage and collapses full-height layouts to
|
|
23
|
+
their content height (measured: 747px → 60px for a screen built on
|
|
24
|
+
`@iyulab/modern-app`'s master-detail layout). `height: 100%` restores the chain, and resolves
|
|
25
|
+
to `auto` whenever the parent's height is `auto` — so ordinary document flow and printing,
|
|
26
|
+
where the shell releases its height, are unaffected.
|
|
27
|
+
|
|
28
|
+
The rule is adopted as a constructable stylesheet on whichever tree the outlet is connected to
|
|
29
|
+
(the document, or the shadow root when the outlet lives in one), written as
|
|
30
|
+
`:where(u-outlet)` so its specificity is zero — any `u-outlet { … }` rule an application writes
|
|
31
|
+
still wins without `!important`, regardless of sheet order. Where constructable sheets are not
|
|
32
|
+
available the rule is added as a `<style>` element instead.
|
|
33
|
+
|
|
34
|
+
**This changes layout in normal flow**, not only when printing: an inline box and a block box
|
|
35
|
+
differ in margin collapsing, and a block outlet can be given a height or a percentage size,
|
|
36
|
+
which an inline one silently ignored. Applications that place the outlet inside a flex or grid
|
|
37
|
+
container see no change — flex and grid items were already blockified. To keep the previous
|
|
38
|
+
behavior, set `u-outlet { display: inline }`; to keep the box but not the height, set
|
|
39
|
+
`u-outlet { height: auto }`. Neither needs `!important`.
|
|
40
|
+
|
|
3
41
|
## [0.13.0] - 2026-09-13
|
|
4
42
|
|
|
5
43
|
### Removed
|
package/README.md
CHANGED
|
@@ -141,6 +141,36 @@ const routes = [
|
|
|
141
141
|
- `<u-link>`: SPA-aware anchor element
|
|
142
142
|
- `<u-outlet>`: render target for matched route output
|
|
143
143
|
|
|
144
|
+
## Outlet Layout
|
|
145
|
+
|
|
146
|
+
`<u-outlet>` declares its own `display: block`. A custom element's UA default is
|
|
147
|
+
`inline`, which would put a route's block-level screen inside an inline box — so the
|
|
148
|
+
outlet adopts a single rule on whichever tree it is connected to (the document, or the
|
|
149
|
+
shadow root if it lives in one):
|
|
150
|
+
|
|
151
|
+
```css
|
|
152
|
+
:where(u-outlet) { display: block; height: 100%; }
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
`height: 100%` is part of the same rule for a reason. A percentage height resolves against
|
|
156
|
+
the nearest block container, so making the outlet a block moves that reference from the box
|
|
157
|
+
*above* the outlet onto the outlet itself — and an `auto` height there voids the percentage
|
|
158
|
+
silently, collapsing a full-height screen to its content height. Declaring `height: 100%`
|
|
159
|
+
keeps the chain intact, and resolves to `auto` whenever the parent's height is `auto`, so
|
|
160
|
+
ordinary document flow and printing are unaffected.
|
|
161
|
+
|
|
162
|
+
The `:where()` wrapper makes the rule's specificity zero, so **any** `u-outlet { … }`
|
|
163
|
+
rule your application writes wins, regardless of sheet order and without `!important`:
|
|
164
|
+
|
|
165
|
+
```css
|
|
166
|
+
u-outlet { display: flex; } /* wins */
|
|
167
|
+
u-outlet { height: auto; } /* keep the box, drop the fill */
|
|
168
|
+
u-outlet { display: inline; } /* restores the pre-0.14.0 behavior */
|
|
169
|
+
@media print { u-outlet { … } } /* wins */
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
The rule is not media-scoped: the outlet is a block box on screen and in print alike.
|
|
173
|
+
|
|
144
174
|
`<u-link>` supports `href`, `target`, `rel`, and `navigate`.
|
|
145
175
|
|
|
146
176
|
```html
|
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-tRZgJ1SR.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
|
package/dist/react.js
CHANGED
|
@@ -3,9 +3,64 @@ import { customElement, property } from "lit/decorators.js";
|
|
|
3
3
|
import { ifDefined } from "lit/directives/if-defined.js";
|
|
4
4
|
//#region src/components/UOutlet.ts
|
|
5
5
|
/**
|
|
6
|
+
* `<u-outlet>` 의 기본 표시 방식.
|
|
7
|
+
*
|
|
8
|
+
* ★커스텀 엘리먼트의 UA 기본값은 `inline` 이다 — 라우트 화면(블록 요소들)을 인라인 상자에
|
|
9
|
+
* 담으면 block-in-inline 분할이 생겨, 인쇄에서 짧은 문서에 빈 꼬리 쪽이 붙는다. 컨테이너
|
|
10
|
+
* 요소의 표시 방식은 그것을 정의한 쪽이 선언해야 하고, 그 쪽은 이 패키지다.
|
|
11
|
+
* ★`:where(u-outlet)` 로 특이도를 0 으로 둔다 — 소비자의 `u-outlet { … }` 규칙이 시트 순서와
|
|
12
|
+
* 무관하게 `!important` 없이 이긴다.
|
|
13
|
+
* ⚠constructable 시트(`adoptedStyleSheets`)를 쓴다 — `<style>` 요소와 달리 CSP 의 인라인
|
|
14
|
+
* 스타일 제한에 걸리지 않는다. 지원하지 않는 환경에서는 `<style>` 로 대신한다.
|
|
15
|
+
* ⚠매체를 가르지 않는다 — 인라인 컨테이너는 화면에서도 의도된 적이 없다(라인 박스 때문에
|
|
16
|
+
* 높이를 줄 수도, 백분율로 채울 수도 없었다). 인쇄에만 한정하면 화면·인쇄가 서로 다른
|
|
17
|
+
* 상자 모델을 갖게 되어 같은 부류의 차이가 다음에 또 난다.
|
|
18
|
+
* 🔴`height: 100%` 가 함께 있어야 한다 — `display` 만 바꾸면 **백분율 높이의 기준 상자가
|
|
19
|
+
* 바뀐다.** 종전(inline)에는 아웃렛이 블록 컨테이너가 아니라, 라우트 화면의 `height: 100%`
|
|
20
|
+
* 가 그 «위» 의 블록(셸의 본문 영역)에 대해 풀렸다. 아웃렛을 block 으로 만들면 기준이
|
|
21
|
+
* 아웃렛 자신이 되는데 그 높이가 `auto` 라 백분율이 무효가 되고, 화면을 채우도록 만들어진
|
|
22
|
+
* 레이아웃(`u-master-detail-layout` 의 `:host{height:100%}` 등)이 내용 높이로 무너진다.
|
|
23
|
+
* 실측(chromium): 같은 화면이 inline 747px → block 60px → block+height:100% 747px.
|
|
24
|
+
* ⚠부모 높이가 `auto` 면 `100%` 는 `auto` 로 풀리므로 인쇄(셸이 높이를 놓는다)와 일반
|
|
25
|
+
* 문서 흐름에는 영향이 없다 — 이 선언이 «채우기» 를 강요하는 것은 부모가 높이를 가진
|
|
26
|
+
* 경우뿐이고, 그것이 종전 동작이다.
|
|
27
|
+
*/
|
|
28
|
+
var OUTLET_DISPLAY_CSS = ":where(u-outlet) { display: block; height: 100%; }";
|
|
29
|
+
/** 시트를 이미 채택한 트리 — 같은 트리에 두 번 넣지 않는다. */
|
|
30
|
+
var styledRoots = /* @__PURE__ */ new WeakSet();
|
|
31
|
+
/**
|
|
32
|
+
* 아웃렛이 실제로 속한 트리에 표시 규칙을 채택한다.
|
|
33
|
+
*
|
|
34
|
+
* ⚠`document` 로 못박지 않는다 — 섀도 루트 안의 `<u-outlet>` 은 문서 시트가 닿지 않아
|
|
35
|
+
* 기본 `inline` 그대로 남는다. 아웃렛을 라이트 DOM 에 두는 것이 이 생태계의 관례이지만,
|
|
36
|
+
* 그 관례를 어긴 배치에서 조용히 규칙이 사라지는 쪽이 더 나쁘다.
|
|
37
|
+
*/
|
|
38
|
+
function adoptOutletDisplay(node) {
|
|
39
|
+
const isDocument = node.nodeType === 9;
|
|
40
|
+
const isShadowRoot = node.nodeType === 11 && "host" in node;
|
|
41
|
+
if (!isDocument && !isShadowRoot) return;
|
|
42
|
+
const root = node;
|
|
43
|
+
if (styledRoots.has(root)) return;
|
|
44
|
+
styledRoots.add(root);
|
|
45
|
+
if ("adoptedStyleSheets" in root && typeof CSSStyleSheet !== "undefined" && typeof CSSStyleSheet.prototype.replaceSync === "function") {
|
|
46
|
+
const sheet = new CSSStyleSheet();
|
|
47
|
+
sheet.replaceSync(OUTLET_DISPLAY_CSS);
|
|
48
|
+
root.adoptedStyleSheets = [...root.adoptedStyleSheets, sheet];
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
const ownerDocument = isDocument ? root : node.ownerDocument;
|
|
52
|
+
if (!ownerDocument) return;
|
|
53
|
+
const style = ownerDocument.createElement("style");
|
|
54
|
+
style.textContent = OUTLET_DISPLAY_CSS;
|
|
55
|
+
(isDocument ? root.head : root)?.prepend(style);
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
6
58
|
* LitElement 또는 React 컴포넌트를 렌더링해주는 웹컴포넌트 입니다.
|
|
7
59
|
*/
|
|
8
60
|
var UOutlet = class extends HTMLElement {
|
|
61
|
+
connectedCallback() {
|
|
62
|
+
adoptOutletDisplay(this.getRootNode());
|
|
63
|
+
}
|
|
9
64
|
/**
|
|
10
65
|
* 주어진 렌더링 옵션에 따라 컨텐츠를 렌더링합니다.
|
|
11
66
|
*
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@iyulab/router",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.0",
|
|
4
4
|
"description": "A modern client-side router for web applications with support for Lit and React components",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"lit",
|
|
@@ -71,6 +71,7 @@
|
|
|
71
71
|
}
|
|
72
72
|
},
|
|
73
73
|
"devDependencies": {
|
|
74
|
+
"@vitest/browser-playwright": "^5.0.0",
|
|
74
75
|
"@types/node": "^26.1.1",
|
|
75
76
|
"@types/react": "^19.2.17",
|
|
76
77
|
"@types/react-dom": "^19.2.3",
|
|
@@ -80,6 +81,6 @@
|
|
|
80
81
|
"typescript": "^6.0.2",
|
|
81
82
|
"vite": "^8.1.4",
|
|
82
83
|
"vite-plugin-dts": "^5.0.3",
|
|
83
|
-
"vitest": "^
|
|
84
|
+
"vitest": "^5.0.0"
|
|
84
85
|
}
|
|
85
86
|
}
|
|
@@ -27,7 +27,7 @@ npm install @iyulab/router
|
|
|
27
27
|
| `RouterConfig` | Constructor config type |
|
|
28
28
|
| `RouteContext` | Passed to every `render()` call |
|
|
29
29
|
| `FallbackRouteConfig` | Error/404 fallback definition |
|
|
30
|
-
| `<u-outlet>` | Renders the matched route output |
|
|
30
|
+
| `<u-outlet>` | Renders the matched route output (a block box — see `references/components.md`) |
|
|
31
31
|
| `<u-link>` | Client-side navigation anchor |
|
|
32
32
|
| `UOutlet`, `ULink` | React wrappers (from `@iyulab/router/react`) |
|
|
33
33
|
|
|
@@ -38,6 +38,36 @@ export function AppRoot() {
|
|
|
38
38
|
}
|
|
39
39
|
```
|
|
40
40
|
|
|
41
|
+
## Outlet Layout
|
|
42
|
+
|
|
43
|
+
`<u-outlet>` declares its own `display: block`. A custom element's UA default is
|
|
44
|
+
`inline`, which would put a route's block-level screen inside an inline box — so the
|
|
45
|
+
outlet adopts a single rule on whichever tree it is connected to (the document, or the
|
|
46
|
+
shadow root if it lives in one):
|
|
47
|
+
|
|
48
|
+
```css
|
|
49
|
+
:where(u-outlet) { display: block; height: 100%; }
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
`height: 100%` is part of the same rule for a reason. A percentage height resolves against
|
|
53
|
+
the nearest block container, so making the outlet a block moves that reference from the box
|
|
54
|
+
*above* the outlet onto the outlet itself — and an `auto` height there voids the percentage
|
|
55
|
+
silently, collapsing a full-height screen to its content height. Declaring `height: 100%`
|
|
56
|
+
keeps the chain intact, and resolves to `auto` whenever the parent's height is `auto`, so
|
|
57
|
+
ordinary document flow and printing are unaffected.
|
|
58
|
+
|
|
59
|
+
The `:where()` wrapper makes the rule's specificity zero, so **any** `u-outlet { … }`
|
|
60
|
+
rule your application writes wins, regardless of sheet order and without `!important`:
|
|
61
|
+
|
|
62
|
+
```css
|
|
63
|
+
u-outlet { display: flex; } /* wins */
|
|
64
|
+
u-outlet { height: auto; } /* keep the box, drop the fill */
|
|
65
|
+
u-outlet { display: inline; } /* restores the pre-0.14.0 behavior */
|
|
66
|
+
@media print { u-outlet { … } } /* wins */
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
The rule is not media-scoped: the outlet is a block box on screen and in print alike.
|
|
70
|
+
|
|
41
71
|
## Nested Outlet Rule
|
|
42
72
|
|
|
43
73
|
A parent route must render `<u-outlet>` to host child route content.
|