@iyulab/modern-app 0.3.7 → 0.4.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 +18 -0
- package/README.md +1 -1
- package/dist/_virtual/{_@oxc-project_runtime@0.133.0 → _@oxc-project_runtime@0.138.0}/helpers/esm/decorate.js +2 -2
- package/dist/_virtual/{_@oxc-project_runtime@0.133.0 → _@oxc-project_runtime@0.138.0}/helpers/esm/decorateMetadata.js +2 -2
- package/dist/components/SidebarButton.js +3 -2
- package/dist/components/SidebarGroup.js +3 -2
- package/dist/components/SidebarLink.js +2 -2
- package/dist/components/SidebarSection.js +3 -2
- package/dist/internals/StyledElement.js +2 -2
- package/dist/layouts/SidebarLayout.d.ts +3 -1
- package/dist/layouts/SidebarLayout.js +30 -12
- package/dist/layouts/SidebarLayout.styles.js +5 -0
- package/dist/layouts/SidebarLayout.types.d.ts +12 -2
- package/package.json +3 -3
- package/skills/modern-app/SKILL.md +2 -2
- package/skills/modern-app/references/layout.md +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.4.0] - 2026-07-02
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- `SidebarLayoutConfig.logo` now accepts an image (`{ src, alt?, href? }`) or a custom render function (`(state) => TemplateResult | HTMLElement | string`), in addition to the existing icon-name string. Logo click navigates to `/` by default, or to `href` when given on the image variant.
|
|
7
|
+
|
|
8
|
+
### Fixed
|
|
9
|
+
- `README.md`/`docs/`/`skills/` previously documented `logo` as accepting an image URL string (e.g. `'/assets/logo.svg'`), but the implementation only ever rendered it as a `u-icon` name — an image URL string silently failed to render an image. Docs now use the image variant (`{ src, alt }`) for that case.
|
|
10
|
+
|
|
11
|
+
## [0.3.7] - 2026-06-09
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
- `SidebarLayout`: main-content keyboard scroll shortcuts (Space/arrows/Home/End/PageUp/PageDown) were swallowing input inside editable elements (input/textarea/select/contenteditable/ARIA textbox) — now skipped via `composedPath()`-based detection
|
|
15
|
+
|
|
16
|
+
## [0.3.6] - 2026-05-21
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
- `SidebarLayout`: keyboard scroll support for `.main` content area (`tabindex="-1"` + `@keydown` handler) — WCAG 2.1 SC 2.1.1 accessibility fix, focus moves to `.main` on route change
|
|
20
|
+
|
|
3
21
|
## [0.3.5] - 2026-04-07
|
|
4
22
|
|
|
5
23
|
### Changed
|
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//#region \0@oxc-project+runtime@0.
|
|
1
|
+
//#region \0@oxc-project+runtime@0.138.0/helpers/esm/decorate.js
|
|
2
2
|
function e(e, t, n, r) {
|
|
3
3
|
var i = arguments.length, a = i < 3 ? t : r === null ? r = Object.getOwnPropertyDescriptor(t, n) : r, o;
|
|
4
4
|
if (typeof Reflect == "object" && typeof Reflect.decorate == "function") a = Reflect.decorate(e, t, n, r);
|
|
@@ -6,4 +6,4 @@ function e(e, t, n, r) {
|
|
|
6
6
|
return i > 3 && a && Object.defineProperty(t, n, a), a;
|
|
7
7
|
}
|
|
8
8
|
//#endregion
|
|
9
|
-
export { e as
|
|
9
|
+
export { e as default };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
//#region \0@oxc-project+runtime@0.
|
|
1
|
+
//#region \0@oxc-project+runtime@0.138.0/helpers/esm/decorateMetadata.js
|
|
2
2
|
function e(e, t) {
|
|
3
3
|
if (typeof Reflect == "object" && typeof Reflect.metadata == "function") return Reflect.metadata(e, t);
|
|
4
4
|
}
|
|
5
5
|
//#endregion
|
|
6
|
-
export { e as
|
|
6
|
+
export { e as default };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
1
|
+
import e from "../_virtual/_@oxc-project_runtime@0.138.0/helpers/esm/decorateMetadata.js";
|
|
2
|
+
import t from "../_virtual/_@oxc-project_runtime@0.138.0/helpers/esm/decorate.js";
|
|
3
3
|
import { StyledElement as n } from "../internals/StyledElement.js";
|
|
4
4
|
import { styles as r } from "./SidebarButton.styles.js";
|
|
5
5
|
import { html as i } from "lit";
|
|
@@ -31,3 +31,4 @@ t([o({
|
|
|
31
31
|
reflect: !0
|
|
32
32
|
}), e("design:type", Object)], s.prototype, "compact", void 0), t([o({ type: String }), e("design:type", String)], s.prototype, "icon", void 0), t([o({ type: String }), e("design:type", Object)], s.prototype, "label", void 0), s = t([a("u-sidebar-button")], s);
|
|
33
33
|
//#endregion
|
|
34
|
+
export { s as SidebarButton };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
1
|
+
import e from "../_virtual/_@oxc-project_runtime@0.138.0/helpers/esm/decorateMetadata.js";
|
|
2
|
+
import t from "../_virtual/_@oxc-project_runtime@0.138.0/helpers/esm/decorate.js";
|
|
3
3
|
import { StyledElement as n } from "../internals/StyledElement.js";
|
|
4
4
|
import { SidebarLink as r } from "./SidebarLink.js";
|
|
5
5
|
import { styles as i } from "./SidebarGroup.styles.js";
|
|
@@ -53,3 +53,4 @@ t([s({
|
|
|
53
53
|
reflect: !0
|
|
54
54
|
}), e("design:type", Boolean)], c.prototype, "collapsed", void 0), t([s({ type: String }), e("design:type", String)], c.prototype, "icon", void 0), t([s({ type: String }), e("design:type", Object)], c.prototype, "label", void 0), c = t([o("u-sidebar-group")], c);
|
|
55
55
|
//#endregion
|
|
56
|
+
export { c as SidebarGroup };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
1
|
+
import e from "../_virtual/_@oxc-project_runtime@0.138.0/helpers/esm/decorateMetadata.js";
|
|
2
|
+
import t from "../_virtual/_@oxc-project_runtime@0.138.0/helpers/esm/decorate.js";
|
|
3
3
|
import { StyledElement as n } from "../internals/StyledElement.js";
|
|
4
4
|
import { styles as r } from "./SidebarLink.styles.js";
|
|
5
5
|
import { html as i } from "lit";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
1
|
+
import e from "../_virtual/_@oxc-project_runtime@0.138.0/helpers/esm/decorateMetadata.js";
|
|
2
|
+
import t from "../_virtual/_@oxc-project_runtime@0.138.0/helpers/esm/decorate.js";
|
|
3
3
|
import { StyledElement as n } from "../internals/StyledElement.js";
|
|
4
4
|
import { styles as r } from "./SidebarSection.styles.js";
|
|
5
5
|
import { html as i } from "lit";
|
|
@@ -31,3 +31,4 @@ var s = class extends n {
|
|
|
31
31
|
};
|
|
32
32
|
t([o({ type: Boolean }), e("design:type", Object)], s.prototype, "compact", void 0), t([o({ type: String }), e("design:type", Object)], s.prototype, "mainTitle", void 0), t([o({ type: String }), e("design:type", Object)], s.prototype, "subTitle", void 0), s = t([a("u-sidebar-section")], s);
|
|
33
33
|
//#endregion
|
|
34
|
+
export { s as SidebarSection };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
1
|
+
import e from "../_virtual/_@oxc-project_runtime@0.138.0/helpers/esm/decorateMetadata.js";
|
|
2
|
+
import t from "../_virtual/_@oxc-project_runtime@0.138.0/helpers/esm/decorate.js";
|
|
3
3
|
import { property as n } from "lit/decorators.js";
|
|
4
4
|
import { UElement as r } from "@iyulab/components/dist/components/UElement.js";
|
|
5
5
|
//#region src/internals/StyledElement.ts
|
|
@@ -20,8 +20,10 @@ export declare class SidebarLayout extends StyledElement<SidebarParts> {
|
|
|
20
20
|
private renderItem;
|
|
21
21
|
/** 현재 경로와 패턴 매칭 여부 확인 */
|
|
22
22
|
private isMatchedLink;
|
|
23
|
-
/** 브랜드 로고 클릭
|
|
23
|
+
/** 브랜드 로고 클릭 핸들러: `href` 지정 시 해당 경로로, 아니면 홈으로 이동 */
|
|
24
24
|
private handleBrandLogoClick;
|
|
25
|
+
/** 로고 렌더링: 아이콘명(문자열, 기존 동작) | 이미지({src,alt,href}) | 커스텀 렌더 함수 */
|
|
26
|
+
private renderLogo;
|
|
25
27
|
/** 사이드바 토글 핸들러 */
|
|
26
28
|
private handleToggleButtonClick;
|
|
27
29
|
/**
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { app as e } from "../App.js";
|
|
2
|
-
import
|
|
3
|
-
import
|
|
2
|
+
import t from "../_virtual/_@oxc-project_runtime@0.138.0/helpers/esm/decorateMetadata.js";
|
|
3
|
+
import n from "../_virtual/_@oxc-project_runtime@0.138.0/helpers/esm/decorate.js";
|
|
4
4
|
import { StyledElement as r } from "../internals/StyledElement.js";
|
|
5
5
|
import "../components/SidebarSection.js";
|
|
6
6
|
import "../components/SidebarLink.js";
|
|
@@ -23,8 +23,8 @@ function m(e) {
|
|
|
23
23
|
}
|
|
24
24
|
var h = class extends r {
|
|
25
25
|
constructor(...t) {
|
|
26
|
-
super(...t), this.state = "default", this.context = null, this.isMatchedLink = (e) => !this.context || !e ? !1 : (e = typeof e == "string" ? new URLPattern(e, window.location.origin) : e, e.test(this.context.path, window.location.origin)), this.handleBrandLogoClick = () => {
|
|
27
|
-
e.navigate("");
|
|
26
|
+
super(...t), this.state = "default", this.context = null, this.isMatchedLink = (e) => !this.context || !e ? !1 : (e = typeof e == "string" ? new URLPattern(e, window.location.origin) : e, e.test(this.context.path, window.location.origin)), this.handleBrandLogoClick = (t) => () => {
|
|
27
|
+
e.navigate(t ?? "");
|
|
28
28
|
}, this.handleToggleButtonClick = () => {
|
|
29
29
|
let t = e.screen ?? "large";
|
|
30
30
|
t === "large" ? this.state = this.state === "default" ? "slim" : "default" : t === "medium" ? this.state = this.state === "slim" ? "modal" : "slim" : t === "small" ? this.state = this.state === "mobile" ? "mobile-open" : "mobile" : console.warn("Unknown screen size:", t);
|
|
@@ -92,10 +92,7 @@ var h = class extends r {
|
|
|
92
92
|
return this.config ? a`
|
|
93
93
|
<!-- Mobile Header -->
|
|
94
94
|
<div class="mobile-header" part="mobile-header" ?hidden="${!this.state.startsWith("mobile")}">
|
|
95
|
-
|
|
96
|
-
.name="${this.config.logo}"
|
|
97
|
-
@click=${this.handleBrandLogoClick}
|
|
98
|
-
></u-icon>
|
|
95
|
+
${this.renderLogo()}
|
|
99
96
|
<span class="title">
|
|
100
97
|
${this.config.title}
|
|
101
98
|
</span>
|
|
@@ -112,10 +109,7 @@ var h = class extends r {
|
|
|
112
109
|
<aside class="sidebar" part="sidebar" state="${this.state}">
|
|
113
110
|
<!-- Sidebar Header -->
|
|
114
111
|
<div class="sidebar-header" part="sidebar-header">
|
|
115
|
-
|
|
116
|
-
.name="${this.config.logo}"
|
|
117
|
-
@click=${this.handleBrandLogoClick}
|
|
118
|
-
></u-icon>
|
|
112
|
+
${this.renderLogo()}
|
|
119
113
|
<span class="title" ?hidden=${this.state === "slim"}>
|
|
120
114
|
${this.config.title}
|
|
121
115
|
</span>
|
|
@@ -203,6 +197,30 @@ var h = class extends r {
|
|
|
203
197
|
`;
|
|
204
198
|
} else return o;
|
|
205
199
|
}
|
|
200
|
+
renderLogo() {
|
|
201
|
+
let e = this.config?.logo;
|
|
202
|
+
if (!e || typeof e == "string") return a`
|
|
203
|
+
<u-icon class="logo"
|
|
204
|
+
.name="${e}"
|
|
205
|
+
@click=${this.handleBrandLogoClick()}
|
|
206
|
+
></u-icon>
|
|
207
|
+
`;
|
|
208
|
+
if (typeof e == "function") {
|
|
209
|
+
let t = e(this.state);
|
|
210
|
+
return a`
|
|
211
|
+
<span class="logo" @click=${this.handleBrandLogoClick()}>
|
|
212
|
+
${typeof t == "string" ? d(t) : t}
|
|
213
|
+
</span>
|
|
214
|
+
`;
|
|
215
|
+
}
|
|
216
|
+
return a`
|
|
217
|
+
<img class="logo"
|
|
218
|
+
src="${e.src}"
|
|
219
|
+
alt="${e.alt ?? ""}"
|
|
220
|
+
@click=${this.handleBrandLogoClick(e.href)}
|
|
221
|
+
/>
|
|
222
|
+
`;
|
|
223
|
+
}
|
|
206
224
|
};
|
|
207
225
|
n([c({
|
|
208
226
|
type: String,
|
|
@@ -13,13 +13,23 @@ export interface SidebarHtmlConfig {
|
|
|
13
13
|
type: 'html';
|
|
14
14
|
render: (state: SidebarState) => TemplateResult<1> | HTMLElement | string;
|
|
15
15
|
}
|
|
16
|
+
/** 이미지 로고 설정. `href` 지정 시 클릭 시 기본 홈 이동 대신 해당 경로로 이동 */
|
|
17
|
+
export interface SidebarLogoImage {
|
|
18
|
+
src: string;
|
|
19
|
+
alt?: string;
|
|
20
|
+
href?: string;
|
|
21
|
+
}
|
|
22
|
+
/** 커스텀 로고 렌더 함수. `SidebarHtmlConfig.render`와 동일한 반환 타입 */
|
|
23
|
+
export type SidebarLogoRenderer = (state: SidebarState) => TemplateResult<1> | HTMLElement | string;
|
|
24
|
+
/** 최상단 앱 로고 설정: 아이콘명(문자열, 기존 동작) | 이미지 | 커스텀 렌더 함수 */
|
|
25
|
+
export type SidebarLogoConfig = string | SidebarLogoImage | SidebarLogoRenderer;
|
|
16
26
|
/** union: section | group | link | button */
|
|
17
27
|
export type SidebarItem = (SidebarLinkConfig | SidebarSectionConfig | SidebarGroupConfig | SidebarButtonConfig | SidebarHtmlConfig);
|
|
18
28
|
/** 사이드바 레이아웃 전체 설정 */
|
|
19
29
|
export interface SidebarLayoutConfig {
|
|
20
30
|
type: 'sidebar';
|
|
21
|
-
/** 최상단 앱
|
|
22
|
-
logo?:
|
|
31
|
+
/** 최상단 앱 로고: 아이콘명(문자열) | 이미지({src,alt,href}) | 커스텀 렌더 함수 */
|
|
32
|
+
logo?: SidebarLogoConfig;
|
|
23
33
|
/** 앱 제목 */
|
|
24
34
|
title?: string;
|
|
25
35
|
/** 상단/메인 메뉴 아이템 항목들 */
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@iyulab/modern-app",
|
|
3
3
|
"description": "web-framework by iyulab based on lit-element",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.4.0",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"iyulab",
|
|
7
7
|
"web-framework",
|
|
@@ -38,8 +38,8 @@
|
|
|
38
38
|
"build": "vite build"
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@iyulab/components": "^1.
|
|
42
|
-
"@iyulab/router": "^0.
|
|
41
|
+
"@iyulab/components": "^1.1.1",
|
|
42
|
+
"@iyulab/router": "^0.10.0",
|
|
43
43
|
"i18next": "^26.2.0",
|
|
44
44
|
"lit": "^3.3.3"
|
|
45
45
|
},
|
|
@@ -35,7 +35,7 @@ await app.load({
|
|
|
35
35
|
basepath: '/',
|
|
36
36
|
layout: {
|
|
37
37
|
type: 'sidebar',
|
|
38
|
-
logo: '/assets/logo.svg',
|
|
38
|
+
logo: { src: '/assets/logo.svg', alt: 'My App' },
|
|
39
39
|
title: 'My App',
|
|
40
40
|
main: [
|
|
41
41
|
{ type: 'link', icon: 'home', label: 'Home', href: '/' },
|
|
@@ -157,7 +157,7 @@ Full configuration reference: [references/layout.md](./references/layout.md)
|
|
|
157
157
|
```typescript
|
|
158
158
|
layout: {
|
|
159
159
|
type: 'sidebar',
|
|
160
|
-
logo: '/logo.svg',
|
|
160
|
+
logo: { src: '/logo.svg', alt: 'App Name' },
|
|
161
161
|
title: 'App Name',
|
|
162
162
|
main: [
|
|
163
163
|
{
|
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
interface SidebarLayoutConfig {
|
|
7
7
|
type: 'sidebar';
|
|
8
8
|
|
|
9
|
-
/**
|
|
10
|
-
logo?: string;
|
|
9
|
+
/** Icon name (string) | image ({ src, alt?, href? }) | custom render function. Click navigates to `/` by default, or `href` if given (image variant). */
|
|
10
|
+
logo?: string | { src: string; alt?: string; href?: string } | ((state: SidebarState) => TemplateResult<1> | HTMLElement | string);
|
|
11
11
|
|
|
12
12
|
/** Application title displayed beside the logo. */
|
|
13
13
|
title?: string;
|