@iyulab/modern-app 0.56.2 → 0.58.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.
Files changed (54) hide show
  1. package/CHANGELOG.md +46 -0
  2. package/dist/App.js +1 -2
  3. package/dist/_virtual/{_@oxc-project_runtime@0.154.0 → _@oxc-project_runtime@0.155.0}/helpers/esm/decorate.js +1 -2
  4. package/dist/_virtual/{_@oxc-project_runtime@0.154.0 → _@oxc-project_runtime@0.155.0}/helpers/esm/decorateMetadata.js +1 -2
  5. package/dist/components/ActionBar.js +3 -4
  6. package/dist/components/ActionBar.styles.js +1 -2
  7. package/dist/components/EmptyState.js +3 -4
  8. package/dist/components/EmptyState.styles.js +1 -2
  9. package/dist/components/GroupBox.js +3 -4
  10. package/dist/components/GroupBox.styles.js +1 -2
  11. package/dist/components/InfoField.d.ts +5 -0
  12. package/dist/components/InfoField.js +25 -19
  13. package/dist/components/InfoField.styles.js +20 -2
  14. package/dist/components/InfoSection.js +3 -4
  15. package/dist/components/InfoSection.styles.js +1 -2
  16. package/dist/components/MasterDetailLayout.js +3 -4
  17. package/dist/components/MasterDetailLayout.styles.js +1 -2
  18. package/dist/components/PageHeader.js +3 -4
  19. package/dist/components/PageHeader.styles.js +1 -2
  20. package/dist/components/SidebarButton.js +3 -4
  21. package/dist/components/SidebarButton.styles.js +1 -2
  22. package/dist/components/SidebarGroup.d.ts +9 -0
  23. package/dist/components/SidebarGroup.js +17 -14
  24. package/dist/components/SidebarGroup.styles.js +1 -2
  25. package/dist/components/SidebarLink.d.ts +6 -0
  26. package/dist/components/SidebarLink.js +16 -14
  27. package/dist/components/SidebarLink.styles.js +2 -3
  28. package/dist/components/SidebarSection.js +3 -4
  29. package/dist/components/SidebarSection.styles.js +1 -2
  30. package/dist/components/Timeline.js +2 -3
  31. package/dist/components/Timeline.styles.js +1 -2
  32. package/dist/components/TimelineItem.js +3 -4
  33. package/dist/components/TimelineItem.styles.js +1 -2
  34. package/dist/components/Wizard.js +3 -4
  35. package/dist/components/Wizard.styles.js +1 -2
  36. package/dist/defaultFallback.js +1 -2
  37. package/dist/index.d.ts +1 -1
  38. package/dist/index.js +1 -2
  39. package/dist/internals/ScreenObserver.js +1 -2
  40. package/dist/internals/StyledElement.js +3 -4
  41. package/dist/internals/locale.js +1 -2
  42. package/dist/internals/nav-icon.js +1 -2
  43. package/dist/internals/slotted.js +1 -2
  44. package/dist/layouts/SidebarLayout.d.ts +18 -0
  45. package/dist/layouts/SidebarLayout.js +100 -76
  46. package/dist/layouts/SidebarLayout.styles.js +41 -7
  47. package/dist/layouts/SidebarLayout.types.d.ts +16 -2
  48. package/dist/layouts/filterSidebarItems.js +1 -2
  49. package/dist/react.js +1 -2
  50. package/dist/translate.js +0 -1
  51. package/dist/unsaved.js +1 -2
  52. package/package.json +3 -3
  53. package/skills/modern-app/references/components/info-field.md +22 -0
  54. package/skills/modern-app/references/layout.md +29 -2
@@ -1,5 +1,5 @@
1
- import e from "../_virtual/_@oxc-project_runtime@0.154.0/helpers/esm/decorateMetadata.js";
2
- import t from "../_virtual/_@oxc-project_runtime@0.154.0/helpers/esm/decorate.js";
1
+ import e from "../_virtual/_@oxc-project_runtime@0.155.0/helpers/esm/decorateMetadata.js";
2
+ import t from "../_virtual/_@oxc-project_runtime@0.155.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 { getLocaleStrings as i } from "../internals/locale.js";
@@ -12,28 +12,29 @@ import "../components/SidebarGroup.js";
12
12
  import "../components/SidebarButton.js";
13
13
  import { styles as c } from "./SidebarLayout.styles.js";
14
14
  import { currentRoutePath as l } from "@iyulab/router";
15
- import { html as u, nothing as d } from "lit";
16
- import { customElement as f, property as p, query as m, state as h } from "lit/decorators.js";
15
+ import { Theme as u } from "@iyulab/components/dist/utilities/Theme.js";
16
+ import { html as d, nothing as f } from "lit";
17
+ import { customElement as p, property as m, query as h, state as g } from "lit/decorators.js";
17
18
  import "@iyulab/components/dist/components/icon/UIcon.js";
18
19
  import "@iyulab/components/dist/components/button/UButton.js";
19
- import { unsafeHTML as g } from "lit/directives/unsafe-html.js";
20
- import { repeat as _ } from "lit/directives/repeat.js";
20
+ import { unsafeHTML as _ } from "lit/directives/unsafe-html.js";
21
+ import { repeat as v } from "lit/directives/repeat.js";
21
22
  import "@iyulab/components/dist/components/popover/UPopover.js";
22
23
  import "@iyulab/components/dist/components/menu/UMenu.js";
23
24
  import "@iyulab/components/dist/components/menu-item/UMenuItem.js";
