@iyulab/modern-app 0.16.0 → 0.18.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,42 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.18.0] - 2026-08-17
4
+
5
+ ### Added
6
+
7
+ - **`SidebarLink` now sets `aria-current="page"` on its inner `<u-link>` when `selected` is
8
+ true.** Previously `selected` only drove visual emphasis (a host attribute for CSS) — a
9
+ screen-reader user had no way to tell which item was the current page.
10
+ - **`SidebarLayoutConfig.mainAriaLabel?: string`.** When set, it's reflected as `aria-label`
11
+ on the sidebar's main `<nav>` landmark. That `<nav>` lives inside `SidebarLayout`'s shadow
12
+ root, so a consumer had no way to name it from the outside. Unset behaves exactly as
13
+ before (no landmark name) — not a breaking change.
14
+ - **`InfoField`'s `tone` now colors the value text itself, not just the trend indicator.**
15
+ Previously `.trend.tone-*` was the only CSS surface for `tone`, so a static figure shown
16
+ without a `trend` (e.g. a balance due) couldn't be toned at all. `trend`'s existing
17
+ behavior is unchanged; this only widens where `tone` is visible.
18
+ - **`ScreenObserver` is now exported from both the main entry and `/react`.** `SidebarLayout`'s
19
+ responsive state (large→slim, medium→modal, small→mobile) depends entirely on the
20
+ `screen-resize` event this class dispatches; `app.load()` instantiates it internally, but a
21
+ consumer mounting `SidebarLayout` standalone through `/react` (without `app.load()`) had no
22
+ public way to drive that behavior.
23
+
24
+ ## [0.17.0] - 2026-08-11
25
+
26
+ ### Added
27
+
28
+ - **`/react` subpath**, wrapping `SidebarLayout` and `Wizard` with `@lit/react`'s
29
+ `createComponent`. `Wizard` is the only component that dispatches a custom event
30
+ (`step-change`) — a raw custom element in JSX maps an `onStepChange` prop to a listener
31
+ for a literal `"StepChange"` DOM event, which `Wizard` never fires, so the event never
32
+ reaches a consumer without this wrapper. Both components also carry object/array
33
+ properties (`config`, `steps`) that a raw custom element handles correctly under React 19
34
+ (which assigns matching properties directly) but not under React 18 (where non-primitive
35
+ JSX attributes stringify) — the wrapper keeps that working on both. Every component in
36
+ this package (not just these two) also inherits an object-typed `styles` property from its
37
+ base class for part-level style overrides; on React 18 that still needs a ref without a
38
+ wrapper.
39
+
3
40
  ## [0.16.0] - 2026-08-11
4
41
 
5
42
  ### Changed
