@iyulab/modern-app 0.2.4 → 0.3.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 (33) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/dist/App.d.ts +53 -0
  3. package/dist/App.js +28 -28
  4. package/dist/components/SidebarButton.d.ts +2 -3
  5. package/dist/components/SidebarButton.js +12 -14
  6. package/dist/components/SidebarButton.styles.js +14 -16
  7. package/dist/components/SidebarGroup.d.ts +5 -4
  8. package/dist/components/SidebarGroup.js +45 -46
  9. package/dist/components/SidebarGroup.styles.js +39 -44
  10. package/dist/components/SidebarLink.d.ts +2 -7
  11. package/dist/components/SidebarLink.js +29 -39
  12. package/dist/components/SidebarLink.styles.js +9 -9
  13. package/dist/components/SidebarSection.d.ts +4 -5
  14. package/dist/components/SidebarSection.js +18 -22
  15. package/dist/components/SidebarSection.styles.js +9 -14
  16. package/dist/index.d.ts +2 -3
  17. package/dist/internals/ScreenObserver.d.ts +43 -0
  18. package/dist/internals/ScreenObserver.js +37 -0
  19. package/dist/internals/{ExtendedBaseElement.d.ts → StyledElement.d.ts} +6 -4
  20. package/dist/internals/{ExtendedBaseElement.js → StyledElement.js} +13 -14
  21. package/dist/layouts/SidebarLayout.d.ts +27 -19
  22. package/dist/layouts/SidebarLayout.js +140 -108
  23. package/dist/layouts/SidebarLayout.styles.js +82 -42
  24. package/dist/layouts/SidebarLayout.types.d.ts +14 -12
  25. package/dist/types/AppConfigs.d.ts +11 -6
  26. package/package.json +8 -9
  27. package/dist/components/SidebarLogo.d.ts +0 -33
  28. package/dist/components/SidebarLogo.js +0 -60
  29. package/dist/components/SidebarLogo.styles.d.ts +0 -1
  30. package/dist/components/SidebarLogo.styles.js +0 -57
  31. package/dist/internals/observables.d.ts +0 -7
  32. package/dist/internals/observables.js +0 -5
  33. package/dist/types/AppTypes.d.ts +0 -4
@@ -1,86 +1,105 @@
1
- import { nothing as p, html as i } from "lit";
2
- import { query as h, state as g, property as b, customElement as f } from "lit/decorators.js";
1
+ import { nothing as h, html as a } from "lit";
2
+ import { query as b, state as g, property as p, customElement as f } from "lit/decorators.js";
3
3
  import { unsafeHTML as m } from "lit/directives/unsafe-html.js";
4
- import { repeat as n } from "lit/directives/repeat.js";
5
- import { autorun as $ } from "mobx";
6
- import { UIconButton as v } from "@iyulab/components/dist/components/icon-button/UIconButton.component.js";
4
+ import { repeat as l } from "lit/directives/repeat.js";
5
+ import { UIcon as v } from "@iyulab/components/dist/components/icon/UIcon.component.js";
6
+ import { UButton as $ } from "@iyulab/components/dist/components/button/UButton.component.js";
7
7
  import { UProgressBar as y } from "@iyulab/components/dist/components/progress-bar/UProgressBar.component.js";
