@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.
- package/CHANGELOG.md +46 -0
- package/dist/App.js +1 -2
- package/dist/_virtual/{_@oxc-project_runtime@0.154.0 → _@oxc-project_runtime@0.155.0}/helpers/esm/decorate.js +1 -2
- package/dist/_virtual/{_@oxc-project_runtime@0.154.0 → _@oxc-project_runtime@0.155.0}/helpers/esm/decorateMetadata.js +1 -2
- package/dist/components/ActionBar.js +3 -4
- package/dist/components/ActionBar.styles.js +1 -2
- package/dist/components/EmptyState.js +3 -4
- package/dist/components/EmptyState.styles.js +1 -2
- package/dist/components/GroupBox.js +3 -4
- package/dist/components/GroupBox.styles.js +1 -2
- package/dist/components/InfoField.d.ts +5 -0
- package/dist/components/InfoField.js +25 -19
- package/dist/components/InfoField.styles.js +20 -2
- package/dist/components/InfoSection.js +3 -4
- package/dist/components/InfoSection.styles.js +1 -2
- package/dist/components/MasterDetailLayout.js +3 -4
- package/dist/components/MasterDetailLayout.styles.js +1 -2
- package/dist/components/PageHeader.js +3 -4
- package/dist/components/PageHeader.styles.js +1 -2
- package/dist/components/SidebarButton.js +3 -4
- package/dist/components/SidebarButton.styles.js +1 -2
- package/dist/components/SidebarGroup.d.ts +9 -0
- package/dist/components/SidebarGroup.js +17 -14
- package/dist/components/SidebarGroup.styles.js +1 -2
- package/dist/components/SidebarLink.d.ts +6 -0
- package/dist/components/SidebarLink.js +16 -14
- package/dist/components/SidebarLink.styles.js +2 -3
- package/dist/components/SidebarSection.js +3 -4
- package/dist/components/SidebarSection.styles.js +1 -2
- package/dist/components/Timeline.js +2 -3
- package/dist/components/Timeline.styles.js +1 -2
- package/dist/components/TimelineItem.js +3 -4
- package/dist/components/TimelineItem.styles.js +1 -2
- package/dist/components/Wizard.js +3 -4
- package/dist/components/Wizard.styles.js +1 -2
- package/dist/defaultFallback.js +1 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -2
- package/dist/internals/ScreenObserver.js +1 -2
- package/dist/internals/StyledElement.js +3 -4
- package/dist/internals/locale.js +1 -2
- package/dist/internals/nav-icon.js +1 -2
- package/dist/internals/slotted.js +1 -2
- package/dist/layouts/SidebarLayout.d.ts +18 -0
- package/dist/layouts/SidebarLayout.js +100 -76
- package/dist/layouts/SidebarLayout.styles.js +41 -7
- package/dist/layouts/SidebarLayout.types.d.ts +16 -2
- package/dist/layouts/filterSidebarItems.js +1 -2
- package/dist/react.js +1 -2
- package/dist/translate.js +0 -1
- package/dist/unsaved.js +1 -2
- package/package.json +3 -3
- package/skills/modern-app/references/components/info-field.md +22 -0
- package/skills/modern-app/references/layout.md +29 -2
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import e from "../_virtual/_@oxc-project_runtime@0.
|
|
2
|
-
import t from "../_virtual/_@oxc-project_runtime@0.
|
|
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 {
|
|
16
|
-
import {
|
|
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
|
|
20
|
-
import { repeat as
|
|
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
|
|
25
|
-
import { isFocusCandidate as
|
|
26
|
-
import { OverlayManager as
|
|
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
|
-
|
|
29
|
-
var
|
|
30
|
-
function
|
|
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
|
|
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
|
|
50
|
+
function O(e) {
|
|
50
51
|
let t = e.tagName;
|
|
51
|
-
return t === "BUTTON" || t === "SUMMARY" || t === "A" && e.hasAttribute("href") ? !0 :
|
|
52
|
+
return t === "BUTTON" || t === "SUMMARY" || t === "A" && e.hasAttribute("href") ? !0 : D.has(e.getAttribute("role") ?? "");
|
|
52
53
|
}
|
|
53
|
-
function
|
|
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
|
|
58
|
+
function A(e, t) {
|
|
58
59
|
for (let n of e) if (n instanceof HTMLElement) {
|
|
59
|
-
if (
|
|
60
|
+
if (k(n)) return n === t;
|
|
60
61
|
if (n === t) break;
|
|
61
62
|
}
|
|
62
63
|
return !0;
|
|
63
64
|
}
|
|
64
|
-
function
|
|
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
|
|
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
|
|
78
|
+
var N = class extends n {
|
|
78
79
|
static {
|
|
79
|
-
|
|
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:${++
|
|
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.
|
|
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
|
|
96
|
-
|
|
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 && !!
|
|
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 =
|
|
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 &&
|
|
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 || !
|
|
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),
|
|
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 ? (
|
|
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 ||
|
|
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 ?
|
|
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 ??
|
|
236
|
-
${
|
|
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
|
-
${
|
|
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 ?
|
|
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" :
|
|
255
|
-
aria-label=${this.config.noticesAriaLabel ??
|
|
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
|
-
` :
|
|
281
|
+
` : f;
|
|
277
282
|
}
|
|
278
283
|
renderMenu(e) {
|
|
279
284
|
let t = this.menuIds.get(e);
|
|
280
|
-
t || (t = `u-sidebar-menu-${++
|
|
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
|
|
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
|
-
${
|
|
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 ?
|
|
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 =
|
|
318
|
-
r &&
|
|
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
|
|
326
|
+
if (!e) return f;
|
|
322
327
|
if (e.type === "html") {
|
|
323
328
|
let t = e.render(this.state);
|
|
324
|
-
return typeof t == "string" ?
|
|
329
|
+
return typeof t == "string" ? _(t) : d`${t}`;
|
|
325
330
|
}
|
|
326
|
-
if (e.type === "button") return
|
|
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
|
|
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
|
|
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
|
-
${
|
|
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
|
|
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
|
-
${
|
|
381
|
+
${v(e.items, (e, t) => t, (e) => this.renderItem(e))}
|
|
377
382
|
</u-sidebar-group>
|
|
378
383
|
`;
|
|
379
384
|
}
|
|
380
|
-
return
|
|
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] ??
|
|
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 ?
|
|
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
|
-
` :
|
|
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
|
|
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" ?
|
|
428
|
+
${typeof n == "string" ? _(n) : n}
|
|
415
429
|
</span>
|
|
416
430
|
</u-link>
|
|
417
431
|
`;
|
|
418
432
|
}
|
|
419
|
-
return
|
|
420
|
-
<u-link class="logo-link" .href=${e.href} aria-label=${e.alt ?
|
|
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 =
|
|
451
|
-
n && t.some((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 =
|
|
476
|
+
let n = x(e, "[autofocus]", b);
|
|
455
477
|
n?.focus();
|
|
456
|
-
let r =
|
|
457
|
-
n && r &&
|
|
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 =
|
|
466
|
-
t && e.some((e) =>
|
|
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 =
|
|
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([
|
|
497
|
+
t([m({
|
|
476
498
|
type: String,
|
|
477
499
|
reflect: !0
|
|
478
|
-
}), e("design:type", Object)],
|
|
479
|
-
|
|
480
|
-
|
|
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
|
-
|
|
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:
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
package/dist/unsaved.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { getLocaleStrings as e } from "./internals/locale.js";
|
|
2
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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",
|