24
- import { createDevWarner as v } from "@iyulab/components/dist/utilities/devWarning.js";
25
- import { isFocusCandidate as y, querySelectorDeep as b } from "@iyulab/components/dist/utilities/elements.js";
26
- import { OverlayManager as x } from "@iyulab/components/dist/utilities/OverlayManager.js";
25
+ import { createDevWarner as y } from "@iyulab/components/dist/utilities/devWarning.js";
26
+ import { isFocusCandidate as b, querySelectorDeep as x } from "@iyulab/components/dist/utilities/elements.js";
27
+ import { OverlayManager as S } from "@iyulab/components/dist/utilities/OverlayManager.js";
27
28
  import "@iyulab/components/dist/components/progress-bar/UProgressBar.js";
28
- //#region src/layouts/SidebarLayout.ts
29
- var S, C = v("@iyulab/modern-app"), w = 0;
30
- function T(e) {
29
+ // src/layouts/SidebarLayout.ts
30
+ var C, w = y("@iyulab/modern-app"), T = 0;
31
+ function E(e) {
31
32
  let t = e.tagName;
32
33
  if (t === "INPUT" || t === "TEXTAREA" || t === "SELECT" || e.isContentEditable) return !0;
33
34
  let n = e.getAttribute("role");
34
35
  return n === "textbox" || n === "searchbox" || n === "combobox" || n === "spinbutton";
35
36
  }
36
- var E = /* @__PURE__ */ new Set([
37
+ var D = /* @__PURE__ */ new Set([
37
38
  "button",
38
39
  "link",
39
40
  "checkbox",
@@ -46,27 +47,27 @@ var E = /* @__PURE__ */ new Set([
46
47
  "tab",
47
48
  "treeitem"
48
49
  ]);
49
- function D(e) {
50
+ function O(e) {
50
51
  let t = e.tagName;
51
- return t === "BUTTON" || t === "SUMMARY" || t === "A" && e.hasAttribute("href") ? !0 : E.has(e.getAttribute("role") ?? "");
52
+ return t === "BUTTON" || t === "SUMMARY" || t === "A" && e.hasAttribute("href") ? !0 : D.has(e.getAttribute("role") ?? "");
52
53
  }
53
- function O(e) {
54
+ function k(e) {
54
55
  let t = getComputedStyle(e);
55
56
  return (t.overflowY === "auto" || t.overflowY === "scroll") && e.scrollHeight > e.clientHeight;
56
57
  }
57
- function k(e, t) {
58
+ function A(e, t) {
58
59
  for (let n of e) if (n instanceof HTMLElement) {
59
- if (O(n)) return n === t;
60
+ if (k(n)) return n === t;
60
61
  if (n === t) break;
61
62
  }
62
63
  return !0;
63
64
  }
64
- function A() {
65
+ function j() {
65
66
  let e = document.activeElement;
66
67
  for (; e?.shadowRoot?.activeElement;) e = e.shadowRoot.activeElement;
67
68
  return e;
68
69
  }
69
- function j(e, t) {
70
+ function M(e, t) {
70
71
  let n = t;
71
72
  for (; n;) {
72
73
  if (n === e) return !0;
@@ -74,12 +75,12 @@ function j(e, t) {
74
75
  }
75
76
  return !1;
76
77
  }
77
- var M = class extends n {
78
+ var N = class extends n {
78
79
  static {
79
- S = this;
80
+ C = this;
80
81
  }
81
82
  constructor(...e) {
82
- super(...e), this.state = "default", this.counts = {}, this.locale = "", this.hasOverlay = !1, this.openMenu = null, this.menuIds = /* @__PURE__ */ new WeakMap(), this.hasNotice = !1, this.routeLoading = !1, this.mainTabStop = !1, this.overlayTrigger = null, this.internals = typeof this.attachInternals == "function" ? this.attachInternals() : void 0, this.currentPath = null, this.unsizedWarnKey = `unsized:${++w}`, this.isMatchedLink = (e) => this.currentPath === null || !e ? !1 : (e = typeof e == "string" ? new URLPattern(e, window.location.origin) : e, e.test(this.currentPath, window.location.origin)), this.handleToggleButtonClick = () => {
83
+ super(...e), this.state = "default", this.immersive = !1, this.routeTheme = !1, this.counts = {}, this.locale = "", this.hasOverlay = !1, this.openMenu = null, this.menuIds = /* @__PURE__ */ new WeakMap(), this.hasNotice = !1, this.routeLoading = !1, this.mainTabStop = !1, this.mobileHeaderBlockSize = 0, this.overlayTrigger = null, this.internals = typeof this.attachInternals == "function" ? this.attachInternals() : void 0, this.currentPath = null, this.unsizedWarnKey = `unsized:${++T}`, this.isMatchedLink = (e) => this.currentPath === null || !e ? !1 : (e = typeof e == "string" ? new URLPattern(e, window.location.origin) : e, e.test(this.currentPath, window.location.origin)), this.handleToggleButtonClick = () => {
83
84
  let e = this.screenSize ?? a.screen ?? "large";
84
85
  e === "large" ? this.state = this.state === "default" ? "slim" : "default" : e === "medium" ? this.state = this.state === "slim" ? "modal" : "slim" : e === "small" ? this.state = this.state === "mobile" ? "mobile-open" : "mobile" : console.warn("[@iyulab/modern-app] unknown screen size:", e);
85
86
  }, this.handleBackdropClick = () => {
@@ -89,17 +90,19 @@ var M = class extends n {
89
90
  }, this.handleRouteProgress = (e) => {
90
91
  this.progressBarEl.value = e.progress;
91
92
  }, this.handleRouteDone = (e) => {
92
- this.progressBarEl.value = 100, setTimeout(() => {
93
+ this.immersive = e.context.metadata?.shell === "hidden";
94
+ let t = e.context.metadata?.theme, n = t === "dark" || t === "light" ? t : null;
95
+ (n || this.routeTheme) && u.override(n), this.routeTheme = n !== null, this.progressBarEl.value = 100, setTimeout(() => {
93
96
  this.routeLoading = !1;
94
97
  }, 300);
95
- let t = this.shadowRoot?.querySelector(".main");
96
- t && (this.config?.scrollBehavior?.(e.context, t), this.focusRoute(t), requestAnimationFrame(this.syncMainTabStop));
98
+ let r = this.shadowRoot?.querySelector(".main");
99
+ r && (this.config?.scrollBehavior?.(e.context, r), this.focusRoute(r), requestAnimationFrame(this.syncMainTabStop));
97
100
  }, this.observedContent = [], this.handleMainSlotChange = () => {
98
101
  this.observeContentSize(), this.syncMainTabStop();
99
102
  }, this.syncMainTabStop = () => {
100
103
  let e = this.shadowRoot?.querySelector(".main");
101
104
  if (!e) return;
102
- let t = e.scrollHeight > e.clientHeight + 1 || e.scrollWidth > e.clientWidth + 1, n = t && !!b([...this.noticeContent(), ...this.routeContent()], "*", (e) => y(e) && e.tabIndex >= 0);
105
+ let t = e.scrollHeight > e.clientHeight + 1 || e.scrollWidth > e.clientWidth + 1, n = t && !!x([...this.noticeContent(), ...this.routeContent()], "*", (e) => b(e) && e.tabIndex >= 0);
103
106
  this.mainTabStop = t && !n;
104
107
  }, this.handleSkipLinkClick = (e) => {
105
108
  e.preventDefault();
@@ -112,7 +115,7 @@ var M = class extends n {
112
115
  }, this.handleOverlaySlotChange = (e) => {
113
116
  let t = r(e.target);
114
117
  if (t && !this.hasOverlay) {
115
- let e = A();
118
+ let e = j();
116
119
  this.overlayTrigger = e && e !== document.body ? e : null;
117
120
  }
118
121
  this.hasOverlay = t;
@@ -126,9 +129,9 @@ var M = class extends n {
126
129
  }, this._handleMainKeydown = (e) => {
127
130
  if (e.defaultPrevented) return;
128
131
  let t = e.composedPath()[0];
129
- if (t instanceof HTMLElement && T(t) || e.key === " " && t instanceof HTMLElement && D(t)) return;
132
+ if (t instanceof HTMLElement && E(t) || e.key === " " && t instanceof HTMLElement && O(t)) return;
130
133
  let n = this.shadowRoot?.querySelector(".main");
131
- if (!n || !k(e.composedPath(), n)) return;
134
+ if (!n || !A(e.composedPath(), n)) return;
132
135
  let r = n.clientHeight;
133
136
  switch (e.key) {
134
137
  case " ":
@@ -171,13 +174,13 @@ var M = class extends n {
171
174
  super.connectedCallback(), this.currentPath = l(), 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);
172
175
  }
173
176
  disconnectedCallback() {
174
- window.removeEventListener("route-begin", this.handleRouteBegin), window.removeEventListener("route-done", this.handleRouteDone), window.removeEventListener("route-progress", this.handleRouteProgress), window.removeEventListener("route-error", this.handleRouteError), window.removeEventListener("screen-resize", this.handleScreenResize), x.closeLayer(this), this.mainSizeObserver?.disconnect(), this.mainSizeObserver = void 0, super.disconnectedCallback();
177
+ this.routeTheme &&= (u.override(null), !1), window.removeEventListener("route-begin", this.handleRouteBegin), window.removeEventListener("route-done", this.handleRouteDone), window.removeEventListener("route-progress", this.handleRouteProgress), window.removeEventListener("route-error", this.handleRouteError), window.removeEventListener("screen-resize", this.handleScreenResize), S.closeLayer(this), this.mainSizeObserver?.disconnect(), this.mainSizeObserver = void 0, this.mobileHeaderObserver?.disconnect(), this.mobileHeaderObserver = void 0, super.disconnectedCallback();
175
178
  }
176
179
  willUpdate(e) {
177
180
  super.willUpdate(e), e.has("config") && (this.styles = this.config?.styles);
178
181
  }
179
182
  updated(e) {
180
- super.updated(e), this.observeMainSize(), e.has("hasOverlay") && (this.internals?.states?.[this.hasOverlay ? "add" : "delete"]("overlay"), this.hasOverlay ? (x.openLayer(this, this.handleOverlayEscape), this.focusOverlay()) : e.get("hasOverlay") === !0 && (x.closeLayer(this), this.restoreOverlayFocus()));
183
+ super.updated(e), this.observeMainSize(), this.observeMobileHeader(), e.has("hasOverlay") && (this.internals?.states?.[this.hasOverlay ? "add" : "delete"]("overlay"), this.hasOverlay ? (S.openLayer(this, this.handleOverlayEscape), this.focusOverlay()) : e.get("hasOverlay") === !0 && (S.closeLayer(this), this.restoreOverlayFocus()));
181
184
  }
182
185
  firstUpdated(e) {
183
186
  super.firstUpdated(e), this.warnIfUnsized();
@@ -186,11 +189,11 @@ var M = class extends n {
186
189
  process.env.NODE_ENV !== "production" && requestAnimationFrame(() => {
187
190
  if (!this.isConnected) return;
188
191
  let e = this.getBoundingClientRect().height;
189
- e >= 200 || C(this.unsizedWarnKey, `u-sidebar-layout is only ${Math.round(e)}px tall — its height: 100% found no sized ancestor, so the shell sits at its own chrome height and the route area has almost no room. Give the root element a height (e.g. #app { height: 100vh } — app.load() does this for document.body).`);
192
+ e >= 200 || w(this.unsizedWarnKey, `u-sidebar-layout is only ${Math.round(e)}px tall — its height: 100% found no sized ancestor, so the shell sits at its own chrome height and the route area has almost no room. Give the root element a height (e.g. #app { height: 100vh } — app.load() does this for document.body).`);
190
193
  });
191
194
  }
192
195
  render() {
193
- return this.config ? u`
196
+ return this.config ? d`
194
197
  <!-- 반복 영역 건너뛰기(WCAG 2.4.1) — 셸의 첫 Tab. 포커스될 때만 보인다. -->
195
198
  <a class="skip-link" part="skip-link" href="#" @click=${this.handleSkipLinkClick}>
196
199
  ${i(this.locale || void 0).skipToContent}
@@ -213,12 +216,14 @@ var M = class extends n {
213
216
  </div>
214
217
 
215
218
  <!-- Sidebar -->
216
- <aside class="sidebar" part="sidebar" state="${this.state}">
219
+ <aside class="sidebar" part="sidebar" state="${this.state}"
220
+ style="--_mobile-header-block-size: ${this.mobileHeaderBlockSize}px">
217
221
  <!-- Sidebar Header -->
218
222
  <div class="sidebar-header" part="sidebar-header">
219
223
  ${this.renderLogo()}
220
224
  <span class="title" ?hidden=${this.state === "slim"}>
221
225
  ${this.config.title}
226
+ ${this.config.subtitle ? d`<span class="subtitle" part="subtitle">${this.config.subtitle}</span>` : f}
222
227
  </span>
223
228
  <u-button class="toggler" appearance="plain"
224
229
  aria-label=${i(this.locale || void 0).toggleSidebar}
@@ -232,13 +237,13 @@ var M = class extends n {
232
237
 
233
238
  <!-- Sidebar Navigation Menu -->
234
239
  <nav class="sidebar-main" part="sidebar-main" scrollable
235
- aria-label=${this.config.mainAriaLabel ?? d}>
236
- ${_(o(this.config.main ?? [], this.config.hasPermission), (e, t) => t, (e) => this.renderItem(e))}
240
+ aria-label=${this.config.mainAriaLabel ?? f}>
241
+ ${v(o(this.config.main ?? [], this.config.hasPermission), (e, t) => t, (e) => this.renderItem(e))}
237
242
  </nav>
238
243
 
239
244
  <!-- Sidebar Footer -->
240
245
  <div class="sidebar-footer" part="sidebar-footer">
241
- ${_(o(this.config.footer ?? [], this.config.hasPermission), (e, t) => t, (e) => this.renderItem(e))}
246
+ ${v(o(this.config.footer ?? [], this.config.hasPermission), (e, t) => t, (e) => this.renderItem(e))}
242
247
  </div>
243
248
  </aside>
244
249
 
@@ -246,13 +251,13 @@ var M = class extends n {
246
251
  <div class="main-region">
247
252
  <main class="main" part="main" scrollable tabindex=${this.mainTabStop ? "0" : "-1"} @keydown=${this._handleMainKeydown}>
248
253
  <u-progress-bar part="progress" class=${this.routeLoading ? "loading" : ""}
249
- aria-hidden=${this.routeLoading ? d : "true"}
254
+ aria-hidden=${this.routeLoading ? f : "true"}
250
255
  aria-label=${i(this.locale || void 0).pageLoading}></u-progress-bar>
251
256
 
252
257
  <div class="main-content" part="main-content" ?inert=${this.hasOverlay}>
253
258
  <div class="notices ${this.hasNotice ? "" : "empty"}" part="notices"
254
- role=${this.config.noticesAriaLabel ? "region" : d}
255
- aria-label=${this.config.noticesAriaLabel ?? d}>
259
+ role=${this.config.noticesAriaLabel ? "region" : f}
260
+ aria-label=${this.config.noticesAriaLabel ?? f}>
256
261
  <slot name="notice" @slotchange=${this.handleNoticeSlotChange}></slot>
257
262
  </div>
258
263
  <slot @slotchange=${this.handleMainSlotChange}></slot>
@@ -273,13 +278,13 @@ var M = class extends n {
273
278
  <div class="backdrop" ?hidden="${this.state !== "modal"}"
274
279
  @click="${this.handleBackdropClick}"
275
280
  ></div>
276
- ` : d;
281
+ ` : f;
277
282
  }
278
283
  renderMenu(e) {
279
284
  let t = this.menuIds.get(e);
280
- t || (t = `u-sidebar-menu-${++S.menuSeq}`, this.menuIds.set(e, t));
285
+ t || (t = `u-sidebar-menu-${++C.menuSeq}`, this.menuIds.set(e, t));
281
286
  let n = t, r = this.state === "mobile" || this.state === "mobile-open" ? "bottom-start" : "right-start";
282
- return u`
287
+ return d`
283
288
  <u-sidebar-button
284
289
  id=${n}
285
290
  haspopup="menu"
@@ -298,9 +303,9 @@ var M = class extends n {
298
303
  @hide=${(e) => this.handleMenuHide(e, n)}
299
304
  @pick=${(t) => this.handleMenuPick(t, e)}>
300
305
  <u-menu>
301
- ${_(e.items, (e, t) => t, (e, t) => u`
306
+ ${v(e.items, (e, t) => t, (e, t) => d`
302
307
  <u-menu-item value=${String(t)} ?disabled=${e.disabled === !0}>
303
- ${e.icon ? u`<u-icon slot="prefix" .lib=${e.lib} .name=${e.icon}></u-icon>` : d}
308
+ ${e.icon ? d`<u-icon slot="prefix" .lib=${e.lib} .name=${e.icon}></u-icon>` : f}
304
309
  ${e.label}
305
310
  </u-menu-item>
306
311
  `)}
@@ -314,16 +319,16 @@ var M = class extends n {
314
319
  }
315
320
  handleMenuHide(e, t) {
316
321
  this.openMenu === t && (this.openMenu = null);
317
- let n = e.currentTarget, r = A();
318
- r && j(n, r) && this.shadowRoot?.querySelector(`#${t}`)?.focus();
322
+ let n = e.currentTarget, r = j();
323
+ r && M(n, r) && this.shadowRoot?.querySelector(`#${t}`)?.focus();
319
324
  }
320
325
  renderItem(e) {
321
- if (!e) return d;
326
+ if (!e) return f;
322
327
  if (e.type === "html") {
323
328
  let t = e.render(this.state);
324
- return typeof t == "string" ? g(t) : u`${t}`;
329
+ return typeof t == "string" ? _(t) : d`${t}`;
325
330
  }
326
- if (e.type === "button") return u`
331
+ if (e.type === "button") return d`
327
332
  <u-sidebar-button
328
333
  ?compact=${this.state === "slim"}
329
334
  .icon="${e.icon}"
@@ -337,7 +342,7 @@ var M = class extends n {
337
342
  if (e.type === "menu") return this.renderMenu(e);
338
343
  if (e.type === "link") {
339
344
  let t = this.isMatchedLink(e.pattern || e.href);
340
- return u`
345
+ return d`
341
346
  <u-sidebar-link
342
347
  ?compact=${this.state === "slim"}
343
348
  ?selected=${t}
@@ -353,18 +358,18 @@ var M = class extends n {
353
358
  ></u-sidebar-link>
354
359
  `;
355
360
  }
356
- if (e.type === "section") return u`
361
+ if (e.type === "section") return d`
357
362
  <u-sidebar-section
358
363
  ?compact=${this.state === "slim"}
359
364
  .mainTitle="${e.title}"
360
365
  .subTitle="${e.subTitle}"
361
366
  .styles="${e.styles}">
362
- ${_(e.items, (e, t) => t, (e) => this.renderItem(e))}
367
+ ${v(e.items, (e, t) => t, (e) => this.renderItem(e))}
363
368
  </u-sidebar-section>
364
369
  `;
365
370
  if (e.type === "group") {
366
371
  let t = e.items.some((e) => this.isMatchedLink(e.pattern || e.href));
367
- return u`
372
+ return d`
368
373
  <u-sidebar-group
369
374
  ?compact=${this.state === "slim"}
370
375
  ?selected=${t}
@@ -373,11 +378,11 @@ var M = class extends n {
373
378
  .lib="${e.lib}"
374
379
  .label="${e.label}"
375
380
  .styles="${e.styles}">
376
- ${_(e.items, (e, t) => t, (e) => this.renderItem(e))}
381
+ ${v(e.items, (e, t) => t, (e) => this.renderItem(e))}
377
382
  </u-sidebar-group>
378
383
  `;
379
384
  }
380
- return d;
385
+ return f;
381
386
  }
382
387
  handleButtonItemClick(e, t) {
383
388
  let n = e.onClick?.(t);
@@ -393,31 +398,40 @@ var M = class extends n {
393
398
  };
394
399
  }
395
400
  icon(e) {
396
- return this.config?.icons?.[e] ?? S.DEFAULT_ICONS[e];
401
+ return this.config?.icons?.[e] ?? C.DEFAULT_ICONS[e];
397
402
  }
398
403
  renderLogo() {
399
404
  let e = this.config?.logo, t = this.config?.title ?? "Home";
400
- if (!e || typeof e == "string") return e ? u`
405
+ if (!e || typeof e == "string") return e ? d`
401
406
  <u-link class="logo-link" aria-label=${t}>
402
407
  <u-icon class="logo"
403
408
  .name="${e}"
404
409
  .fallback=${s}
405
410
  ></u-icon>
406
411
  </u-link>
407
- ` : d;
412
+ ` : f;
413
+ if (typeof e == "object" && "icon" in e) return d`
414
+ <u-link class="logo-link" aria-label=${t}>
415
+ <u-icon class="logo"
416
+ .lib=${e.lib}
417
+ .name="${e.icon}"
418
+ .fallback=${s}
419
+ ></u-icon>
420
+ </u-link>
421
+ `;
408
422
  if (typeof e == "function") {
409
423
  let n = e(this.state);
410
- return u`
424
+ return d`
411
425
  <u-link class="logo-link" aria-label=${t}>
412
426
  <span class="logo">
413
427
  <!-- html-sink: documented HTML contract — a logo function returns markup the developer writes -->
414
- ${typeof n == "string" ? g(n) : n}
428
+ ${typeof n == "string" ? _(n) : n}
415
429
  </span>
416
430
  </u-link>
417
431
  `;
418
432
  }
419
- return u`
420
- <u-link class="logo-link" .href=${e.href} aria-label=${e.alt ? d : t}>
433
+ return d`
434
+ <u-link class="logo-link" .href=${e.href} aria-label=${e.alt ? f : t}>
421
435
  <img class="logo"
422
436
  src="${e.src}"
423
437
  alt="${e.alt ?? ""}"
@@ -430,6 +444,14 @@ var M = class extends n {
430
444
  let e = this.shadowRoot?.querySelector(".main");
431
445
  e && (this.mainSizeObserver = new ResizeObserver(() => this.syncMainTabStop()), this.mainSizeObserver.observe(e), this.observeContentSize());
432
446
  }
447
+ observeMobileHeader() {
448
+ if (this.mobileHeaderObserver || typeof ResizeObserver > "u") return;
449
+ let e = this.shadowRoot?.querySelector(".mobile-header");
450
+ e && (this.mobileHeaderObserver = new ResizeObserver(([t]) => {
451
+ let n = t?.borderBoxSize?.[0]?.blockSize ?? e.getBoundingClientRect().height;
452
+ n !== this.mobileHeaderBlockSize && (this.mobileHeaderBlockSize = n);
453
+ }), this.mobileHeaderObserver.observe(e));
454
+ }
433
455
  observeContentSize() {
434
456
  let e = this.mainSizeObserver;
435
457
  if (e) {
@@ -447,14 +469,14 @@ var M = class extends n {
447
469
  async focusRoute(e) {
448
470
  let t = this.routeContent();
449
471
  if (await Promise.all(t.map((e) => e.updateComplete)), this.hasOverlay || !e.isConnected) return;
450
- let n = A();
451
- n && t.some((e) => j(e, n)) || this.focusContentStart(t, e);
472
+ let n = j();
473
+ n && t.some((e) => M(e, n)) || this.focusContentStart(t, e);
452
474
  }
453
475
  focusContentStart(e, t) {
454
- let n = b(e, "[autofocus]", y);
476
+ let n = x(e, "[autofocus]", b);
455
477
  n?.focus();
456
- let r = A();
457
- n && r && j(n, r) || t.focus({ preventScroll: !0 });
478
+ let r = j();
479
+ n && r && M(n, r) || t.focus({ preventScroll: !0 });
458
480
  }
459
481
  overlayPanels() {
460
482
  return (this.shadowRoot?.querySelector("slot[name=\"overlay\"]"))?.assignedElements({ flatten: !0 }) ?? [];
@@ -462,19 +484,21 @@ var M = class extends n {
462
484
  async focusOverlay() {
463
485
  let e = this.overlayPanels();
464
486
  if (await Promise.all(e.map((e) => e.updateComplete)), !this.hasOverlay) return;
465
- let t = A();
466
- t && e.some((e) => j(e, t)) || (b(e, "[autofocus]", y) ?? b(e, "input, select, textarea, u-input, u-textarea, u-select, u-checkbox, u-radio, u-switch, u-slider", y) ?? this.shadowRoot?.querySelector(".overlay-close"))?.focus();
487
+ let t = j();
488
+ t && e.some((e) => M(e, t)) || (x(e, "[autofocus]", b) ?? x(e, "input, select, textarea, u-input, u-textarea, u-select, u-checkbox, u-radio, u-switch, u-slider", b) ?? this.shadowRoot?.querySelector(".overlay-close"))?.focus();
467
489
  }
468
490
  restoreOverlayFocus() {
469
491
  let e = this.overlayTrigger;
470
492
  this.overlayTrigger = null;
471
- let t = A();
493
+ let t = j();
472
494
  t && t !== document.body || (e?.isConnected ? e.focus() : this.mainElement?.focus({ preventScroll: !0 }));
473
495
  }
474
496
  };
475
- t([p({
497
+ t([m({
476
498
  type: String,
477
499
  reflect: !0
478
- }), e("design:type", Object)], M.prototype, "state", void 0), t([p({ type: Object }), e("design:type", Object)], M.prototype, "config", void 0), t([p({ type: Object }), e("design:type", typeof Record > "u" ? Object : Record)], M.prototype, "counts", void 0), t([p({ type: String }), e("design:type", Object)], M.prototype, "locale", void 0), t([m("u-progress-bar"), e("design:type", Object)], M.prototype, "progressBarEl", void 0), t([h(), e("design:type", Object)], M.prototype, "hasOverlay", void 0), t([h(), e("design:type", Object)], M.prototype, "openMenu", void 0), t([h(), e("design:type", Object)], M.prototype, "hasNotice", void 0), t([h(), e("design:type", Object)], M.prototype, "routeLoading", void 0), t([h(), e("design:type", Object)], M.prototype, "mainTabStop", void 0), t([h(), e("design:type", Object)], M.prototype, "currentPath", void 0), M = S = t([f("u-sidebar-layout")], M);
479
- //#endregion
480
- export { M as SidebarLayout };
500
+ }), e("design:type", Object)], N.prototype, "state", void 0), t([m({ type: Object }), e("design:type", Object)], N.prototype, "config", void 0), t([m({
501
+ type: Boolean,
502
+ reflect: !0
503
+ }), e("design:type", Object)], N.prototype, "immersive", void 0), t([m({ type: Object }), e("design:type", typeof Record > "u" ? Object : Record)], N.prototype, "counts", void 0), t([m({ type: String }), e("design:type", Object)], N.prototype, "locale", void 0), t([h("u-progress-bar"), e("design:type", Object)], N.prototype, "progressBarEl", void 0), t([g(), e("design:type", Object)], N.prototype, "hasOverlay", void 0), t([g(), e("design:type", Object)], N.prototype, "openMenu", void 0), t([g(), e("design:type", Object)], N.prototype, "hasNotice", void 0), t([g(), e("design:type", Object)], N.prototype, "routeLoading", void 0), t([g(), e("design:type", Object)], N.prototype, "mainTabStop", void 0), t([g(), e("design:type", Object)], N.prototype, "mobileHeaderBlockSize", void 0), t([g(), e("design:type", Object)], N.prototype, "currentPath", void 0), N = C = t([p("u-sidebar-layout")], N);
504
+ export { N as SidebarLayout };
@@ -1,5 +1,5 @@
1
1
  import { css as e } from "lit";
2
- //#region src/layouts/SidebarLayout.styles.ts
2
+ // src/layouts/SidebarLayout.styles.ts
3
3
  var t = e`
4
4
  :host {
5
5
  position: relative;
@@ -19,6 +19,16 @@ var t = e`
19
19
  :host([state="mobile-open"]) {
20
20
  flex-direction: column;
21
21
  }
22
+
23
+ :host([immersive]) .sidebar,
24
+ :host([immersive]) .mobile-header,
25
+ :host([immersive]) .backdrop,
26
+ :host([immersive]) .skip-link {
27
+ display: none;
28
+ }
29
+ :host([immersive]) .main {
30
+ padding: 0;
31
+ }
22
32
 
23
33
  .logo {
24
34
  color: var(--u-txt-color, #212121);
@@ -62,6 +72,17 @@ var t = e`
62
72
  overflow: hidden;
63
73
  text-overflow: ellipsis;
64
74
  }
75
+
76
+ .title .subtitle {
77
+ display: block;
78
+ overflow: hidden;
79
+ text-overflow: ellipsis;
80
+ color: var(--u-txt-color-weak, #616161);
81
+ font-size: var(--u-text-caption-size, 12px);
82
+ line-height: var(--u-text-caption-leading, 1.5);
83
+ font-weight: var(--u-text-caption-weight, 400);
84
+ letter-spacing: var(--u-text-caption-tracking, 0);
85
+ }
65
86
 
66
87
 
67
88
 
@@ -74,11 +95,14 @@ var t = e`
74
95
 
75
96
 
76
97
 
98
+
77
99
  .toggler {
78
100
  color: inherit;
79
- font-size: 20px;
80
101
  padding: 0px;
81
102
  }
103
+ .toggler u-icon {
104
+ font-size: 20px;
105
+ }
82
106
  .toggler:hover {
83
107
  color: var(--u-txt-color-hover, #1565C0);
84
108
  }
@@ -154,21 +178,32 @@ var t = e`
154
178
  bottom: 0;
155
179
  left: 0;
156
180
  }
181
+
182
+
183
+ .sidebar[state="mobile"] .sidebar-header,
184
+ .sidebar[state="mobile-open"] .sidebar-header {
185
+ display: none;
186
+ }
157
187
  .sidebar[state="mobile"] {
158
188
  position: absolute;
159
- top: 0;
189
+ top: var(--_mobile-header-block-size, 0px);
160
190
  bottom: 0;
161
191
  left: 0;
162
192
  right: 0;
163
193
  transform: translateY(-100%);
164
194
  }
195
+
196
+
197
+
198
+
165
199
  .sidebar[state="mobile-open"] {
166
200
  position: absolute;
167
- top: 0;
201
+ top: var(--_mobile-header-block-size, 0px);
168
202
  bottom: 0;
169
203
  left: 0;
170
204
  right: 0;
171
205
  transform: translateY(0);
206
+ overflow: hidden;
172
207
  }
173
208
 
174
209
 
@@ -180,7 +215,7 @@ var t = e`
180
215
  align-items: center;
181
216
  justify-content: space-between;
182
217
  gap: 12px;
183
- padding: 16px;
218
+ padding: 12px 16px;
184
219
  background: var(--app-header-bg, var(--u-bg-color-raised, #FAFAFA));
185
220
  color: var(--app-header-fg, var(--u-txt-color, #212121));
186
221
  border-bottom: 1px solid var(--u-border-color-weak, #EEEEEE);
@@ -194,7 +229,7 @@ var t = e`
194
229
  align-items: center;
195
230
  justify-content: space-between;
196
231
  gap: 12px;
197
- padding: 16px;
232
+ padding: 12px 16px;
198
233
  border-bottom: 1px solid var(--u-border-color-weak, #EEEEEE);
199
234
  user-select: none;
200
235
  }
@@ -439,5 +474,4 @@ var t = e`
439
474
  }
440
475
  }
441
476
  `;
442
- //#endregion
443
477
  export { t as styles };
@@ -57,8 +57,17 @@ export interface SidebarLogoImage {
57
57
  }
58
58
  /** 커스텀 로고 렌더 함수. `SidebarHtmlConfig.render`와 동일한 반환 타입 */
59
59
  export type SidebarLogoRenderer = (state: SidebarState) => TemplateResult<1> | HTMLElement | string;
60
- /** 최상단 앱 로고 설정: 아이콘명(문자열, 기존 동작) | 이미지 | 커스텀 렌더 함수 */
61
- export type SidebarLogoConfig = string | SidebarLogoImage | SidebarLogoRenderer;
60
+ /**
61
+ * 등록된 아이콘 라이브러리의 아이콘 로고 — 메뉴 항목과 같은 `icon` + `lib` 짝.
62
+ * 문자열 로고는 라이브러리를 고르지 못해 `u-icon` 의 기본 경로(`iconBasepath`)에서만 찾는다.
63
+ */
64
+ export interface SidebarLogoIcon {
65
+ icon: string;
66
+ /** `icon` 을 찾을 등록 라이브러리(`IconRegistry`). 없으면 문자열 로고와 같다. */
67
+ lib?: string;
68
+ }
69
+ /** 최상단 앱 로고 설정: 아이콘명(문자열, 기존 동작) | 라이브러리 아이콘 | 이미지 | 커스텀 렌더 함수 */
70
+ export type SidebarLogoConfig = string | SidebarLogoIcon | SidebarLogoImage | SidebarLogoRenderer;
62
71
  /** One entry of a `type: 'menu'` popup. */
63
72
  export interface SidebarMenuItemConfig extends SidebarPermissionGuard {
64
73
  label: string | DirectiveResult;
@@ -101,6 +110,11 @@ export interface SidebarLayoutConfig {
101
110
  logo?: SidebarLogoConfig;
102
111
  /** 앱 제목 */
103
112
  title?: string;
113
+ /**
114
+ * 제목 아래 한 줄 — 앱이 무엇인지(«여신 서류 보관 시스템»). 펼친 사이드바의 머리에만 보인다: 접힌 사이드바는 제목과 함께 숨고,
115
+ * 모바일 머리 줄은 한 줄을 지킨다.
116
+ */
117
+ subtitle?: string;
104
118
  /** 상단/메인 메뉴 아이템 항목들 */
105
119
  main?: SidebarItem[];
106
120
  /** 하단(footer)에 고정해서 렌더할 항목들 */
@@ -1,4 +1,4 @@
1
- //#region src/layouts/filterSidebarItems.ts
1
+ // src/layouts/filterSidebarItems.ts
2
2
  function e(e, t) {
3
3
  return !(e.requirePermission && !t(e.requirePermission) || e.requireAnyPermission && e.requireAnyPermission.length > 0 && !e.requireAnyPermission.some((e) => t(e)));
4
4
  }
@@ -24,5 +24,4 @@ function t(n, r) {
24
24
  }
25
25
  return a;
26
26
  }
27
- //#endregion
28
27
  export { t as filterSidebarItems };
package/dist/react.js CHANGED
@@ -4,7 +4,7 @@ import { Wizard as n } from "./components/Wizard.js";
4
4
  import { SidebarLayout as r } from "./layouts/SidebarLayout.js";
5
5
  import i from "react";
6
6
  import { createComponent as a } from "@lit/react";
7
- //#region src/react.ts
7
+ // src/react.ts
8
8
  function o(e) {
9
9
  let n = i.useRef(e);
10
10
  n.current = e;
@@ -26,5 +26,4 @@ var s = a({
26
26
  elementClass: n,
27
27
  events: { onStepChange: "step-change" }
28
28
  });
29
- //#endregion
30
29
  export { e as ScreenObserver, s as SidebarLayout, c as Wizard, o as useUnsavedChanges };
package/dist/translate.js CHANGED
@@ -27,5 +27,4 @@ var r = n(class extends t {
27
27
  this.subscribed && (this.subscribed = !1, e.off("languageChanged", this.refresh), e.off("initialized", this.refresh), e.store?.off("added", this.refresh));
28
28
  }
29
29
  });
30
- //#endregion
31
30
  export { r as translate };
package/dist/unsaved.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { getLocaleStrings as e } from "./internals/locale.js";
2
- //#region src/unsaved.ts
2
+ // src/unsaved.ts
3
3
  var t = /* @__PURE__ */ new Set(), n = (e) => {
4
4
  i() && (e.preventDefault(), e.returnValue = "");
5
5
  };
@@ -56,5 +56,4 @@ var l = class {
56
56
  this.release?.(), this.release = void 0;
57
57
  }
58
58
  };
59
- //#endregion
60
59
  export { l as UnsavedChangesController, a as confirmDiscardChanges, i as hasUnsavedChanges, r as trackUnsavedChanges, c as unsavedChangesGuard };
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.56.2",
4
+ "version": "0.58.0",
5
5
  "keywords": [
6
6
  "iyulab",
7
7
  "web-framework",
@@ -64,7 +64,7 @@
64
64
  "lit": "^3.3.3"
65
65
  },
66
66
  "peerDependencies": {
67
- "@iyulab/components": ">=2.8.0",
67
+ "@iyulab/components": ">=2.32.0",
68
68
  "@iyulab/router": "^0.24.0",
69
69
  "@lit/react": "^1.0.8",
70
70
  "react": "^18.0.0 || ^19.0.0"
@@ -78,7 +78,7 @@
78
78
  }
79
79
  },
80
80
  "devDependencies": {
81
- "@iyulab/components": "^2.20.3",
81
+ "@iyulab/components": "^2.32.0",
82
82
  "@iyulab/router": "^0.24.0",
83
83
  "@lit/react": "^1.0.8",
84
84
  "@types/node": "^26.1.1",