@iyulab/modern-app 0.17.0 → 0.18.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.
Files changed (32) hide show
  1. package/CHANGELOG.md +49 -0
  2. package/dist/_virtual/{_@oxc-project_runtime@0.143.0 → _@oxc-project_runtime@0.146.0}/helpers/esm/decorate.js +1 -1
  3. package/dist/_virtual/{_@oxc-project_runtime@0.143.0 → _@oxc-project_runtime@0.146.0}/helpers/esm/decorateMetadata.js +1 -1
  4. package/dist/components/ActionBar.js +2 -2
  5. package/dist/components/EmptyState.js +2 -2
  6. package/dist/components/GroupBox.js +2 -2
  7. package/dist/components/GroupBox.styles.js +1 -1
  8. package/dist/components/InfoField.d.ts +4 -0
  9. package/dist/components/InfoField.js +3 -3
  10. package/dist/components/InfoField.styles.js +11 -0
  11. package/dist/components/InfoSection.js +2 -2
  12. package/dist/components/MasterDetailLayout.js +2 -2
  13. package/dist/components/PageHeader.js +2 -2
  14. package/dist/components/SidebarButton.d.ts +3 -0
  15. package/dist/components/SidebarButton.js +2 -2
  16. package/dist/components/SidebarGroup.js +2 -2
  17. package/dist/components/SidebarLink.d.ts +1 -1
  18. package/dist/components/SidebarLink.js +9 -8
  19. package/dist/components/SidebarSection.js +2 -2
  20. package/dist/components/Wizard.js +2 -2
  21. package/dist/index.d.ts +2 -0
  22. package/dist/index.js +14 -13
  23. package/dist/internals/StyledElement.js +2 -2
  24. package/dist/internals/locale.d.ts +4 -0
  25. package/dist/internals/locale.js +2 -0
  26. package/dist/layouts/SidebarLayout.d.ts +3 -1
  27. package/dist/layouts/SidebarLayout.js +40 -34
  28. package/dist/layouts/SidebarLayout.types.d.ts +7 -0
  29. package/dist/react.d.ts +2 -0
  30. package/dist/react.js +12 -11
  31. package/package.json +1 -1
  32. package/skills/modern-app/references/components/info-field.md +1 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,54 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.18.2] - 2026-08-19
4
+
5
+ ### Fixed
6
+
7
+ - **`SidebarLayout`'s mobile-header and sidebar-header toggle buttons now have an accessible
8
+ name.** Both are icon-only `<u-button>`s with no `aria-label`, text, or `title` — a
9
+ screen-reader user only ever heard an unnamed "button". Wired through the same locale
10
+ registry `MasterDetailLayout.detailClose` already uses (`toggleMobileMenu` /
11
+ `toggleSidebar`), so translations follow `registerLocale` automatically; default English
12
+ labels preserve current behavior for everyone who hasn't set a `locale`.
13
+
14
+ ### Added
15
+
16
+ - **`SidebarButtonConfig.id?: string`.** Passed straight through to the rendered
17
+ `<u-sidebar-button>` host, so a consumer can anchor a `u-popover` (or any `for="#id"`
18
+ pattern) to a sidebar button using only public API — previously the only way to get an
19
+ anchorable id was to reach into the internal, unexported `<u-sidebar-button>` tag name.
20
+ Unset behaves exactly as before (no `id` attribute) — not a breaking change.
21
+
22
+ ## [0.18.1] - 2026-08-19
23
+
24
+ ### Fixed
25
+
26
+ - **`GroupBox`'s background no longer matches the surrounding page/sidebar in light mode, or
27
+ input fields in dark mode.** It referenced the same token as those surfaces
28
+ (`--u-panel-bg-color`); switched to `--u-bg-color-raised`, which both themes already define
29
+ distinctly.
30
+
31
+ ## [0.18.0] - 2026-08-17
32
+
33
+ ### Added
34
+
35
+ - **`SidebarLink` now sets `aria-current="page"` on its inner `<u-link>` when `selected` is
36
+ true.** Previously `selected` only drove visual emphasis (a host attribute for CSS) — a
37
+ screen-reader user had no way to tell which item was the current page.
38
+ - **`SidebarLayoutConfig.mainAriaLabel?: string`.** When set, it's reflected as `aria-label`
39
+ on the sidebar's main `<nav>` landmark. That `<nav>` lives inside `SidebarLayout`'s shadow
40
+ root, so a consumer had no way to name it from the outside. Unset behaves exactly as
41
+ before (no landmark name) — not a breaking change.
42
+ - **`InfoField`'s `tone` now colors the value text itself, not just the trend indicator.**
43
+ Previously `.trend.tone-*` was the only CSS surface for `tone`, so a static figure shown
44
+ without a `trend` (e.g. a balance due) couldn't be toned at all. `trend`'s existing
45
+ behavior is unchanged; this only widens where `tone` is visible.
46
+ - **`ScreenObserver` is now exported from both the main entry and `/react`.** `SidebarLayout`'s
47
+ responsive state (large→slim, medium→modal, small→mobile) depends entirely on the
48
+ `screen-resize` event this class dispatches; `app.load()` instantiates it internally, but a
49
+ consumer mounting `SidebarLayout` standalone through `/react` (without `app.load()`) had no
50
+ public way to drive that behavior.
51
+
3
52
  ## [0.17.0] - 2026-08-11