package/README.md CHANGED
@@ -173,6 +173,38 @@ await app.load({
173
173
  - `hasPermission` 미지정 시 필터링하지 않는다(모든 항목 표시 — 하위호환).
174
174
  - 순수 헬퍼 `filterSidebarItems(items, hasPermission)` 를 직접 재사용할 수도 있다.
175
175
 
176
+ ## React
177
+
178
+ A `@lit/react`-based wrapper for `SidebarLayout` and `Wizard` is available under `/react`.
179
+ Every other exported component works as a plain custom element in JSX; these two specifically
180
+ need the wrapper because `Wizard` dispatches a `step-change` custom event (a raw element maps
181
+ `onStepChange` to a literal `"StepChange"` DOM event it never fires) and both carry object/array
182
+ properties that a raw element only handles correctly under React 19.
183
+
184
+ `@lit/react` and `react` are optional peer dependencies — install them to use this subpath:
185
+
186
+ ```bash
187
+ npm install @iyulab/modern-app @lit/react react
188
+ ```
189
+
190
+ ```tsx
191
+ import { SidebarLayout, Wizard, type SidebarItem, type WizardStep } from '@iyulab/modern-app/react';
192
+
193
+ const main: SidebarItem[] = [{ type: 'link', label: 'Home', href: '/' }];
194
+ const steps: WizardStep[] = [{ id: 'info', label: 'Info' }, { id: 'review', label: 'Review' }];
195
+
196
+ export function App() {
197
+ return (
198
+ <SidebarLayout config={{ type: 'sidebar', title: 'My App', main }}>
199
+ <Wizard steps={steps} active={0} onStepChange={(e) => console.log(e.detail)}>
200
+ <section>Info panel</section>
201
+ <section>Review panel</section>
202
+ </Wizard>
203
+ </SidebarLayout>
204
+ );
205
+ }
206
+ ```
207
+
176
208
  ## Documentation
177
209
 
178
210
  | Guide | Description |
@@ -1,4 +1,4 @@
1
- //#region \0@oxc-project+runtime@0.143.0/helpers/esm/decorate.js
1
+ //#region \0@oxc-project+runtime@0.144.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.144.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.144.0/helpers/esm/decorateMetadata.js";
2
+ import t from "../_virtual/_@oxc-project_runtime@0.144.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.144.0/helpers/esm/decorateMetadata.js";
2
+ import t from "../_virtual/_@oxc-project_runtime@0.144.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.144.0/helpers/esm/decorateMetadata.js";
2
+ import t from "../_virtual/_@oxc-project_runtime@0.144.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";
@@ -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,10 +1,10 @@
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.144.0/helpers/esm/decorateMetadata.js";
2
+ import t from "../_virtual/_@oxc-project_runtime@0.144.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";
6
6
  import { customElement as a, property as o } from "lit/decorators.js";
7
- import { formatCurrency as s, formatDate as c, formatNumber as l } from "@iyulab/components";
7
+ import { formatCurrency as s, formatDate as c, formatNumber as l } from "@iyulab/components/dist/utilities/format.js";
8
8
  //#region src/components/InfoField.ts
9
9
  function u(e) {
10
10
  return e === "up" ? "positive" : e === "down" ? "negative" : "neutral";
@@ -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.144.0/helpers/esm/decorateMetadata.js";
2
+ import t from "../_virtual/_@oxc-project_runtime@0.144.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.144.0/helpers/esm/decorateMetadata.js";
2
+ import t from "../_virtual/_@oxc-project_runtime@0.144.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.144.0/helpers/esm/decorateMetadata.js";
2
+ import t from "../_virtual/_@oxc-project_runtime@0.144.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.144.0/helpers/esm/decorateMetadata.js";
2
+ import t from "../_virtual/_@oxc-project_runtime@0.144.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.144.0/helpers/esm/decorateMetadata.js";
2
+ import t from "../_virtual/_@oxc-project_runtime@0.144.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.144.0/helpers/esm/decorateMetadata.js";
2
+ import t from "../_virtual/_@oxc-project_runtime@0.144.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.144.0/helpers/esm/decorateMetadata.js";
2
+ import t from "../_virtual/_@oxc-project_runtime@0.144.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.144.0/helpers/esm/decorateMetadata.js";
2
+ import t from "../_virtual/_@oxc-project_runtime@0.144.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.144.0/helpers/esm/decorateMetadata.js";
2
+ import t from "../_virtual/_@oxc-project_runtime@0.144.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
@@ -15,7 +15,7 @@ export declare class SidebarLayout extends StyledElement<SidebarParts> {
15
15
  connectedCallback(): void;
16
16
  disconnectedCallback(): void;
17
17
  protected willUpdate(changedProperties: PropertyValues): void;
18
- render(): import('lit-html').TemplateResult<1> | typeof nothing;
18
+ render(): typeof nothing | import('lit-html').TemplateResult<1>;
19
19
  /** 사이드바 아이템 렌더링 */
20
20
  private renderItem;
21
21
  /** 현재 경로와 패턴 매칭 여부 확인 */
@@ -1,7 +1,7 @@
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.144.0/helpers/esm/decorateMetadata.js";
4
+ import r from "../_virtual/_@oxc-project_runtime@0.144.0/helpers/esm/decorate.js";
5
5
  import { StyledElement as i } from "../internals/StyledElement.js";
6
6
  import "../components/SidebarSection.js";
7
7
  import "../components/SidebarLink.js";
@@ -124,7 +124,8 @@ var g = class extends i {
124
124
  </div>
125
125
 
126
126
  <!-- Sidebar Navigation Menu -->
127
- <nav class="sidebar-main" part="sidebar-main" scrollable>
127
+ <nav class="sidebar-main" part="sidebar-main" scrollable
128
+ aria-label=${this.config.mainAriaLabel ?? s}>
128
129
  ${p(t(this.config.main ?? [], this.config.hasPermission), (e, t) => t, (e) => this.renderItem(e))}
129
130
  </nav>
130
131
 
@@ -6,6 +6,10 @@ import { SidebarGroupConfig } from '../components/SidebarGroup';
6
6
  import { SidebarButtonConfig } from '../components/SidebarButton';
7
7
  import { SidebarPermissionGuard } from './SidebarPermission';
8
8
  export type { SidebarPermissionGuard } from './SidebarPermission';
9
+ export type { SidebarLinkConfig } from '../components/SidebarLink';
10
+ export type { SidebarSectionConfig } from '../components/SidebarSection';
11
+ export type { SidebarGroupConfig } from '../components/SidebarGroup';
12
+ export type { SidebarButtonConfig } from '../components/SidebarButton';
9
13
  /** 사이드바 레이아웃 컴포넌트의 요소(part) 타입 */
10
14
  export type SidebarParts = 'host' | 'mobile-header' | 'sidebar' | 'sidebar-header' | 'sidebar-main' | 'sidebar-footer' | 'main' | 'progress';
11
15
  /** 사이드바 상태 타입 */
@@ -38,6 +42,13 @@ export interface SidebarLayoutConfig {
38
42
  main?: SidebarItem[];
39
43
  /** 하단(footer)에 고정해서 렌더할 항목들 */
40
44
  footer?: SidebarItem[];
45
+ /**
46
+ * 메인 내비게이션(`<nav class="sidebar-main">`)의 접근 가능한 이름. 지정하면
47
+ * `aria-label`로 반영돼 스크린리더가 랜드마크를 "주 메뉴" 같은 이름으로 발표한다.
48
+ * `title`은 사이드바 헤더에 표시되는 브랜드 텍스트일 뿐 이 `<nav>`와 연결되지 않는다 —
49
+ * 별개 값이다. 미지정 시 종전과 동일하게 랜드마크 이름이 없다(breaking 아님).
50
+ */
51
+ mainAriaLabel?: string;
41
52
  /**
42
53
  * 메뉴 항목 권한 필터 판정. 지정하면 `requirePermission`/`requireAnyPermission` 를 만족하지
43
54
  * 않는 항목을 숨기고, 항목이 모두 걸러진 section/group 은 통째로 숨긴다.
@@ -0,0 +1,27 @@
1
+ import { default as React } from 'react';
2
+ import { EventName } from '@lit/react';
3
+ import { SidebarLayout as SidebarLayoutElement } from './layouts/SidebarLayout.js';
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';
7
+ /**
8
+ * React wrapper for `<u-sidebar-layout>`. `config` is an `@property({ type: Object })` —
9
+ * a raw custom element handles that correctly under React 19 (property assignment) but not
10
+ * React 18 (JSX attributes stringify), so this wrapper keeps `config` working on both.
11
+ */
12
+ export declare const SidebarLayout: import('@lit/react').ReactWebComponent<SidebarLayoutElement, {}>;
13
+ export type SidebarLayoutProps = React.ComponentProps<typeof SidebarLayout>;
14
+ /**
15
+ * React wrapper for `<u-wizard>`. `Wizard` dispatches a `step-change` custom event, but a raw
16
+ * custom element in JSX maps `onStepChange` to a listener for a literal `"StepChange"` DOM
17
+ * event — one `Wizard` never fires — so the event never reaches a consumer without this
18
+ * wrapper's `events` mapping. `steps` is also an `@property({ type: Array })`, which the
19
+ * wrapper keeps working on React 18 the same way it does for `SidebarLayout`'s `config`.
20
+ */
21
+ export declare const Wizard: import('@lit/react').ReactWebComponent<WizardElement, {
22
+ onStepChange: EventName<CustomEvent<WizardStepChangeDetail>>;
23
+ }>;
24
+ export type WizardProps = React.ComponentProps<typeof Wizard>;
25
+ export type { SidebarLayoutElement, WizardElement };
26
+ export type { SidebarLayoutConfig, SidebarItem, SidebarLogoConfig, SidebarLinkConfig, SidebarSectionConfig, SidebarGroupConfig, SidebarButtonConfig, SidebarHtmlConfig, } from './layouts/SidebarLayout.types.js';
27
+ export type { WizardStep, WizardStepState, WizardStepChangeDetail } from './components/Wizard.js';
package/dist/react.js ADDED
@@ -0,0 +1,19 @@
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";
6
+ //#region src/react.ts
7
+ var a = i({
8
+ react: r,
9
+ tagName: "u-sidebar-layout",
10
+ elementClass: n,
11
+ events: {}
12
+ }), o = i({
13
+ react: r,
14
+ tagName: "u-wizard",
15
+ elementClass: t,
16
+ events: { onStepChange: "step-change" }
17
+ });
18
+ //#endregion
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.16.0",
4
+ "version": "0.18.0",
5
5
  "keywords": [
6
6
  "iyulab",
7
7
  "web-framework",
@@ -32,13 +32,19 @@
32
32
  "./dist/index.js",
33
33
  "./src/index.ts",
34
34
  "./dist/layouts/*.js",
35
- "./src/layouts/*.ts"
35
+ "./src/layouts/*.ts",
36
+ "./dist/react.js",
37
+ "./src/react.ts"
36
38
  ],
37
39
  "exports": {
38
40
  ".": {
39
41
  "types": "./dist/index.d.ts",
40
42
  "import": "./dist/index.js"
41
43
  },
44
+ "./react": {
45
+ "types": "./dist/react.d.ts",
46
+ "import": "./dist/react.js"
47
+ },
42
48
  "./dist/*": {
43
49
  "types": "./dist/*.d.ts",
44
50
  "import": "./dist/*"
@@ -58,10 +64,27 @@
58
64
  "i18next": "^26.3.6",
59
65
  "lit": "^3.3.3"
60
66
  },
67
+ "peerDependencies": {
68
+ "@lit/react": ">=1.0.8",
69
+ "react": ">=18.0.0"
70
+ },
71
+ "peerDependenciesMeta": {
72
+ "@lit/react": {
73
+ "optional": true
74
+ },
75
+ "react": {
76
+ "optional": true
77
+ }
78
+ },
61
79
  "devDependencies": {
80
+ "@lit/react": "^1.0.8",
62
81
  "@types/node": "^26.1.1",
82
+ "@types/react": "^19.2.14",
83
+ "@types/react-dom": "^19.2.3",
63
84
  "@vitest/browser-playwright": "^4.1.10",
64
85
  "happy-dom": "^20.10.6",
86
+ "react": "^19.2.7",
87
+ "react-dom": "^19.2.7",
65
88
  "typescript": "^5.9.3",
66
89
  "vite": "^8.1.4",
67
90
  "vite-plugin-dts": "^5.0.3",
@@ -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.