@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 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
@@ -26,7 +26,7 @@ await app.load({
26
26
  basepath: '/',
27
27
  layout: {
28
28
  type: 'sidebar',
29
- logo: '/assets/logo.svg',
29
+ logo: { src: '/assets/logo.svg', alt: 'My App' },
30
30
  title: 'My App',
31
31
  main: [
32
32
  { type: 'link', icon: 'home', label: 'Home', href: '/' },
@@ -1,4 +1,4 @@
1
- //#region \0@oxc-project+runtime@0.133.0/helpers/esm/decorate.js
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 __decorate };
9
+ export { e as default };
@@ -1,6 +1,6 @@
1
- //#region \0@oxc-project+runtime@0.133.0/helpers/esm/decorateMetadata.js
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 __decorateMetadata };
6
+ export { e as default };
@@ -1,5 +1,5 @@
1
- import { __decorateMetadata as e } from "../_virtual/_@oxc-project_runtime@0.133.0/helpers/esm/decorateMetadata.js";
2
- import { __decorate as t } from "../_virtual/_@oxc-project_runtime@0.133.0/helpers/esm/decorate.js";
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 { __decorateMetadata as e } from "../_virtual/_@oxc-project_runtime@0.133.0/helpers/esm/decorateMetadata.js";
2
- import { __decorate as t } from "../_virtual/_@oxc-project_runtime@0.133.0/helpers/esm/decorate.js";
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 { __decorateMetadata as e } from "../_virtual/_@oxc-project_runtime@0.133.0/helpers/esm/decorateMetadata.js";
2
- import { __decorate as t } from "../_virtual/_@oxc-project_runtime@0.133.0/helpers/esm/decorate.js";
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 { __decorateMetadata as e } from "../_virtual/_@oxc-project_runtime@0.133.0/helpers/esm/decorateMetadata.js";
2
- import { __decorate as t } from "../_virtual/_@oxc-project_runtime@0.133.0/helpers/esm/decorate.js";
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 { __decorateMetadata as e } from "../_virtual/_@oxc-project_runtime@0.133.0/helpers/esm/decorateMetadata.js";
2
- import { __decorate as t } from "../_virtual/_@oxc-project_runtime@0.133.0/helpers/esm/decorate.js";
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 { __decorateMetadata as t } from "../_virtual/_@oxc-project_runtime@0.133.0/helpers/esm/decorateMetadata.js";
3
- import { __decorate as n } from "../_virtual/_@oxc-project_runtime@0.133.0/helpers/esm/decorate.js";
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
- <u-icon class="logo"
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
- <u-icon class="logo"
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,
@@ -23,6 +23,11 @@ var t = e`
23
23
  .logo:hover {
24
24
  color: var(--u-txt-color-hover);
25
25
  }
26
+ img.logo {
27
+ height: 24px;
28
+ width: auto;
29
+ object-fit: contain;
30
+ }
26
31
 
27
32
  .title {
28
33
  flex: 1;
@@ -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?: string;
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.3.7",
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.0.6",
42
- "@iyulab/router": "^0.9.3",
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
- /** URL or path to the logo image. */
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;