@iyulab/modern-app 0.2.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 +7 -0
- package/LICENSE +21 -0
- package/README.md +35 -0
- package/dist/app.d.ts +53 -0
- package/dist/app.js +111 -0
- package/dist/components/ProgressBar.d.ts +22 -0
- package/dist/components/ProgressBar.js +54 -0
- package/dist/components/ProgressBar.styles.d.ts +1 -0
- package/dist/components/ProgressBar.styles.js +31 -0
- package/dist/components/SidebarButton.d.ts +29 -0
- package/dist/components/SidebarButton.js +50 -0
- package/dist/components/SidebarButton.styles.d.ts +1 -0
- package/dist/components/SidebarButton.styles.js +62 -0
- package/dist/components/SidebarGroup.d.ts +35 -0
- package/dist/components/SidebarGroup.js +70 -0
- package/dist/components/SidebarGroup.styles.d.ts +1 -0
- package/dist/components/SidebarGroup.styles.js +93 -0
- package/dist/components/SidebarLink.d.ts +42 -0
- package/dist/components/SidebarLink.js +71 -0
- package/dist/components/SidebarLink.styles.d.ts +1 -0
- package/dist/components/SidebarLink.styles.js +58 -0
- package/dist/components/SidebarLogo.d.ts +33 -0
- package/dist/components/SidebarLogo.js +60 -0
- package/dist/components/SidebarLogo.styles.d.ts +1 -0
- package/dist/components/SidebarLogo.styles.js +53 -0
- package/dist/components/SidebarSection.d.ts +30 -0
- package/dist/components/SidebarSection.js +52 -0
- package/dist/components/SidebarSection.styles.d.ts +1 -0
- package/dist/components/SidebarSection.styles.js +44 -0
- package/dist/components/UnsafeContent.d.ts +16 -0
- package/dist/components/UnsafeContent.js +26 -0
- package/dist/components/UnsafeContent.styles.d.ts +1 -0
- package/dist/components/UnsafeContent.styles.js +9 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +4 -0
- package/dist/internals/ExtendedBaseElement.d.ts +22 -0
- package/dist/internals/ExtendedBaseElement.js +59 -0
- package/dist/internals/observers.d.ts +10 -0
- package/dist/internals/observers.js +6 -0
- package/dist/layouts/SidebarLayout.d.ts +60 -0
- package/dist/layouts/SidebarLayout.js +183 -0
- package/dist/layouts/SidebarLayout.styles.d.ts +1 -0
- package/dist/layouts/SidebarLayout.styles.js +126 -0
- package/dist/layouts/SidebarLayout.types.d.ts +25 -0
- package/dist/types/AppConfigs.d.ts +54 -0
- package/dist/types/AppOptions.d.ts +12 -0
- package/dist/types/AppTypes.d.ts +6 -0
- package/package.json +53 -0
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { property as c } from "lit/decorators.js";
|
|
2
|
+
import { BaseElement as l } from "@iyulab/components/dist/components/BaseElement.js";
|
|
3
|
+
var p = Object.defineProperty, f = (o, e, i, r) => {
|
|
4
|
+
for (var t = void 0, s = o.length - 1, n; s >= 0; s--)
|
|
5
|
+
(n = o[s]) && (t = n(e, i, t) || t);
|
|
6
|
+
return t && p(e, i, t), t;
|
|
7
|
+
};
|
|
8
|
+
class y extends l {
|
|
9
|
+
static {
|
|
10
|
+
this.styles = super.styles;
|
|
11
|
+
}
|
|
12
|
+
static {
|
|
13
|
+
this.dependencies = {};
|
|
14
|
+
}
|
|
15
|
+
updated(e) {
|
|
16
|
+
super.updated?.(e), e.has("styles") && this.applyToParts(this.styles);
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* 현재 스타일 구성을 각 파트에 적용, host 포함
|
|
20
|
+
*/
|
|
21
|
+
applyToParts(e) {
|
|
22
|
+
if (!e) return;
|
|
23
|
+
const i = Object.entries(e);
|
|
24
|
+
for (const [r, t] of i)
|
|
25
|
+
if (t)
|
|
26
|
+
if (r === "host")
|
|
27
|
+
this.assignToObject(this, t);
|
|
28
|
+
else {
|
|
29
|
+
const s = this.renderRoot?.querySelector(`[part="${String(r)}"]`);
|
|
30
|
+
if (!s) continue;
|
|
31
|
+
this.assignToObject(s, t);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* 스타일 객체를 개별 프로퍼티 단위로 적용 (camelCase / kebab-case / custom props 처리)
|
|
36
|
+
*/
|
|
37
|
+
assignToObject(e, i) {
|
|
38
|
+
for (const [r, t] of Object.entries(i)) {
|
|
39
|
+
if (t == null) continue;
|
|
40
|
+
const s = String(t);
|
|
41
|
+
if (r.startsWith("--") || r.includes("-")) {
|
|
42
|
+
e.style.setProperty(r, s);
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
try {
|
|
46
|
+
e.style[r] = s;
|
|
47
|
+
} catch {
|
|
48
|
+
const n = r.replace(/[A-Z]/g, (a) => "-" + a.toLowerCase());
|
|
49
|
+
e.style.setProperty(n, s);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
f([
|
|
55
|
+
c({ type: Object, attribute: !1 })
|
|
56
|
+
], y.prototype, "styles");
|
|
57
|
+
export {
|
|
58
|
+
y as ExtendedBaseElement
|
|
59
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { IObservableValue } from 'mobx';
|
|
2
|
+
import { ScreenSize } from '../types/AppTypes';
|
|
3
|
+
/**
|
|
4
|
+
* 현재 화면 크기 상태
|
|
5
|
+
*/
|
|
6
|
+
export declare const screen: IObservableValue<ScreenSize>;
|
|
7
|
+
/**
|
|
8
|
+
* 로딩 진행 상태 (0 - 100)
|
|
9
|
+
*/
|
|
10
|
+
export declare const progress: IObservableValue<number>;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { nothing, PropertyValues, TemplateResult } from 'lit';
|
|
2
|
+
import { BaseElement } from '@iyulab/components/dist/components/BaseElement.js';
|
|
3
|
+
import { ExtendedBaseElement } from '../internals/ExtendedBaseElement';
|
|
4
|
+
import { ProgressBar } from '../components/ProgressBar';
|
|
5
|
+
import { UnsafeContent } from '../components/UnsafeContent';
|
|
6
|
+
import { SidebarLogo } from '../components/SidebarLogo';
|
|
7
|
+
import { SidebarSection } from '../components/SidebarSection';
|
|
8
|
+
import { SidebarGroup } from '../components/SidebarGroup';
|
|
9
|
+
import { SidebarLink } from '../components/SidebarLink';
|
|
10
|
+
import { SidebarButton } from '../components/SidebarButton';
|
|
11
|
+
import { SidebarLayoutConfig, SidebarState, SidebarParts } from './SidebarLayout.types';
|
|
12
|
+
/** 엘리먼트 타입 매핑 */
|
|
13
|
+
declare global {
|
|
14
|
+
interface HTMLElementTagNameMap {
|
|
15
|
+
'u-progress-bar': ProgressBar;
|
|
16
|
+
'u-unsafe-content': UnsafeContent;
|
|
17
|
+
'u-sidebar-logo': SidebarLogo;
|
|
18
|
+
'u-sidebar-section': SidebarSection;
|
|
19
|
+
'u-sidebar-group': SidebarGroup;
|
|
20
|
+
'u-sidebar-link': SidebarLink;
|
|
21
|
+
'u-sidebar-button': SidebarButton;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* SidebarLayout - 반응형 사이드바 레이아웃
|
|
26
|
+
* - large: docked / slim
|
|
27
|
+
* - medium: slim / modal
|
|
28
|
+
* - small: closed / modal
|
|
29
|
+
*
|
|
30
|
+
* - docked: 사이드바가 펼침 상태로 표시됨
|
|
31
|
+
* - slim: 아이콘만 표시되는 슬림 모드
|
|
32
|
+
* - modal: 오버레이로 사이드바가 펼침 상태로 표시되는 모달 모드, 백드롭 클릭 시 닫힘
|
|
33
|
+
* - closed: 사이드바가 왼쪽에 완전히 숨겨진 상태
|
|
34
|
+
*/
|
|
35
|
+
export declare class SidebarLayout extends ExtendedBaseElement<SidebarParts> {
|
|
36
|
+
static styles: import('lit').CSSResultGroup[];
|
|
37
|
+
static dependencies: Record<string, typeof BaseElement>;
|
|
38
|
+
/** 반응형 상태 관리를 위한 MobX 반응 해제 함수들 */
|
|
39
|
+
private disposers;
|
|
40
|
+
progressEl?: ProgressBar;
|
|
41
|
+
/** 사이드바 상태 */
|
|
42
|
+
state: SidebarState;
|
|
43
|
+
/** 사이드바 레이아웃 설정 */
|
|
44
|
+
config?: SidebarLayoutConfig;
|
|
45
|
+
connectedCallback(): void;
|
|
46
|
+
disconnectedCallback(): void;
|
|
47
|
+
protected updated(changedProperties: PropertyValues): void;
|
|
48
|
+
render(): TemplateResult<1> | typeof nothing;
|
|
49
|
+
/** 사이드바 아이템 렌더링 */
|
|
50
|
+
private renderItem;
|
|
51
|
+
/** 화면 크기 변경에 따른 사이드바 상태 업데이트 */
|
|
52
|
+
private updateState;
|
|
53
|
+
/** 사이드바 토글 핸들러 */
|
|
54
|
+
private toggleState;
|
|
55
|
+
/**
|
|
56
|
+
* 모달 상태에서 사이드바 닫기
|
|
57
|
+
* 백드롭 클릭 or 라우트 변경 시
|
|
58
|
+
*/
|
|
59
|
+
private handleCloseModal;
|
|
60
|
+
}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { nothing as u, html as i } from "lit";
|
|
2
|
+
import { query as h, state as f, property as m, customElement as g } from "lit/decorators.js";
|
|
3
|
+
import { repeat as l } from "lit/directives/repeat.js";
|
|
4
|
+
import { autorun as b } from "mobx";
|
|
5
|
+
import { IconButton as $ } from "@iyulab/components/dist/components/icon-button/IconButton.js";
|
|
6
|
+
import { screen as p, progress as y } from "../internals/observers.js";
|
|
7
|
+
import { ExtendedBaseElement as v } from "../internals/ExtendedBaseElement.js";
|
|
8
|
+
import { ProgressBar as _ } from "../components/ProgressBar.js";
|
|
9
|
+
import { UnsafeContent as S } from "../components/UnsafeContent.js";
|
|
10
|
+
import { SidebarLogo as k } from "../components/SidebarLogo.js";
|
|
11
|
+
import { SidebarSection as C } from "../components/SidebarSection.js";
|
|
12
|
+
import { SidebarGroup as w } from "../components/SidebarGroup.js";
|
|
13
|
+
import { SidebarLink as E } from "../components/SidebarLink.js";
|
|
14
|
+
import { SidebarButton as O } from "../components/SidebarButton.js";
|
|
15
|
+
import { styles as I } from "./SidebarLayout.styles.js";
|
|
16
|
+
var M = Object.defineProperty, P = Object.getOwnPropertyDescriptor, z = Object.getPrototypeOf, B = Reflect.get, n = (e, t, s, a) => {
|
|
17
|
+
for (var r = a > 1 ? void 0 : a ? P(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 && M(t, s, r), r;
|
|
20
|
+
}, L = (e, t, s) => B(z(e), s, t);
|
|
21
|
+
let o = class extends v {
|
|
22
|
+
constructor() {
|
|
23
|
+
super(...arguments), this.disposers = [], this.state = "docked", this.toggleState = () => {
|
|
24
|
+
const e = p.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.handleCloseModal = () => {
|
|
27
|
+
const e = p.get();
|
|
28
|
+
this.state === "modal" && (e === "medium" ? this.state = "slim" : e === "small" && (this.state = "closed"));
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
connectedCallback() {
|
|
32
|
+
super.connectedCallback(), this.disposers.push(b(() => {
|
|
33
|
+
const e = p.get();
|
|
34
|
+
this.updateState(e);
|
|
35
|
+
})), this.disposers.push(b(() => {
|
|
36
|
+
const e = y.get();
|
|
37
|
+
this.progressEl && (this.progressEl.value = e);
|
|
38
|
+
})), window.addEventListener("route-begin", this.handleCloseModal);
|
|
39
|
+
}
|
|
40
|
+
disconnectedCallback() {
|
|
41
|
+
this.disposers.forEach((e) => e()), window.removeEventListener("route-begin", this.handleCloseModal), super.disconnectedCallback();
|
|
42
|
+
}
|
|
43
|
+
updated(e) {
|
|
44
|
+
super.updated(e), e.has("config") && (this.styles = this.config?.styles);
|
|
45
|
+
}
|
|
46
|
+
render() {
|
|
47
|
+
return this.config ? i`
|
|
48
|
+
<!-- Sidebar -->
|
|
49
|
+
<aside class="sidebar" part="sidebar" state=${this.state}>
|
|
50
|
+
<!-- Sidebar Header -->
|
|
51
|
+
<div class="sidebar-header" part="sidebar-header">
|
|
52
|
+
<u-sidebar-logo
|
|
53
|
+
.compact="${this.state === "slim"}"
|
|
54
|
+
.type=${this.config.logo.type}
|
|
55
|
+
.image="${this.config.logo.image}"
|
|
56
|
+
.icon="${this.config.logo.icon}"
|
|
57
|
+
.label="${this.config.logo.label}"
|
|
58
|
+
.styles="${this.config.logo.styles}"
|
|
59
|
+
@click="${this.config.logo.onClick}"
|
|
60
|
+
></u-sidebar-logo>
|
|
61
|
+
<u-icon-button class="sidebar-toggler" part="sidebar-toggler"
|
|
62
|
+
name=${this.state === "closed" ? "chevron-right" : "layout-sidebar"}
|
|
63
|
+
@click="${this.toggleState}"
|
|
64
|
+
></u-icon-button>
|
|
65
|
+
</div>
|
|
66
|
+
|
|
67
|
+
<!-- Sidebar Navigation Menu -->
|
|
68
|
+
<nav class="sidebar-menu" part="sidebar-menu">
|
|
69
|
+
${l(
|
|
70
|
+
this.config.menu ?? [],
|
|
71
|
+
(e, t) => t,
|
|
72
|
+
(e, t) => this.renderItem(e)
|
|
73
|
+
)}
|
|
74
|
+
</nav>
|
|
75
|
+
|
|
76
|
+
<!-- Sidebar Footer -->
|
|
77
|
+
<div class="sidebar-footer" part="sidebar-footer">
|
|
78
|
+
${l(
|
|
79
|
+
this.config.footer ?? [],
|
|
80
|
+
(e, t) => t,
|
|
81
|
+
(e, t) => this.renderItem(e)
|
|
82
|
+
)}
|
|
83
|
+
</div>
|
|
84
|
+
</aside>
|
|
85
|
+
|
|
86
|
+
<!-- Main Content -->
|
|
87
|
+
<div class="main" part="main">
|
|
88
|
+
<u-progress-bar part="progress"></u-progress-bar>
|
|
89
|
+
|
|
90
|
+
<u-outlet></u-outlet>
|
|
91
|
+
</div>
|
|
92
|
+
|
|
93
|
+
<!-- Backdrop for modal state -->
|
|
94
|
+
<div class="backdrop"
|
|
95
|
+
?hidden="${this.state !== "modal"}"
|
|
96
|
+
@click="${this.handleCloseModal}"
|
|
97
|
+
></div>
|
|
98
|
+
` : u;
|
|
99
|
+
}
|
|
100
|
+
/** 사이드바 아이템 렌더링 */
|
|
101
|
+
renderItem(e) {
|
|
102
|
+
return e ? e.type === "group" ? i`
|
|
103
|
+
<u-sidebar-group
|
|
104
|
+
?compact=${this.state === "slim"}
|
|
105
|
+
?collapsed="${e.collapsed ?? !1}"
|
|
106
|
+
.icon="${e.icon}"
|
|
107
|
+
.label="${e.label}"
|
|
108
|
+
.items="${e.items}"
|
|
109
|
+
.styles="${e.styles}">
|
|
110
|
+
${l(
|
|
111
|
+
e.items,
|
|
112
|
+
(t, s) => s,
|
|
113
|
+
(t, s) => this.renderItem(t)
|
|
114
|
+
)}
|
|
115
|
+
</u-sidebar-group>
|
|
116
|
+
` : e.type === "section" ? i`
|
|
117
|
+
<u-sidebar-section
|
|
118
|
+
?compact=${this.state === "slim"}
|
|
119
|
+
.mainTitle="${e.title}"
|
|
120
|
+
.subTitle="${e.subTitle}"
|
|
121
|
+
.items="${e.items}"
|
|
122
|
+
.styles="${e.styles}">
|
|
123
|
+
${l(
|
|
124
|
+
e.items,
|
|
125
|
+
(t, s) => s,
|
|
126
|
+
(t, s) => this.renderItem(t)
|
|
127
|
+
)}
|
|
128
|
+
</u-sidebar-section>
|
|
129
|
+
` : e.type === "button" ? i`
|
|
130
|
+
<u-sidebar-button
|
|
131
|
+
?compact=${this.state === "slim"}
|
|
132
|
+
.icon="${e.icon}"
|
|
133
|
+
.label="${e.label}"
|
|
134
|
+
.styles="${e.styles}"
|
|
135
|
+
@click="${e.onClick}"
|
|
136
|
+
></u-sidebar-button>
|
|
137
|
+
` : e.type === "content" ? i`
|
|
138
|
+
<u-sidebar-content
|
|
139
|
+
?compact=${this.state === "slim"}
|
|
140
|
+
.content="${e.content}"
|
|
141
|
+
></u-sidebar-content>
|
|
142
|
+
` : i`
|
|
143
|
+
<u-sidebar-link
|
|
144
|
+
?compact=${this.state === "slim"}
|
|
145
|
+
.icon="${e.icon}"
|
|
146
|
+
.label="${e.label}"
|
|
147
|
+
.href="${e.href}"
|
|
148
|
+
.pattern="${e.pattern}"
|
|
149
|
+
.styles="${e.styles}"
|
|
150
|
+
></u-sidebar-link>
|
|
151
|
+
` : u;
|
|
152
|
+
}
|
|
153
|
+
/** 화면 크기 변경에 따른 사이드바 상태 업데이트 */
|
|
154
|
+
updateState(e) {
|
|
155
|
+
e === "large" ? this.state = "docked" : e === "medium" ? this.state = "slim" : e === "small" ? this.state = "closed" : console.warn("Unknown screen size:", e);
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
o.styles = [L(o, o, "styles"), I];
|
|
159
|
+
o.dependencies = {
|
|
160
|
+
"u-icon-button": $,
|
|
161
|
+
"u-progress-bar": _,
|
|
162
|
+
"u-unsafe-content": S,
|
|
163
|
+
"u-sidebar-logo": k,
|
|
164
|
+
"u-sidebar-section": C,
|
|
165
|
+
"u-sidebar-group": w,
|
|
166
|
+
"u-sidebar-link": E,
|
|
167
|
+
"u-sidebar-button": O
|
|
168
|
+
};
|
|
169
|
+
n([
|
|
170
|
+
h("u-progress-bar")
|
|
171
|
+
], o.prototype, "progressEl", 2);
|
|
172
|
+
n([
|
|
173
|
+
f()
|
|
174
|
+
], o.prototype, "state", 2);
|
|
175
|
+
n([
|
|
176
|
+
m({ type: Object })
|
|
177
|
+
], o.prototype, "config", 2);
|
|
178
|
+
o = n([
|
|
179
|
+
g("app-sidebar-layout")
|
|
180
|
+
], o);
|
|
181
|
+
export {
|
|
182
|
+
o as SidebarLayout
|
|
183
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const styles: import('lit').CSSResult;
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { css as o } from "lit";
|
|
2
|
+
const r = o`
|
|
3
|
+
:host {
|
|
4
|
+
position: relative;
|
|
5
|
+
display: flex;
|
|
6
|
+
flex-direction: row;
|
|
7
|
+
width: 100vw;
|
|
8
|
+
height: 100vh;
|
|
9
|
+
overflow: hidden;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/* Sidebar Container */
|
|
13
|
+
.sidebar {
|
|
14
|
+
position: relative;
|
|
15
|
+
z-index: 1000;
|
|
16
|
+
display: flex;
|
|
17
|
+
flex-direction: column;
|
|
18
|
+
justify-content: space-between;
|
|
19
|
+
gap: 8px;
|
|
20
|
+
width: 260px;
|
|
21
|
+
height: 100vh;
|
|
22
|
+
background: var(--u-panel-bg-color);
|
|
23
|
+
border-right: 1px solid var(--u-border-color);
|
|
24
|
+
box-shadow: 0 2px 8px var(--u-shadow-weak);
|
|
25
|
+
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
26
|
+
}
|
|
27
|
+
/* Sidebar states */
|
|
28
|
+
.sidebar[state="slim"] {
|
|
29
|
+
width: 64px;
|
|
30
|
+
}
|
|
31
|
+
.sidebar[state="slim"] .sidebar-header {
|
|
32
|
+
flex-direction: column;
|
|
33
|
+
}
|
|
34
|
+
.sidebar[state="closed"] {
|
|
35
|
+
position: fixed;
|
|
36
|
+
top: 0;
|
|
37
|
+
left: -260px;
|
|
38
|
+
bottom: 0;
|
|
39
|
+
width: 260px;
|
|
40
|
+
}
|
|
41
|
+
.sidebar[state="closed"] .sidebar-toggler {
|
|
42
|
+
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;
|
|
49
|
+
}
|
|
50
|
+
.sidebar[state="closed"] .sidebar-toggler:hover {
|
|
51
|
+
transform: translateX(24px);
|
|
52
|
+
}
|
|
53
|
+
.sidebar[state="modal"] {
|
|
54
|
+
position: fixed;
|
|
55
|
+
top: 0;
|
|
56
|
+
left: 0;
|
|
57
|
+
bottom: 0;
|
|
58
|
+
width: 260px;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/* Sidebar Toggler Button */
|
|
62
|
+
.sidebar-toggler {
|
|
63
|
+
font-size: 20px;
|
|
64
|
+
border: none;
|
|
65
|
+
background: transparent;
|
|
66
|
+
cursor: col-resize;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/* Sidebar Header */
|
|
70
|
+
.sidebar-header {
|
|
71
|
+
display: flex;
|
|
72
|
+
flex-direction: row;
|
|
73
|
+
align-items: center;
|
|
74
|
+
justify-content: space-between;
|
|
75
|
+
gap: 8px;
|
|
76
|
+
padding: 16px 12px;
|
|
77
|
+
border-bottom: 1px solid var(--u-border-color-weak);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/* Sidebar Menu */
|
|
81
|
+
.sidebar-menu {
|
|
82
|
+
flex: 1;
|
|
83
|
+
display: flex;
|
|
84
|
+
flex-direction: column;
|
|
85
|
+
gap: 4px;
|
|
86
|
+
padding: 8px;
|
|
87
|
+
overflow-y: auto;
|
|
88
|
+
overflow-x: hidden;
|
|
89
|
+
|
|
90
|
+
scrollbar-width: thin;
|
|
91
|
+
scrollbar-color: var(--u-scrollbar-color) var(--u-scrollbar-track-color);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/* Sidebar Footer */
|
|
95
|
+
.sidebar-footer {
|
|
96
|
+
display: flex;
|
|
97
|
+
flex-direction: column;
|
|
98
|
+
gap: 4px;
|
|
99
|
+
padding: 8px;
|
|
100
|
+
border-top: 1px solid var(--u-border-color-weak);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/* Backdrop for modal mode */
|
|
104
|
+
.backdrop {
|
|
105
|
+
content: '';
|
|
106
|
+
position: absolute;
|
|
107
|
+
z-index: 100;
|
|
108
|
+
top: 0;
|
|
109
|
+
left: 0;
|
|
110
|
+
right: 0;
|
|
111
|
+
bottom: 0;
|
|
112
|
+
background: var(--u-overlay-bg-color);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/* Main Content */
|
|
116
|
+
.main {
|
|
117
|
+
position: relative;
|
|
118
|
+
flex: 1;
|
|
119
|
+
display: block;
|
|
120
|
+
overflow: hidden;
|
|
121
|
+
background: var(--u-bg-color);
|
|
122
|
+
}
|
|
123
|
+
`;
|
|
124
|
+
export {
|
|
125
|
+
r as styles
|
|
126
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { StyleMap } from '../types/AppTypes';
|
|
2
|
+
import { SidebarLogoConfig } from '../components/SidebarLogo';
|
|
3
|
+
import { SidebarLinkConfig } from '../components/SidebarLink';
|
|
4
|
+
import { SidebarSectionConfig } from '../components/SidebarSection';
|
|
5
|
+
import { SidebarGroupConfig } from '../components/SidebarGroup';
|
|
6
|
+
import { SidebarButtonConfig } from '../components/SidebarButton';
|
|
7
|
+
import { UnsafeContentConfig } from '../components/UnsafeContent';
|
|
8
|
+
/** 사이드바 레이아웃 컴포넌트의 요소(part) 타입 */
|
|
9
|
+
export type SidebarParts = 'host' | 'sidebar' | 'sidebar-toggler' | 'sidebar-header' | 'sidebar-menu' | 'sidebar-footer' | 'main' | 'progress';
|
|
10
|
+
/** 사이드바 상태 타입 */
|
|
11
|
+
export type SidebarState = 'docked' | 'modal' | 'slim' | 'closed';
|
|
12
|
+
/** union: section | group | link | button */
|
|
13
|
+
export type SidebarItem = (SidebarLinkConfig | SidebarSectionConfig | SidebarGroupConfig | SidebarButtonConfig | UnsafeContentConfig);
|
|
14
|
+
/** 사이드바 전체 설정 (루트) */
|
|
15
|
+
export interface SidebarLayoutConfig {
|
|
16
|
+
type: 'sidebar';
|
|
17
|
+
/** 최상단 앱 로고 */
|
|
18
|
+
logo: SidebarLogoConfig;
|
|
19
|
+
/** 상단/메인 메뉴 항목들 */
|
|
20
|
+
menu?: SidebarItem[];
|
|
21
|
+
/** 하단(footer)에 고정해서 렌더할 항목들 */
|
|
22
|
+
footer?: SidebarItem[];
|
|
23
|
+
/** 사이드바 스타일 맵 */
|
|
24
|
+
styles?: StyleMap<SidebarParts>;
|
|
25
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { InitOptions, Module, Newable, NewableModule } from 'i18next';
|
|
2
|
+
import { RouteConfig } from '@iyulab/router';
|
|
3
|
+
import { ThemeInitOptions } from '@iyulab/components/dist/utilities/theme.js';
|
|
4
|
+
import { SidebarLayoutConfig } from '../layouts/SidebarLayout.types';
|
|
5
|
+
/**
|
|
6
|
+
* i18next의 초기화 옵션과 플러그인 배열을 포함한 다국어 설정
|
|
7
|
+
*/
|
|
8
|
+
export type LocaleInitOptions = InitOptions & {
|
|
9
|
+
/**
|
|
10
|
+
* i18next 플러그인 배열
|
|
11
|
+
* @description i18next.use() 메서드에 전달될 플러그인들의 배열입니다.
|
|
12
|
+
*/
|
|
13
|
+
plugins?: (Module | NewableModule<Module> | Newable<Module>)[];
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* 어플리케이션 레이아웃 설정
|
|
17
|
+
*/
|
|
18
|
+
export type LayoutConfig = (SidebarLayoutConfig) & {
|
|
19
|
+
/**
|
|
20
|
+
* 반응형 레이아웃 디자인을 위한 화면 크기의 브레이크포인트 설정
|
|
21
|
+
* @description 태블릿과 데스크탑의 최소 픽셀 너비를 차례로 지정합니다.
|
|
22
|
+
* @default [768, 1024]
|
|
23
|
+
*/
|
|
24
|
+
breakpoints?: [number, number];
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* 전체 애플리케이션 설정
|
|
28
|
+
*/
|
|
29
|
+
export interface AppConfig {
|
|
30
|
+
/**
|
|
31
|
+
* 현재 애플리케이션의 기본 경로 설정
|
|
32
|
+
* @default '/'
|
|
33
|
+
*/
|
|
34
|
+
basepath?: string;
|
|
35
|
+
/**
|
|
36
|
+
* 클라이언트 사이드 라우팅을 위한 설정 배열
|
|
37
|
+
*/
|
|
38
|
+
routes: RouteConfig[];
|
|
39
|
+
/**
|
|
40
|
+
* 애플리케이션의 스타일 테마 설정
|
|
41
|
+
*/
|
|
42
|
+
theme?: ThemeInitOptions;
|
|
43
|
+
/**
|
|
44
|
+
* i18next를 사용하는 다국어 설정
|
|
45
|
+
* @description i18next의 InitOptions와 플러그인 배열을 포함합니다. i18next를 사용하지 않는 경우 이 설정은 생략할 수 있습니다.
|
|
46
|
+
* @see 설정에 대한 자세한 내용은 {@link https://www.i18next.com/overview/configuration-options} 참조하십시오.
|
|
47
|
+
*/
|
|
48
|
+
locales?: LocaleInitOptions;
|
|
49
|
+
/**
|
|
50
|
+
* 애플리케이션을 구성하기 위한 레이아웃 설정
|
|
51
|
+
* @description 현재는 사이드바 레이아웃만 지원합니다.
|
|
52
|
+
*/
|
|
53
|
+
layout: LayoutConfig;
|
|
54
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { ScreenPosition } from '@iyulab/components/dist/utilities/notifier.js';
|
|
2
|
+
export interface NotificationOptions {
|
|
3
|
+
title?: string;
|
|
4
|
+
duration?: number;
|
|
5
|
+
position?: ScreenPosition;
|
|
6
|
+
}
|
|
7
|
+
export interface DialogOptions {
|
|
8
|
+
title?: string;
|
|
9
|
+
message?: string;
|
|
10
|
+
confirmText?: string;
|
|
11
|
+
cancelText?: string;
|
|
12
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@iyulab/modern-app",
|
|
3
|
+
"description": "web-framework by iyulab based on react, lit-element",
|
|
4
|
+
"version": "0.2.0",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"iyulab",
|
|
7
|
+
"web-framework",
|
|
8
|
+
"react",
|
|
9
|
+
"lit-element"
|
|
10
|
+
],
|
|
11
|
+
"license": "MIT",
|
|
12
|
+
"author": "iyulab",
|
|
13
|
+
"repository": {
|
|
14
|
+
"type": "git",
|
|
15
|
+
"url": "https://github.com/iyulab/node-modern-app.git"
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"dist",
|
|
19
|
+
"package.json",
|
|
20
|
+
"README.md",
|
|
21
|
+
"CHANGELOG.md",
|
|
22
|
+
"LICENSE"
|
|
23
|
+
],
|
|
24
|
+
"type": "module",
|
|
25
|
+
"types": "dist/index.d.ts",
|
|
26
|
+
"exports": {
|
|
27
|
+
".": {
|
|
28
|
+
"types": "./dist/index.d.ts",
|
|
29
|
+
"import": "./dist/index.js"
|
|
30
|
+
},
|
|
31
|
+
"./dist/*": {
|
|
32
|
+
"types": "./dist/*",
|
|
33
|
+
"import": "./dist/*"
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
"scripts": {
|
|
37
|
+
"start": "vite",
|
|
38
|
+
"build": "vite build"
|
|
39
|
+
},
|
|
40
|
+
"dependencies": {
|
|
41
|
+
"@iyulab/components": "^0.1.0",
|
|
42
|
+
"@iyulab/router": "^0.5.0",
|
|
43
|
+
"lit": "^3.3.1",
|
|
44
|
+
"mobx": "^6.15.0",
|
|
45
|
+
"i18next": "^25.6.2"
|
|
46
|
+
},
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"@types/node": "^24.10.1",
|
|
49
|
+
"typescript": "^5.9.3",
|
|
50
|
+
"vite": "^7.2.2",
|
|
51
|
+
"vite-plugin-dts": "^4.5.4"
|
|
52
|
+
}
|
|
53
|
+
}
|