4
53
 
5
54
  ### Added
@@ -1,4 +1,4 @@
1
- //#region \0@oxc-project+runtime@0.143.0/helpers/esm/decorate.js
1
+ //#region \0@oxc-project+runtime@0.146.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);
@@ -1,4 +1,4 @@
1
- //#region \0@oxc-project+runtime@0.143.0/helpers/esm/decorateMetadata.js
1
+ //#region \0@oxc-project+runtime@0.146.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
  }
@@ -1,5 +1,5 @@
1
- import e from "../_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorateMetadata.js";
2
- import t from "../_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorate.js";
1
+ import e from "../_virtual/_@oxc-project_runtime@0.146.0/helpers/esm/decorateMetadata.js";
2
+ import t from "../_virtual/_@oxc-project_runtime@0.146.0/helpers/esm/decorate.js";
3
3
  import { StyledElement as n } from "../internals/StyledElement.js";
4
4
  import { slotHasContent as r } from "../internals/slotted.js";
5
5
  import { styles as i } from "./ActionBar.styles.js";
@@ -1,5 +1,5 @@
1
- import e from "../_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorateMetadata.js";
2
- import t from "../_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorate.js";
1
+ import e from "../_virtual/_@oxc-project_runtime@0.146.0/helpers/esm/decorateMetadata.js";
2
+ import t from "../_virtual/_@oxc-project_runtime@0.146.0/helpers/esm/decorate.js";
3
3
  import { StyledElement as n } from "../internals/StyledElement.js";
4
4
  import { getLocaleStrings as r } from "../internals/locale.js";
5
5
  import { slotHasContent as i } from "../internals/slotted.js";
@@ -1,5 +1,5 @@
1
- import e from "../_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorateMetadata.js";
2
- import t from "../_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorate.js";
1
+ import e from "../_virtual/_@oxc-project_runtime@0.146.0/helpers/esm/decorateMetadata.js";
2
+ import t from "../_virtual/_@oxc-project_runtime@0.146.0/helpers/esm/decorate.js";
3
3
  import { StyledElement as n } from "../internals/StyledElement.js";
4
4
  import { slotHasContent as r } from "../internals/slotted.js";
5
5
  import { styles as i } from "./GroupBox.styles.js";
@@ -7,7 +7,7 @@ var t = e`
7
7
  사이드바가 열린 1280px 화면의 좁은 본문에서도, 넓은 본문을 가진 태블릿에서도
8
8
  같은 판단을 하려면 기준이 자기 폭이어야 한다. */
9
9
  container-type: inline-size;
10
- background-color: var(--u-panel-bg-color, #FFFFFF);
10
+ background-color: var(--u-bg-color-raised, #FAFAFA);
11
11
  border: 1px solid var(--u-border-color-weak, #EEEEEE);
12
12
  /* 면(surface) 단 — 컨트롤과 같은 반경을 쓰면 큰 사각형이 각져 보인다. */
13
13
  border-radius: var(--u-radius-2xl, 12px);
@@ -77,6 +77,10 @@ export declare class InfoField extends StyledElement<ElementParts> {
77
77
  * Explicit tone override. When unset, resolves from `trend` (`up→positive`, `down→negative`,
78
78
  * `flat`/unset→`neutral`). **Always wins over inference** — some metrics invert the usual
79
79
  * direction-to-sentiment mapping (e.g. a falling "open tickets" count is `positive`).
80
+ *
81
+ * Colors the value text itself, independent of `trend` — a static "balance due" figure can be
82
+ * toned `negative` without a trend arrow. `neutral` has no visual effect on the value text
83
+ * (it already renders at full strength; only `positive`/`negative` stand out from it).
80
84
  */
81
85
  tone?: InfoFieldTone;
82
86
  private get hasSlotted();
@@ -1,5 +1,5 @@
1
- import e from "../_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorateMetadata.js";
2
- import t from "../_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorate.js";
1
+ import e from "../_virtual/_@oxc-project_runtime@0.146.0/helpers/esm/decorateMetadata.js";
2
+ import t from "../_virtual/_@oxc-project_runtime@0.146.0/helpers/esm/decorate.js";
3
3
  import { StyledElement as n } from "../internals/StyledElement.js";
4
4
  import { styles as r } from "./InfoField.styles.js";
5
5
  import { html as i } from "lit";
@@ -29,7 +29,7 @@ var f = class extends n {
29
29
  let e = !this.hasSlotted && d(this.value), t = this.numeric || this.format === "number" || this.format === "currency", n = this.trend !== void 0 || this.trendLabel !== void 0, r = this.tone ?? u(this.trend), a = this.trend === "up" ? "▲" : this.trend === "down" ? "▼" : "";
30
30
  return i`
31
31
  <div class="label" part="label">${this.label}</div>
32
- <div class="value ${t ? "numeric" : ""} ${e ? "blank" : ""}" part="value">
32
+ <div class="value ${t ? "numeric" : ""} ${e ? "blank" : ""} tone-${r}" part="value">
33
33
  ${this.hasSlotted ? i`<slot></slot>` : e ? this.blank : this.formatValue()}
34
34
  </div>