8
- import { screen as u } from "../internals/observables.js";
9
- import { ExtendedBaseElement as S } from "../internals/ExtendedBaseElement.js";
10
- import { SidebarLogo as _ } from "../components/SidebarLogo.js";
11
- import { SidebarSection as w } from "../components/SidebarSection.js";
12
- import { SidebarGroup as k } from "../components/SidebarGroup.js";
13
- import { SidebarLink as E } from "../components/SidebarLink.js";
14
- import { SidebarButton as B } from "../components/SidebarButton.js";
15
- import { styles as L } from "./SidebarLayout.styles.js";
16
- var R = Object.defineProperty, I = Object.getOwnPropertyDescriptor, P = Object.getPrototypeOf, C = Reflect.get, l = (e, t, s, a) => {
17
- for (var r = a > 1 ? void 0 : a ? I(t, s) : t, d = e.length - 1, c; d >= 0; d--)
18
- (c = e[d]) && (r = (a ? c(t, s, r) : c(r)) || r);
19
- return a && r && R(t, s, r), r;
20
- }, O = (e, t, s) => C(P(e), s, t);
21
- let o = class extends S {
8
+ import { app as u } from "../App.js";
9
+ import { StyledElement as w } from "../internals/StyledElement.js";
10
+ import { SidebarSection as k } from "../components/SidebarSection.js";
11
+ import { SidebarGroup as B } from "../components/SidebarGroup.js";
12
+ import { SidebarLink as L } from "../components/SidebarLink.js";
13
+ import { SidebarButton as _ } from "../components/SidebarButton.js";
14
+ import { styles as C } from "./SidebarLayout.styles.js";
15
+ import { IconRegistry as R } from "@iyulab/components/dist/utilities/IconRegistry.js";
16
+ var S = Object.defineProperty, z = Object.getOwnPropertyDescriptor, E = Object.getPrototypeOf, x = Reflect.get, n = (e, t, s, r) => {
17
+ for (var o = r > 1 ? void 0 : r ? z(t, s) : t, d = e.length - 1, c; d >= 0; d--)
18
+ (c = e[d]) && (o = (r ? c(t, s, o) : c(o)) || o);
19
+ return r && o && S(t, s, o), o;
20
+ }, M = (e, t, s) => x(E(e), s, t);
21
+ R.register("sidebar-app", (e) => e === "layout-sidebar" ? '<svg viewBox="0 0 16 16"><path d="M0 3a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2zm5-1v12h9a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1zM4 2H2a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h2z"/></svg>' : e === "list" ? '<svg viewBox="0 0 16 16"><path fill-rule="evenodd" d="M1 3a1 1 0 0 1 1-1h12a1 1 0 0 1 0 2H2a1 1 0 0 1-1-1 m0 5a1 1 0 0 1 1-1h12a1 1 0 0 1 0 2H2a1 1 0 0 1-1-1 m0 5a1 1 0 0 1 1-1h12a1 1 0 0 1 0 2H2a1 1 0 0 1-1-1"/></svg>' : e === "x-lg" ? '<svg viewBox="0 0 16 16"><path d="M2.146 2.854a.5.5 0 1 1 .708-.708L8 7.293l5.146-5.147a.5.5 0 0 1 .708.708L8.707 8l5.147 5.146a.5.5 0 0 1-.708.708L8 8.707l-5.146 5.147a.5.5 0 0 1-.708-.708L7.293 8z"/></svg>' : "");
22
+ let i = class extends w {
22
23
  constructor() {
23
- super(...arguments), this.disposers = [], this.state = "docked", this.toggleState = () => {
24
- const e = u.get();
25
- e === "large" ? this.state = this.state === "docked" ? "slim" : "docked" : e === "medium" ? this.state = this.state === "slim" ? "modal" : "slim" : e === "small" ? this.state = this.state === "closed" ? "modal" : "closed" : console.warn("Unknown screen size:", e);
26
- }, this.toggleStateIfModalState = () => {
27
- const e = u.get();
28
- this.state === "modal" && (e === "medium" ? this.state = "slim" : e === "small" && (this.state = "closed"));
24
+ super(...arguments), this.context = null, this.state = "default", this.isMatchedLink = (e) => !this.context || !e ? !1 : (e = typeof e == "string" ? new URLPattern(e, window.location.origin) : e, e.test(this.context.path, window.location.origin)), this.handleBrandLogoClick = () => {
25
+ u.navigate("");
26
+ }, this.handleToggleButtonClick = () => {
27
+ const e = u.screen ?? "large";
28
+ 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("Unknown screen size:", e);
29
+ }, this.handleBackdropClick = () => {
30
+ this.state = "slim";
29
31
  }, this.handleRouteBegin = (e) => {
30
- this.progressBarEl.value = 0, this.toggleStateIfModalState();
32
+ this.progressBarEl.value = 0, this.state === "modal" && (this.state = "slim"), this.state === "mobile-open" && (this.state = "mobile"), this.context = e.context;
31
33
  }, this.handleRouteProgress = (e) => {
32
34
  this.progressBarEl.value = e.progress;
33
35
  }, this.handleRouteDone = (e) => {
34
36
  this.progressBarEl.value = 100;
37
+ }, this.handleScreenResize = (e) => {
38
+ const t = e.detail.size;
39
+ t === "large" ? this.state = "default" : t === "medium" ? this.state = "slim" : t === "small" ? this.state = "mobile" : console.warn("Unknown screen size:", t);
35
40
  };
36
41
  }
37
42
  connectedCallback() {
38
- super.connectedCallback(), this.disposers.push($(() => {
39
- const e = u.get();
40
- this.updateState(e);
41
- })), window.addEventListener("route-begin", this.handleRouteBegin), window.addEventListener("route-done", this.handleRouteDone), window.addEventListener("route-progress", this.handleRouteProgress);
43
+ super.connectedCallback(), window.addEventListener("route-begin", this.handleRouteBegin), window.addEventListener("route-done", this.handleRouteDone), window.addEventListener("route-progress", this.handleRouteProgress), window.addEventListener("screen-resize", this.handleScreenResize);
42
44
  }
43
45
  disconnectedCallback() {
44
- this.disposers.forEach((e) => e()), window.removeEventListener("route-begin", this.handleRouteBegin), window.removeEventListener("route-done", this.handleRouteDone), window.removeEventListener("route-progress", this.handleRouteProgress), super.disconnectedCallback();
46
+ window.removeEventListener("route-begin", this.handleRouteBegin), window.removeEventListener("route-done", this.handleRouteDone), window.removeEventListener("route-progress", this.handleRouteProgress), window.removeEventListener("screen-resize", this.handleScreenResize), super.disconnectedCallback();
45
47
  }
46
48
  updated(e) {
47
49
  super.updated(e), e.has("config") && (this.styles = this.config?.styles);
48
50
  }
49
51
  render() {
50
- return this.config ? i`
52
+ return this.config ? a`
53
+ <!-- Mobile Header -->
54
+ <div class="mobile-header" part="mobile-header" ?hidden="${!this.state.startsWith("mobile")}">
55
+ <u-icon class="logo"
56
+ .name="${this.config.logo}"
57
+ @click=${this.handleBrandLogoClick}
58
+ ></u-icon>
59
+ <span class="title">
60
+ ${this.config.title}
61
+ </span>
62
+ <u-button class="toggler"
63
+ @click=${this.handleToggleButtonClick}>
64
+ <u-icon
65
+ lib="sidebar-app"
66
+ name=${this.state === "mobile-open" ? "x-lg" : "list"}
67
+ ></u-icon>
68
+ </u-button>
69
+ </div>
70
+
51
71
  <!-- Sidebar -->
52
- <aside class="sidebar" part="sidebar" state=${this.state}>
72
+ <aside class="sidebar" part="sidebar" state="${this.state}">
53
73
  <!-- Sidebar Header -->
54
74
  <div class="sidebar-header" part="sidebar-header">
55
- <u-sidebar-logo
56
- .compact="${this.state === "slim"}"
57
- .type=${this.config.logo?.type || "icon"}
58
- .image="${this.config.logo?.image}"
59
- .icon="${this.config.logo?.icon}"
60
- .label="${this.config.logo?.label}"
61
- .styles="${this.config.logo?.styles}"
62
- @click="${this.config.logo?.onClick}"
63
- ></u-sidebar-logo>
64
- <u-icon-button class="sidebar-toggler" part="sidebar-toggler"
65
- lib="internal"
66
- name=${this.state === "closed" ? "chevron-right" : "layout-sidebar"}
67
- @click="${this.toggleState}"
68
- ></u-icon-button>
75
+ <u-icon class="logo"
76
+ .name="${this.config.logo}"
77
+ @click=${this.handleBrandLogoClick}
78
+ ></u-icon>
79
+ <span class="title" ?hidden=${this.state === "slim"}>
80
+ ${this.config.title}
81
+ </span>
82
+ <u-button class="toggler"
83
+ @click=${this.handleToggleButtonClick}>
84
+ <u-icon
85
+ lib="sidebar-app"
86
+ name="layout-sidebar"
87
+ ></u-icon>
88
+ </u-button>
69
89
  </div>
70
90
 
71
91
  <!-- Sidebar Navigation Menu -->
72
- <nav class="sidebar-menu" part="sidebar-menu">
73
- ${n(
74
- this.config.menu ?? [],
92
+ <nav class="sidebar-main" part="sidebar-main" scrollable>
93
+ ${l(
94
+ this.config.main ?? [],
75
95
  (e, t) => t,
76
96
  (e, t) => this.renderItem(e)
77
97
  )}
78
98
  </nav>
79
99
 
80
100
  <!-- Sidebar Footer -->
81
- <div class="sidebar-footer" part="sidebar-footer"
82
- ?hidden="${!this.config.footer || this.config.footer.length === 0}">
83
- ${n(
101
+ <div class="sidebar-footer" part="sidebar-footer">
102
+ ${l(
84
103
  this.config.footer ?? [],
85
104
  (e, t) => t,
86
105
  (e, t) => this.renderItem(e)
@@ -89,26 +108,27 @@ let o = class extends S {
89
108
  </aside>
90
109
 
91
110
  <!-- Main Content -->
92
- <div class="main" part="main">
111
+ <div class="main" part="main" scrollable>
93
112
  <u-progress-bar part="progress"></u-progress-bar>
94
113
 
95
114
  <u-outlet></u-outlet>
96
115
  </div>
97
116
 
98
117
  <!-- Backdrop for modal state -->
99
- <div class="backdrop"
100
- ?hidden="${this.state !== "modal"}"
101
- @click="${this.toggleStateIfModalState}"
118
+ <div class="backdrop" ?hidden="${this.state !== "modal"}"
119
+ @click="${this.handleBackdropClick}"
102
120
  ></div>
103
- ` : p;
121
+ ` : h;
104
122
  }
105
123
  /** 사이드바 아이템 렌더링 */
106
124
  renderItem(e) {
107
- if (!e) return p;
108
- if (e.type === "innerHtml") {
125
+ if (!e) return h;
126
+ if (e.type === "html") {
109
127
  const t = e.render(this.state);
110
- return typeof t == "string" ? m(t) : i`${t}`;
111
- } else return e.type === "button" ? i`
128
+ return typeof t == "string" ? m(t) : a`${t}`;
129
+ } else {
130
+ if (e.type === "button")
131
+ return a`
112
132
  <u-sidebar-button
113
133
  ?compact=${this.state === "slim"}
114
134
  .icon="${e.icon}"
@@ -116,71 +136,83 @@ let o = class extends S {
116
136
  .styles="${e.styles}"
117
137
  @click="${e.onClick}"
118
138
  ></u-sidebar-button>
119
- ` : e.type === "section" ? i`
139
+ `;
140
+ if (e.type === "link") {
141
+ const t = this.isMatchedLink(e.pattern || e.href);
142
+ return a`
143
+ <u-sidebar-link
144
+ ?compact=${this.state === "slim"}
145
+ ?selected=${t}
146
+ .icon="${e.icon}"
147
+ .label="${e.label}"
148
+ .href="${e.href}"
149
+ .pattern="${e.pattern}"
150
+ .styles="${e.styles}"
151
+ ></u-sidebar-link>
152
+ `;
153
+ } else {
154
+ if (e.type === "section")
155
+ return a`
120
156
  <u-sidebar-section
121
157
  ?compact=${this.state === "slim"}
122
158
  .mainTitle="${e.title}"
123
159
  .subTitle="${e.subTitle}"
124
- .items="${e.items}"
125
160
  .styles="${e.styles}">
126
- ${n(
127
- e.items,
128
- (t, s) => s,
129
- (t, s) => this.renderItem(t)
130
- )}
161
+ ${l(
162
+ e.items,
163
+ (t, s) => s,
164
+ (t, s) => this.renderItem(t)
165
+ )}
131
166
  </u-sidebar-section>
132
- ` : e.type === "group" ? i`
167
+ `;
168
+ if (e.type === "group") {
169
+ const t = e.items.some((s) => this.isMatchedLink(s.pattern || s.href));
170
+ return a`
133
171
  <u-sidebar-group
134
172
  ?compact=${this.state === "slim"}
173
+ ?selected=${t}
135
174
  ?collapsed="${e.collapsed ?? !1}"
136
175
  .icon="${e.icon}"
137
176
  .label="${e.label}"
138
- .items="${e.items}"
139
177
  .styles="${e.styles}">
140
- ${n(
141
- e.items,
142
- (t, s) => s,
143
- (t, s) => this.renderItem(t)
144
- )}
178
+ ${l(
179
+ e.items,
180
+ (s, r) => r,
181
+ (s, r) => this.renderItem(s)
182
+ )}
145
183
  </u-sidebar-group>
146
- ` : i`
147
- <u-sidebar-link
148
- ?compact=${this.state === "slim"}
149
- .icon="${e.icon}"
150
- .label="${e.label}"
151
- .href="${e.href}"
152
- .pattern="${e.pattern}"
153
- .styles="${e.styles}"
154
- ></u-sidebar-link>
155
184
  `;
156
- }
157
- /** 화면 크기 변경에 따른 사이드바 상태 업데이트 */
158
- updateState(e) {
159
- e === "large" ? this.state = "docked" : e === "medium" ? this.state = "slim" : e === "small" ? this.state = "closed" : console.warn("Unknown screen size:", e);
185
+ } else
186
+ return h;
187
+ }
188
+ }
160
189
  }
161
190
  };
162
- o.styles = [O(o, o, "styles"), L];
163
- o.dependencies = {
164
- "u-icon-button": v,
191
+ i.styles = [M(i, i, "styles"), C];
192
+ i.dependencies = {
193
+ "u-icon": v,
194
+ "u-button": $,
165
195
  "u-progress-bar": y,
166
- "u-sidebar-logo": _,
167
- "u-sidebar-section": w,
168
- "u-sidebar-group": k,
169
- "u-sidebar-link": E,
170
- "u-sidebar-button": B
196
+ "u-sidebar-section": k,
197
+ "u-sidebar-group": B,
198
+ "u-sidebar-link": L,
199
+ "u-sidebar-button": _
171
200
  };
172
- l([
173
- h("u-progress-bar")
174
- ], o.prototype, "progressBarEl", 2);
175
- l([
201
+ n([
202
+ b("u-progress-bar")
203
+ ], i.prototype, "progressBarEl", 2);
204
+ n([
176
205
  g()
177
- ], o.prototype, "state", 2);
178
- l([
179
- b({ type: Object })
180
- ], o.prototype, "config", 2);
181
- o = l([
206
+ ], i.prototype, "context", 2);
207
+ n([
208
+ p({ type: String, reflect: !0 })
209
+ ], i.prototype, "state", 2);
210
+ n([
211
+ p({ type: Object })
212
+ ], i.prototype, "config", 2);
213
+ i = n([
182
214
  f("u-sidebar-layout")
183
- ], o);
215
+ ], i);
184
216
  export {
185
- o as SidebarLayout
217
+ i as SidebarLayout
186
218
  };
@@ -1,13 +1,49 @@
1
- import { css as o } from "lit";
2
- const r = o`
1
+ import { css as e } from "lit";
2
+ const r = e`
3
3
  :host {
4
4
  position: relative;
5
5
  display: flex;
6
6
  flex-direction: row;
7
7
  width: 100%;
8
8
  height: 100%;
9
+ font-family: var(--u-font-base);
9
10
  overflow: hidden;
10
11
  }
12
+ :host([state="mobile"]),
13
+ :host([state="mobile-open"]) {
14
+ flex-direction: column;
15
+ }
16
+
17
+ .logo {
18
+ color: var(--u-txt-color);
19
+ font-size: 24px;
20
+ cursor: pointer;
21
+ }
22
+ .logo:hover {
23
+ color: var(--u-txt-color-hover);
24
+ }
25
+
26
+ .title {
27
+ flex: 1;
28
+ color: var(--u-txt-color);
29
+ font-size: 18px;
30
+ line-height: 24px;
31
+ font-weight: 700;
32
+ white-space: nowrap;
33
+ overflow: hidden;
34
+ text-overflow: ellipsis;
35
+ }
36
+
37
+ .toggler {
38
+ color: inherit;
39
+ font-size: 20px;
40
+ padding: 0px;
41
+ border: none;
42
+ background: transparent;
43
+ }
44
+ .toggler:hover {
45
+ color: var(--u-txt-color-hover);
46
+ }
11
47
 
12
48
  /* Sidebar Container */
13
49
  .sidebar {
@@ -17,53 +53,57 @@ const r = o`
17
53
  flex-direction: column;
18
54
  justify-content: space-between;
19
55
  gap: 8px;
20
- width: 260px;
21
- height: 100vh;
56
+ height: 100%;
22
57
  background: var(--u-panel-bg-color);
23
58
  border-right: 1px solid var(--u-border-color);
24
- box-shadow: 0 2px 8px var(--u-shadow-color-weak);
25
- transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
59
+ transition: all 0.3s ease;
26
60
  }
27
61
  /* Sidebar states */
62
+ .sidebar[state="default"] {
63
+ width: 260px;
64
+ }
28
65
  .sidebar[state="slim"] {
29
66
  width: 64px;
30
67
  }
31
68
  .sidebar[state="slim"] .sidebar-header {
32
69
  flex-direction: column;
70
+ gap: 16px;
71
+ padding: 16px;
33
72
  }
34
- .sidebar[state="closed"] {
35
- position: fixed;
36
- top: 0;
37
- left: -260px;
38
- bottom: 0;
73
+ .sidebar[state="modal"] {
39
74
  width: 260px;
40
- }
41
- .sidebar[state="closed"] .sidebar-toggler {
42
75
  position: absolute;
43
- z-index: 1000;
44
- top: 16px;
45
- left: calc(100% - 16px);
46
- background-color: var(--u-panel-bg-color);
47
- border: 1px solid var(--u-border-color-strong);
48
- border-radius: 4px 8px 8px 4px;
76
+ top: 0;
77
+ left: 0;
49
78
  }
50
- .sidebar[state="closed"] .sidebar-toggler:hover {
51
- transform: translateX(24px);
79
+ .sidebar[state="mobile"] {
80
+ position: absolute;
81
+ top: 0;
82
+ left: 0;
83
+ right: 0;
84
+ transform: translateY(-100%);
52
85
  }
53
- .sidebar[state="modal"] {
54
- position: fixed;
86
+ .sidebar[state="mobile-open"] {
87
+ position: absolute;
55
88
  top: 0;
56
89
  left: 0;
57
- bottom: 0;
58
- width: 260px;
90
+ right: 0;
91
+ transform: translateY(0);
59
92
  }
60
93
 
61
- /* Sidebar Toggler Button */
62
- .sidebar-toggler {
63
- font-size: 20px;
64
- border: none;
65
- background: transparent;
66
- cursor: col-resize;
94
+ /* Mobile Header */
95
+ .mobile-header {
96
+ position: relative;
97
+ z-index: 1001;
98
+ display: flex;
99
+ flex-direction: row;
100
+ align-items: center;
101
+ justify-content: space-between;
102
+ gap: 12px;
103
+ padding: 16px;
104
+ background: var(--u-panel-bg-color);
105
+ border-bottom: 1px solid var(--u-border-color-weak);
106
+ user-select: none;
67
107
  }
68
108
 
69
109
  /* Sidebar Header */
@@ -72,23 +112,21 @@ const r = o`
72
112
  flex-direction: row;
73
113
  align-items: center;
74
114
  justify-content: space-between;
75
- gap: 8px;
76
- padding: 16px 12px;
115
+ gap: 12px;
116
+ padding: 16px;
77
117
  border-bottom: 1px solid var(--u-border-color-weak);
118
+ user-select: none;
78
119
  }
79
120
 
80
- /* Sidebar Menu */
81
- .sidebar-menu {
121
+ /* Sidebar Main Menu */
122
+ .sidebar-main {
82
123
  flex: 1;
83
124
  display: flex;
84
125
  flex-direction: column;
85
126
  gap: 4px;
86
127
  padding: 8px;
87
- overflow-y: auto;
88
128
  overflow-x: hidden;
89
-
90
- scrollbar-width: thin;
91
- scrollbar-color: var(--u-scrollbar-color) var(--u-scrollbar-track-color);
129
+ overflow-y: auto;
92
130
  }
93
131
 
94
132
  /* Sidebar Footer */
@@ -99,14 +137,16 @@ const r = o`
99
137
  padding: 8px;
100
138
  border-top: 1px solid var(--u-border-color-weak);
101
139
  }
140
+ .sidebar-footer:empty {
141
+ display: none;
142
+ }
102
143
 
103
144
  /* Main Content */
104
145
  .main {
105
146
  position: relative;
106
147
  flex: 1;
107
- display: block;
108
- overflow: hidden;
109
148
  background: var(--u-bg-color);
149
+ overflow: auto;
110
150
  }
111
151
 
112
152
  .main u-progress-bar {
@@ -114,6 +154,7 @@ const r = o`
114
154
  z-index: 100;
115
155
  top: 0;
116
156
  left: 0;
157
+ right: 0;
117
158
  height: 4px;
118
159
  border-radius: 0;
119
160
  background-color: transparent;
@@ -121,7 +162,6 @@ const r = o`
121
162
 
122
163
  /* Backdrop for modal mode */
123
164
  .backdrop {
124
- content: '';
125
165
  position: absolute;
126
166
  z-index: 100;
127
167
  top: 0;
@@ -1,27 +1,29 @@
1
- import { StyleMap } from '../types/AppTypes';
2
- import { SidebarLogoConfig } from '../components/SidebarLogo';
1
+ import { TemplateResult } from 'lit';
2
+ import { StyleMap } from '../internals/StyledElement';
3
3
  import { SidebarLinkConfig } from '../components/SidebarLink';
4
4
  import { SidebarSectionConfig } from '../components/SidebarSection';
5
5
  import { SidebarGroupConfig } from '../components/SidebarGroup';
6
6
  import { SidebarButtonConfig } from '../components/SidebarButton';
7
7
  /** 사이드바 레이아웃 컴포넌트의 요소(part) 타입 */
8
- export type SidebarParts = 'host' | 'sidebar' | 'sidebar-toggler' | 'sidebar-header' | 'sidebar-menu' | 'sidebar-footer' | 'main' | 'progress';
8
+ export type SidebarParts = 'host' | 'mobile-header' | 'sidebar' | 'sidebar-header' | 'sidebar-main' | 'sidebar-footer' | 'main' | 'progress';
9
9
  /** 사이드바 상태 타입 */
10
- export type SidebarState = 'docked' | 'modal' | 'slim' | 'closed';
10
+ export type SidebarState = 'default' | 'slim' | 'modal' | 'mobile' | 'mobile-open';
11
11
  /** 사이드바 안에 HTML 또는 엘리먼트를 직접 렌더링하는 설정 */
12
- export interface SidebarInnerHtmlConfig {
13
- type: 'innerHtml';
14
- render: (state: SidebarState) => HTMLElement | string;
12
+ export interface SidebarHtmlConfig {
13
+ type: 'html';
14
+ render: (state: SidebarState) => TemplateResult<1> | HTMLElement | string;
15
15
  }
16
16
  /** union: section | group | link | button */
17
- export type SidebarItem = (SidebarLinkConfig | SidebarSectionConfig | SidebarGroupConfig | SidebarButtonConfig | SidebarInnerHtmlConfig);
18
- /** 사이드바 전체 설정 (루트) */
17
+ export type SidebarItem = (SidebarLinkConfig | SidebarSectionConfig | SidebarGroupConfig | SidebarButtonConfig | SidebarHtmlConfig);
18
+ /** 사이드바 레이아웃 전체 설정 */
19
19
  export interface SidebarLayoutConfig {
20
20
  type: 'sidebar';
21
21
  /** 최상단 앱 로고 */
22
- logo?: SidebarLogoConfig;
23
- /** 상단/메인 메뉴 항목들 */
24
- menu?: SidebarItem[];
22
+ logo?: string;
23
+ /** 제목 */
24
+ title?: string;
25
+ /** 상단/메인 메뉴 아이템 항목들 */
26
+ main?: SidebarItem[];
25
27
  /** 하단(footer)에 고정해서 렌더할 항목들 */
26
28
  footer?: SidebarItem[];
27
29
  /** 사이드바 스타일 맵 */
@@ -5,7 +5,7 @@ import { SidebarLayoutConfig } from '../layouts/SidebarLayout.types';
5
5
  /**
6
6
  * i18next의 초기화 옵션과 플러그인 배열을 포함한 다국어 설정
7
7
  */
8
- export type LocalizationInitOptions = InitOptions & {
8
+ export type I18nInitOptions = InitOptions & {
9
9
  /**
10
10
  * i18next 플러그인 배열
11
11
  * @description i18next.use() 메서드에 전달되는 플러그인들의 배열입니다.
@@ -28,10 +28,15 @@ export type LayoutConfig = (SidebarLayoutConfig) & {
28
28
  */
29
29
  export interface AppConfig {
30
30
  /**
31
- * 현재 애플리케이션의 기본 경로 설정
31
+ * 현재 애플리케이션의 기본 라우팅 경로 설정
32
32
  * @default '/'
33
33
  */
34
34
  basepath?: string;
35
+ /**
36
+ * 현재 애플리케이션의 기본 아이콘 경로 설정
37
+ * @default '/assets/icons/'
38
+ */
39
+ iconBasepath?: string;
35
40
  /**
36
41
  * 클라이언트 사이드 라우팅을 위한 설정 배열
37
42
  */
@@ -42,11 +47,12 @@ export interface AppConfig {
42
47
  fallback?: FallbackRouteConfig;
43
48
  /**
44
49
  * 애플리케이션이 렌더링될 루트 HTML 요소
45
- * @description 지정하지 않을 경우 document.body가 사용됩니다.
50
+ * @default document.body
46
51
  */
47
52
  root?: Element;
48
53
  /**
49
54
  * 애플리케이션을 구성하기 위한 레이아웃 설정
55
+ *
50
56
  * @description 현재는 사이드바 레이아웃만 지원합니다.
51
57
  */
52
58
  layout: LayoutConfig;
@@ -55,9 +61,8 @@ export interface AppConfig {
55
61
  */
56
62
  theme?: ThemeInitOptions;
57
63
  /**
58
- * i18next를 사용하는 다국어 설정
59
- * @description i18next의 InitOptions와 플러그인 배열을 포함합니다. i18next를 사용하지 않는 경우 이 설정은 생략할 수 있습니다.
64
+ * i18next의 InitOptions와 플러그인 배열을 포함합니다. i18next사용하지 않는 경우 이 설정은 생략할 수 있습니다.
60
65
  * @see 설정에 대한 자세한 내용은 {@link https://www.i18next.com/overview/configuration-options} 참조하십시오.
61
66
  */
62
- localization?: LocalizationInitOptions;
67
+ i18n?: I18nInitOptions;
63
68
  }
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 react, lit-element",
4
- "version": "0.2.4",
4
+ "version": "0.3.0",
5
5
  "keywords": [
6
6
  "iyulab",
7
7
  "web-framework",
@@ -34,20 +34,19 @@
34
34
  }
35
35
  },
36
36
  "scripts": {
37
- "start": "vite",
37
+ "test": "vite",
38
38
  "build": "vite build"
39
39
  },
40
40
  "dependencies": {
41
- "@iyulab/components": "^0.1.10",
42
- "@iyulab/router": "^0.5.3",
43
- "lit": "^3.3.1",
44
- "mobx": "^6.15.0",
45
- "i18next": "^25.7.3"
41
+ "@iyulab/components": "^0.2.0",
42
+ "@iyulab/router": "^0.6.2",
43
+ "lit": "^3.3.2",
44
+ "i18next": "^25.8.0"
46
45
  },
47
46
  "devDependencies": {
48
- "@types/node": "^25.0.3",
47
+ "@types/node": "^25.0.9",
49
48
  "typescript": "^5.9.3",
50
- "vite": "^7.3.0",
49
+ "vite": "^7.3.1",
51
50
  "vite-plugin-dts": "^4.5.4"
52
51
  }
53
52
  }