35
35
  ${n ? i`
@@ -46,6 +46,17 @@ var t = e`
46
46
  font-weight: var(--u-text-body-weight, 400);
47
47
  }
48
48
 
49
+ /* tone은 trend 유무와 무관하게 값 텍스트 자체에도 적용된다(neutral은 무규칙 — 기본
50
+ .value 색과 시각적으로 같은 자리라 별도 규칙을 두면 오히려 "옅어 보이는" 부작용이
51
+ 생긴다, .trend.tone-neutral과 달리 .value는 이미 강한 색이 기본값이다). */
52
+ .value.tone-positive {
53
+ color: var(--u-success-color-strong, #1B5E20);
54
+ }
55
+
56
+ .value.tone-negative {
57
+ color: var(--u-danger-color-strong, #C62828);
58
+ }
59
+
49
60
  .trend {
50
61
  font-size: var(--u-text-caption-size, 12px);
51
62
  margin-top: var(--u-space-3xs, 2px);
@@ -1,5 +1,5 @@
1
- import e from "../_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorateMetadata.js";
2
- import t from "../_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorate.js";
1
+ import e from "../_virtual/_@oxc-project_runtime@0.146.0/helpers/esm/decorateMetadata.js";
2
+ import t from "../_virtual/_@oxc-project_runtime@0.146.0/helpers/esm/decorate.js";
3
3
  import { StyledElement as n } from "../internals/StyledElement.js";
4
4
  import { styles as r } from "./InfoSection.styles.js";
5
5
  import { html as i } from "lit";
@@ -1,5 +1,5 @@
1
- import e from "../_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorateMetadata.js";
2
- import t from "../_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorate.js";
1
+ import e from "../_virtual/_@oxc-project_runtime@0.146.0/helpers/esm/decorateMetadata.js";
2
+ import t from "../_virtual/_@oxc-project_runtime@0.146.0/helpers/esm/decorate.js";
3
3
  import { StyledElement as n } from "../internals/StyledElement.js";
4
4
  import { getLocaleStrings as r } from "../internals/locale.js";
5
5
  import { slotHasContent as i } from "../internals/slotted.js";
@@ -1,5 +1,5 @@
1
- import e from "../_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorateMetadata.js";
2
- import t from "../_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorate.js";
1
+ import e from "../_virtual/_@oxc-project_runtime@0.146.0/helpers/esm/decorateMetadata.js";
2
+ import t from "../_virtual/_@oxc-project_runtime@0.146.0/helpers/esm/decorate.js";
3
3
  import { StyledElement as n } from "../internals/StyledElement.js";
4
4
  import { getLocaleStrings as r } from "../internals/locale.js";
5
5
  import { slotHasContent as i } from "../internals/slotted.js";
@@ -6,6 +6,9 @@ type ElementParts = 'host' | 'base' | 'icon' | 'label';
6
6
  /** 버튼 항목 구성 */
7
7
  export interface SidebarButtonConfig extends SidebarPermissionGuard {
8
8
  type: 'button';
9
+ /** 렌더된 `<u-sidebar-button>` 호스트에 그대로 전달된다 — `u-popover[for="#id"]` 같은
10
+ * 외부 앵커링에 쓴다. */
11
+ id?: string;
9
12
  icon?: string;
10
13
  /** `icon`을 어느 등록 라이브러리에서 찾을지. 미지정 시 `u-icon`의 기본 URL 경로로
11
14
  * 해석된다(`IconRegistry.register()`로 등록한 이름 있는 세트를 쓰려면 지정해야 함). */
@@ -1,5 +1,5 @@
1
- import e from "../_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorateMetadata.js";
2
- import t from "../_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorate.js";
1
+ import e from "../_virtual/_@oxc-project_runtime@0.146.0/helpers/esm/decorateMetadata.js";
2
+ import t from "../_virtual/_@oxc-project_runtime@0.146.0/helpers/esm/decorate.js";
3
3
  import { StyledElement as n } from "../internals/StyledElement.js";
4
4
  import { DEFAULT_NAV_ICON as r } from "../internals/nav-icon.js";
5
5
  import { styles as i } from "./SidebarButton.styles.js";
@@ -1,5 +1,5 @@
1
- import e from "../_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorateMetadata.js";
2
- import t from "../_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorate.js";
1
+ import e from "../_virtual/_@oxc-project_runtime@0.146.0/helpers/esm/decorateMetadata.js";
2
+ import t from "../_virtual/_@oxc-project_runtime@0.146.0/helpers/esm/decorate.js";
3
3
  import { StyledElement as n } from "../internals/StyledElement.js";
4
4
  import { DEFAULT_NAV_ICON as r } from "../internals/nav-icon.js";
5
5
  import { SidebarLink as i } from "./SidebarLink.js";
@@ -37,7 +37,7 @@ export interface SidebarLinkConfig extends SidebarPermissionGuard {
37
37
  */
38
38
  export declare class SidebarLink extends StyledElement<ElementParts> {
39
39
  static styles: import('lit').CSSResultGroup[];
40
- /** selected 상태 */
40
+ /** selected 상태. `true`면 내부 `<u-link>`에 `aria-current="page"`도 함께 세팅한다. */
41
41
  selected: boolean;
42
42
  /** 콤팩트 모드 여부 */
43
43
  compact: boolean;
@@ -1,13 +1,13 @@
1
- import e from "../_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorateMetadata.js";
2
- import t from "../_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorate.js";
1
+ import e from "../_virtual/_@oxc-project_runtime@0.146.0/helpers/esm/decorateMetadata.js";
2
+ import t from "../_virtual/_@oxc-project_runtime@0.146.0/helpers/esm/decorate.js";
3
3
  import { StyledElement as n } from "../internals/StyledElement.js";
4
4
  import { DEFAULT_NAV_ICON as r } from "../internals/nav-icon.js";
5
5
  import { styles as i } from "./SidebarLink.styles.js";
6
- import { html as a } from "lit";
7
- import { customElement as o, property as s } from "lit/decorators.js";
6
+ import { html as a, nothing as o } from "lit";
7
+ import { customElement as s, property as c } from "lit/decorators.js";
8
8
  import "@iyulab/components/dist/components/icon/UIcon.js";
9
9
  //#region src/components/SidebarLink.ts
10
- var c = class extends n {
10
+ var l = class extends n {
11
11
  constructor(...e) {
12
12
  super(...e), this.selected = !1, this.compact = !1;
13
13
  }
@@ -20,6 +20,7 @@ var c = class extends n {
20
20
  .href=${this.href || "#"}
21
21
  .navigate=${this.navigate}
22
22
  .target=${this.target}
23
+ aria-current=${this.selected ? "page" : o}
23
24
  >
24
25
  <div class="container" part="base" ?compact=${this.compact}>
25
26
  <u-icon part="icon"
@@ -35,9 +36,9 @@ var c = class extends n {
35
36
  `;
36
37
  }
37
38
  };
38
- t([s({
39
+ t([c({
39
40
  type: Boolean,
40
41
  reflect: !0
41
- }), e("design:type", Object)], c.prototype, "selected", void 0), t([s({ type: Boolean }), e("design:type", Object)], c.prototype, "compact", void 0), t([s({ type: String }), e("design:type", String)], c.prototype, "icon", void 0), t([s({ type: String }), e("design:type", String)], c.prototype, "lib", void 0), t([s({ type: String }), e("design:type", Object)], c.prototype, "label", void 0), t([s({ type: String }), e("design:type", String)], c.prototype, "href", void 0), t([s({ type: String }), e("design:type", Object)], c.prototype, "pattern", void 0), t([s({ type: String }), e("design:type", String)], c.prototype, "navigate", void 0), t([s({ type: String }), e("design:type", String)], c.prototype, "target", void 0), c = t([o("u-sidebar-link")], c);
42
+ }), e("design:type", Object)], l.prototype, "selected", void 0), t([c({ type: Boolean }), e("design:type", Object)], l.prototype, "compact", void 0), t([c({ type: String }), e("design:type", String)], l.prototype, "icon", void 0), t([c({ type: String }), e("design:type", String)], l.prototype, "lib", void 0), t([c({ type: String }), e("design:type", Object)], l.prototype, "label", void 0), t([c({ type: String }), e("design:type", String)], l.prototype, "href", void 0), t([c({ type: String }), e("design:type", Object)], l.prototype, "pattern", void 0), t([c({ type: String }), e("design:type", String)], l.prototype, "navigate", void 0), t([c({ type: String }), e("design:type", String)], l.prototype, "target", void 0), l = t([s("u-sidebar-link")], l);
42
43
  //#endregion
43
- export { c as SidebarLink };
44
+ export { l as SidebarLink };
@@ -1,5 +1,5 @@
1
- import e from "../_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorateMetadata.js";
2
- import t from "../_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorate.js";
1
+ import e from "../_virtual/_@oxc-project_runtime@0.146.0/helpers/esm/decorateMetadata.js";
2
+ import t from "../_virtual/_@oxc-project_runtime@0.146.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";
@@ -1,5 +1,5 @@
1
- import e from "../_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorateMetadata.js";
2
- import t from "../_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorate.js";
1
+ import e from "../_virtual/_@oxc-project_runtime@0.146.0/helpers/esm/decorateMetadata.js";
2
+ import t from "../_virtual/_@oxc-project_runtime@0.146.0/helpers/esm/decorate.js";
3
3
  import { StyledElement as n } from "../internals/StyledElement.js";
4
4
  import { getLocaleStrings as r } from "../internals/locale.js";
5
5
  import { styles as i } from "./Wizard.styles.js";
package/dist/index.d.ts CHANGED
@@ -15,5 +15,7 @@ export { Wizard } from './components/Wizard.js';
15
15
  export type { WizardStep, WizardStepState, WizardStepChangeDetail } from './components/Wizard.js';
16
16
  export { registerLocale, setDefaultLocale, getLocaleStrings, getDefaultLocale, } from './internals/locale.js';
17
17
  export type { ModernAppLocaleStrings } from './internals/locale.js';
18
+ export { ScreenObserver } from './internals/ScreenObserver.js';
19
+ export type { ScreenSize, ScreenObserverConfig, ScreenResizeEvent } from './internals/ScreenObserver.js';
18
20
  export { app };
19
21
  export default app;
package/dist/index.js CHANGED
@@ -1,15 +1,16 @@
1
- import { app as e } from "./App.js";
2
- import { filterSidebarItems as t } from "./layouts/filterSidebarItems.js";
3
- import { getDefaultLocale as n, getLocaleStrings as r, registerLocale as i, setDefaultLocale as a } from "./internals/locale.js";
4
- import { PageHeader as o } from "./components/PageHeader.js";
5
- import { GroupBox as s } from "./components/GroupBox.js";
6
- import { InfoSection as c } from "./components/InfoSection.js";
7
- import { InfoField as l, isBlank as u } from "./components/InfoField.js";
8
- import { EmptyState as d } from "./components/EmptyState.js";
9
- import { ActionBar as f } from "./components/ActionBar.js";
10
- import { MasterDetailLayout as p } from "./components/MasterDetailLayout.js";
11
- import { Wizard as m } from "./components/Wizard.js";
1
+ import { ScreenObserver as e } from "./internals/ScreenObserver.js";
2
+ import { app as t } from "./App.js";
3
+ import { filterSidebarItems as n } from "./layouts/filterSidebarItems.js";
4
+ import { getDefaultLocale as r, getLocaleStrings as i, registerLocale as a, setDefaultLocale as o } from "./internals/locale.js";
5
+ import { PageHeader as s } from "./components/PageHeader.js";
6
+ import { GroupBox as c } from "./components/GroupBox.js";
7
+ import { InfoSection as l } from "./components/InfoSection.js";
8
+ import { InfoField as u, isBlank as d } from "./components/InfoField.js";
9
+ import { EmptyState as f } from "./components/EmptyState.js";
10
+ import { ActionBar as p } from "./components/ActionBar.js";
11
+ import { MasterDetailLayout as m } from "./components/MasterDetailLayout.js";
12
+ import { Wizard as h } from "./components/Wizard.js";
12
13
  //#region src/index.ts
13
- var h = e;
14
+ var g = t;
14
15
  //#endregion
15
- export { f as ActionBar, d as EmptyState, s as GroupBox, l as InfoField, c as InfoSection, p as MasterDetailLayout, o as PageHeader, m as Wizard, e as app, h as default, t as filterSidebarItems, n as getDefaultLocale, r as getLocaleStrings, u as isBlank, i as registerLocale, a as setDefaultLocale };
16
+ export { p as ActionBar, f as EmptyState, c as GroupBox, u as InfoField, l as InfoSection, m as MasterDetailLayout, s as PageHeader, e as ScreenObserver, h as Wizard, t as app, g as default, n as filterSidebarItems, r as getDefaultLocale, i as getLocaleStrings, d as isBlank, a as registerLocale, o as setDefaultLocale };
@@ -1,5 +1,5 @@
1
- import e from "../_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorateMetadata.js";
2
- import t from "../_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorate.js";
1
+ import e from "../_virtual/_@oxc-project_runtime@0.146.0/helpers/esm/decorateMetadata.js";
2
+ import t from "../_virtual/_@oxc-project_runtime@0.146.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
@@ -28,6 +28,10 @@ export interface ModernAppLocaleStrings {
28
28
  noResultsDescription: string;
29
29
  /** Master-detail layout — accessible label for the overlay-mode close button. */
30
30
  detailClose: string;
31
+ /** Sidebar layout — accessible label for the mobile-header menu toggle button. */
32
+ toggleMobileMenu: string;
33
+ /** Sidebar layout — accessible label for the sidebar-header collapse/expand toggle button. */
34
+ toggleSidebar: string;
31
35
  /** Wizard — default Back/Next action labels ("Submit" wording is the consumer's call). */
32
36
  wizardBack: string;
33
37
  wizardNext: string;
@@ -6,6 +6,8 @@ var e = {
6
6
  noResultsTitle: "No matching results",
7
7
  noResultsDescription: "Try changing your search terms or filters.",
8
8
  detailClose: "Close",
9
+ toggleMobileMenu: "Toggle menu",
10
+ toggleSidebar: "Toggle sidebar",
9
11
  wizardBack: "Back",
10
12
  wizardNext: "Next",
11
13
  wizardStepAnnouncement: (e, t, n) => `Step ${e} of ${t}: ${n}`
@@ -9,13 +9,15 @@ export declare class SidebarLayout extends StyledElement<SidebarParts> {
9
9
  state: SidebarState;
10
10
  /** 사이드바 레이아웃 설정 */
11
11
  config?: SidebarLayoutConfig;
12
+ /** 크롬 문자열(토글 버튼 접근성 라벨 등) 로케일 — `registerLocale`로 등록한 언어 태그 */
13
+ locale: string;
12
14
  progressBarEl: UProgressBar;
13
15
  /** 현재 라우터 컨텍스트 */
14
16
  context: RouteContext | null;
15
17
  connectedCallback(): void;
16
18
  disconnectedCallback(): void;
17
19
  protected willUpdate(changedProperties: PropertyValues): void;
18
- render(): import('lit-html').TemplateResult<1> | typeof nothing;
20
+ render(): typeof nothing | import('lit-html').TemplateResult<1>;
19
21
  /** 사이드바 아이템 렌더링 */
20
22
  private renderItem;
21
23
  /** 현재 경로와 패턴 매칭 여부 확인 */
@@ -1,30 +1,32 @@
1
1
  import { app as e } from "../App.js";
2
2
  import { filterSidebarItems as t } from "./filterSidebarItems.js";
3
- import n from "../_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorateMetadata.js";
4
- import r from "../_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorate.js";
3
+ import n from "../_virtual/_@oxc-project_runtime@0.146.0/helpers/esm/decorateMetadata.js";
4
+ import r from "../_virtual/_@oxc-project_runtime@0.146.0/helpers/esm/decorate.js";
5
5
  import { StyledElement as i } from "../internals/StyledElement.js";
6
+ import { getLocaleStrings as a } from "../internals/locale.js";
6
7
  import "../components/SidebarSection.js";
7
8
  import "../components/SidebarLink.js";
8
9
  import "../components/SidebarGroup.js";
9
10
  import "../components/SidebarButton.js";
10
- import { styles as a } from "./SidebarLayout.styles.js";
11
- import { html as o, nothing as s } from "lit";
12
- import { customElement as c, property as l, query as u, state as d } from "lit/decorators.js";
11
+ import { styles as o } from "./SidebarLayout.styles.js";
12
+ import { html as s, nothing as c } from "lit";
13
+ import { customElement as l, property as u, query as d, state as f } from "lit/decorators.js";
13
14
  import "@iyulab/components/dist/components/button/UButton.js";
14
15
  import "@iyulab/components/dist/components/icon/UIcon.js";
15
- import { unsafeHTML as f } from "lit/directives/unsafe-html.js";
16
- import { repeat as p } from "lit/directives/repeat.js";
17
- import { UProgressBar as m } from "@iyulab/components/dist/components/progress-bar/UProgressBar.js";
16
+ import { unsafeHTML as p } from "lit/directives/unsafe-html.js";
17
+ import { repeat as m } from "lit/directives/repeat.js";
18
+ import { ifDefined as h } from "lit/directives/if-defined.js";
19
+ import { UProgressBar as g } from "@iyulab/components/dist/components/progress-bar/UProgressBar.js";
18
20
  //#region src/layouts/SidebarLayout.ts
19
- function h(e) {
21
+ function _(e) {
20
22
  let t = e.tagName;
21
23
  if (t === "INPUT" || t === "TEXTAREA" || t === "SELECT" || e.isContentEditable) return !0;
22
24
  let n = e.getAttribute("role");
23
25
  return n === "textbox" || n === "searchbox" || n === "combobox" || n === "spinbutton";
24
26
  }
25
- var g = class extends i {
27
+ var v = class extends i {
26
28
  constructor(...t) {
27
- 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) => () => {
29
+ super(...t), this.state = "default", this.locale = "", 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) => () => {
28
30
  e.navigate(t ?? "");
29
31
  }, this.handleToggleButtonClick = () => {
30
32
  let t = e.screen ?? "large";
@@ -45,7 +47,7 @@ var g = class extends i {
45
47
  }, 300);
46
48
  }, this._handleMainKeydown = (e) => {
47
49
  let t = e.composedPath()[0];
48
- if (t instanceof HTMLElement && h(t)) return;
50
+ if (t instanceof HTMLElement && _(t)) return;
49
51
  let n = this.shadowRoot?.querySelector(".main");
50
52
  if (!n) return;
51
53
  let r = n.clientHeight;
@@ -78,7 +80,7 @@ var g = class extends i {
78
80
  };
79
81
  }
80
82
  static {
81
- this.styles = [super.styles, a];
83
+ this.styles = [super.styles, o];
82
84
  }
83
85
  connectedCallback() {
84
86
  super.connectedCallback(), window.addEventListener("route-begin", this.handleRouteBegin), window.addEventListener("route-done", this.handleRouteDone), window.addEventListener("route-progress", this.handleRouteProgress), window.addEventListener("route-error", this.handleRouteError), window.addEventListener("screen-resize", this.handleScreenResize);
@@ -90,7 +92,7 @@ var g = class extends i {
90
92
  super.willUpdate(e), e.has("config") && (this.styles = this.config?.styles);
91
93
  }
92
94
  render() {
93
- return this.config ? o`
95
+ return this.config ? s`
94
96
  <!-- Mobile Header -->
95
97
  <div class="mobile-header" part="mobile-header" ?hidden="${!this.state.startsWith("mobile")}">
96
98
  ${this.renderLogo()}
@@ -98,6 +100,7 @@ var g = class extends i {
98
100
  ${this.config.title}
99
101
  </span>
100
102
  <u-button class="toggler" variant="ghost"
103
+ aria-label=${a(this.locale || void 0).toggleMobileMenu}
101
104
  @click=${this.handleToggleButtonClick}>
102
105
  <u-icon
103
106
  lib="bootstrap"
@@ -115,6 +118,7 @@ var g = class extends i {
115
118
  ${this.config.title}
116
119
  </span>
117
120
  <u-button class="toggler" variant="ghost"
121
+ aria-label=${a(this.locale || void 0).toggleSidebar}
118
122
  @click=${this.handleToggleButtonClick}>
119
123
  <u-icon
120
124
  lib="bootstrap"
@@ -124,13 +128,14 @@ var g = class extends i {
124
128
  </div>
125
129
 
126
130
  <!-- Sidebar Navigation Menu -->
127
- <nav class="sidebar-main" part="sidebar-main" scrollable>
128
- ${p(t(this.config.main ?? [], this.config.hasPermission), (e, t) => t, (e) => this.renderItem(e))}
131
+ <nav class="sidebar-main" part="sidebar-main" scrollable
132
+ aria-label=${this.config.mainAriaLabel ?? c}>
133
+ ${m(t(this.config.main ?? [], this.config.hasPermission), (e, t) => t, (e) => this.renderItem(e))}
129
134
  </nav>
130
135
 
131
136
  <!-- Sidebar Footer -->
132
137
  <div class="sidebar-footer" part="sidebar-footer">
133
- ${p(t(this.config.footer ?? [], this.config.hasPermission), (e, t) => t, (e) => this.renderItem(e))}
138
+ ${m(t(this.config.footer ?? [], this.config.hasPermission), (e, t) => t, (e) => this.renderItem(e))}
134
139
  </div>
135
140
  </aside>
136
141
 
@@ -145,16 +150,17 @@ var g = class extends i {
145
150
  <div class="backdrop" ?hidden="${this.state !== "modal"}"
146
151
  @click="${this.handleBackdropClick}"
147
152
  ></div>
148
- ` : s;
153
+ ` : c;
149
154
  }
150
155
  renderItem(e) {
151
- if (!e) return s;
156
+ if (!e) return c;
152
157
  if (e.type === "html") {
153
158
  let t = e.render(this.state);
154
- return typeof t == "string" ? f(t) : o`${t}`;
159
+ return typeof t == "string" ? p(t) : s`${t}`;
155
160
  }
156
- if (e.type === "button") return o`
161
+ if (e.type === "button") return s`
157
162
  <u-sidebar-button
163
+ id=${h(e.id)}
158
164
  ?compact=${this.state === "slim"}
159
165
  .icon="${e.icon}"
160
166
  .lib="${e.lib}"
@@ -165,7 +171,7 @@ var g = class extends i {
165
171
  `;
166
172
  if (e.type === "link") {
167
173
  let t = this.isMatchedLink(e.pattern || e.href);
168
- return o`
174
+ return s`
169
175
  <u-sidebar-link
170
176
  ?compact=${this.state === "slim"}
171
177
  ?selected=${t}
@@ -180,18 +186,18 @@ var g = class extends i {
180
186
  ></u-sidebar-link>
181
187
  `;
182
188
  }
183
- if (e.type === "section") return o`
189
+ if (e.type === "section") return s`
184
190
  <u-sidebar-section
185
191
  ?compact=${this.state === "slim"}
186
192
  .mainTitle="${e.title}"
187
193
  .subTitle="${e.subTitle}"
188
194
  .styles="${e.styles}">
189
- ${p(e.items, (e, t) => t, (e) => this.renderItem(e))}
195
+ ${m(e.items, (e, t) => t, (e) => this.renderItem(e))}
190
196
  </u-sidebar-section>
191
197
  `;
192
198
  if (e.type === "group") {
193
199
  let t = e.items.some((e) => this.isMatchedLink(e.pattern || e.href));
194
- return o`
200
+ return s`
195
201
  <u-sidebar-group
196
202
  ?compact=${this.state === "slim"}
197
203
  ?selected=${t}
@@ -200,15 +206,15 @@ var g = class extends i {
200
206
  .lib="${e.lib}"
201
207
  .label="${e.label}"
202
208
  .styles="${e.styles}">
203
- ${p(e.items, (e, t) => t, (e) => this.renderItem(e))}
209
+ ${m(e.items, (e, t) => t, (e) => this.renderItem(e))}
204
210
  </u-sidebar-group>
205
211
  `;
206
212
  }
207
- return s;
213
+ return c;
208
214
  }
209
215
  renderLogo() {
210
216
  let e = this.config?.logo;
211
- if (!e || typeof e == "string") return o`
217
+ if (!e || typeof e == "string") return s`
212
218
  <u-icon class="logo"
213
219
  .name="${e}"
214
220
  @click=${this.handleBrandLogoClick()}
@@ -216,13 +222,13 @@ var g = class extends i {
216
222
  `;
217
223
  if (typeof e == "function") {
218
224
  let t = e(this.state);
219
- return o`
225
+ return s`
220
226
  <span class="logo" @click=${this.handleBrandLogoClick()}>
221
- ${typeof t == "string" ? f(t) : t}
227
+ ${typeof t == "string" ? p(t) : t}
222
228
  </span>
223
229
  `;
224
230
  }
225
- return o`
231
+ return s`
226
232
  <img class="logo"
227
233
  src="${e.src}"
228
234
  alt="${e.alt ?? ""}"
@@ -231,9 +237,9 @@ var g = class extends i {
231
237
  `;
232
238
  }
233
239
  };
234
- r([l({
240
+ r([u({
235
241
  type: String,
236
242
  reflect: !0
237
- }), n("design:type", Object)], g.prototype, "state", void 0), r([l({ type: Object }), n("design:type", Object)], g.prototype, "config", void 0), r([u("u-progress-bar"), n("design:type", m === void 0 ? Object : m)], g.prototype, "progressBarEl", void 0), r([d(), n("design:type", Object)], g.prototype, "context", void 0), g = r([c("u-sidebar-layout")], g);
243
+ }), n("design:type", Object)], v.prototype, "state", void 0), r([u({ type: Object }), n("design:type", Object)], v.prototype, "config", void 0), r([u({ type: String }), n("design:type", Object)], v.prototype, "locale", void 0), r([d("u-progress-bar"), n("design:type", g === void 0 ? Object : g)], v.prototype, "progressBarEl", void 0), r([f(), n("design:type", Object)], v.prototype, "context", void 0), v = r([l("u-sidebar-layout")], v);
238
244
  //#endregion
239
- export { g as SidebarLayout };
245
+ export { v as SidebarLayout };
@@ -42,6 +42,13 @@ export interface SidebarLayoutConfig {
42
42
  main?: SidebarItem[];
43
43
  /** 하단(footer)에 고정해서 렌더할 항목들 */
44
44
  footer?: SidebarItem[];
45
+ /**
46
+ * 메인 내비게이션(`<nav class="sidebar-main">`)의 접근 가능한 이름. 지정하면
47
+ * `aria-label`로 반영돼 스크린리더가 랜드마크를 "주 메뉴" 같은 이름으로 발표한다.
48
+ * `title`은 사이드바 헤더에 표시되는 브랜드 텍스트일 뿐 이 `<nav>`와 연결되지 않는다 —
49
+ * 별개 값이다. 미지정 시 종전과 동일하게 랜드마크 이름이 없다(breaking 아님).
50
+ */
51
+ mainAriaLabel?: string;
45
52
  /**
46
53
  * 메뉴 항목 권한 필터 판정. 지정하면 `requirePermission`/`requireAnyPermission` 를 만족하지
47
54
  * 않는 항목을 숨기고, 항목이 모두 걸러진 section/group 은 통째로 숨긴다.
package/dist/react.d.ts CHANGED
@@ -2,6 +2,8 @@ import { default as React } from 'react';
2
2
  import { EventName } from '@lit/react';
3
3
  import { SidebarLayout as SidebarLayoutElement } from './layouts/SidebarLayout.js';
4
4
  import { Wizard as WizardElement, WizardStepChangeDetail } from './components/Wizard.js';
5
+ export { ScreenObserver } from './internals/ScreenObserver.js';
6
+ export type { ScreenSize, ScreenObserverConfig, ScreenResizeEvent } from './internals/ScreenObserver.js';
5
7
  /**
6
8
  * React wrapper for `<u-sidebar-layout>`. `config` is an `@property({ type: Object })` —
7
9
  * a raw custom element handles that correctly under React 19 (property assignment) but not
package/dist/react.js CHANGED
@@ -1,18 +1,19 @@
1
- import { Wizard as e } from "./components/Wizard.js";
2
- import { SidebarLayout as t } from "./layouts/SidebarLayout.js";
3
- import n from "react";
4
- import { createComponent as r } from "@lit/react";
1
+ import { ScreenObserver as e } from "./internals/ScreenObserver.js";
2
+ import { Wizard as t } from "./components/Wizard.js";
3
+ import { SidebarLayout as n } from "./layouts/SidebarLayout.js";
4
+ import r from "react";
5
+ import { createComponent as i } from "@lit/react";
5
6
  //#region src/react.ts
6
- var i = r({
7
- react: n,
7
+ var a = i({
8
+ react: r,
8
9
  tagName: "u-sidebar-layout",
9
- elementClass: t,
10
+ elementClass: n,
10
11
  events: {}
11
- }), a = r({
12
- react: n,
12
+ }), o = i({
13
+ react: r,
13
14
  tagName: "u-wizard",
14
- elementClass: e,
15
+ elementClass: t,
15
16
  events: { onStepChange: "step-change" }
16
17
  });
17
18
  //#endregion
18
- export { i as SidebarLayout, a as Wizard };
19
+ export { e as ScreenObserver, a as SidebarLayout, o as Wizard };
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.17.0",
4
+ "version": "0.18.2",
5
5
  "keywords": [
6
6
  "iyulab",
7
7
  "web-framework",
@@ -39,7 +39,7 @@ and those are different states of the business. So the component owns the rule.
39
39
  | `size` | `'default'\|'lg'` | `'default'` | ✓ | `'lg'` renders the value at the `title` type-scale step — for dashboard KPI tiles composed inside `u-info-section` |
40
40
  | `trend` | `'up'\|'down'\|'flat'` | — | | Trend direction; renders a trend indicator when set, alongside `trendLabel`; pair with trendLabel for an accessible name — trend alone conveys direction by color only |
41
41
  | `trendLabel` | `string` | — | | Trend copy, e.g. `"+12% vs last month"` — wording is the consumer's responsibility |
42
- | `tone` | `'positive'\|'negative'\|'neutral'` | — | | Explicit tone override; resolves from `trend` when unset (`up→positive`, `down→negative`, `flat`/unset→`neutral`) but always wins over inference |
42
+ | `tone` | `'positive'\|'negative'\|'neutral'` | — | | Explicit tone override; resolves from `trend` when unset (`up→positive`, `down→negative`, `flat`/unset→`neutral`) but always wins over inference. Colors the value text itself, independent of `trend` — a static figure (e.g. a balance due) can be toned `negative` with no trend arrow |
43
43
 
44
44
  ⚠ `numeric` earns its keep in **tables**, where columns of figures are compared by eye. On a
45
45
  lone field it just pushes the value away from its label.