@sonhoseong/mfa-lib 1.3.0 → 1.3.1

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.
@@ -0,0 +1,254 @@
1
+ /* ============================================
2
+ AppSidebar - KOMCA 패턴
3
+ 사이드바 네비게이션 스타일
4
+ ============================================ */
5
+
6
+ .app-sidebar {
7
+ position: fixed;
8
+ top: 0;
9
+ left: 0;
10
+ width: 260px;
11
+ height: 100vh;
12
+ background: #ffffff;
13
+ border-right: 1px solid var(--color-border, #E2E8F0);
14
+ display: flex;
15
+ flex-direction: column;
16
+ z-index: 100;
17
+ transition: transform 0.3s ease;
18
+ }
19
+
20
+ /* Sidebar Header */
21
+ .sidebar-header {
22
+ padding: 20px;
23
+ border-bottom: 1px solid var(--color-border, #E2E8F0);
24
+ }
25
+
26
+ .sidebar-logo {
27
+ display: flex;
28
+ align-items: center;
29
+ gap: 12px;
30
+ padding: 8px 12px;
31
+ background: transparent;
32
+ border: none;
33
+ border-radius: 12px;
34
+ cursor: pointer;
35
+ transition: background 0.2s ease;
36
+ width: 100%;
37
+ }
38
+
39
+ .sidebar-logo:hover {
40
+ background: var(--color-bg-secondary, #F8FAFC);
41
+ }
42
+
43
+ .sidebar-app-name {
44
+ font-size: 18px;
45
+ font-weight: 700;
46
+ color: var(--color-primary, #1E3A5F);
47
+ }
48
+
49
+ /* Sidebar Navigation */
50
+ .sidebar-nav {
51
+ flex: 1;
52
+ overflow-y: auto;
53
+ padding: 16px 12px;
54
+ }
55
+
56
+ .sidebar-menu {
57
+ list-style: none;
58
+ margin: 0;
59
+ padding: 0;
60
+ }
61
+
62
+ .sidebar-menu-item {
63
+ margin-bottom: 4px;
64
+ }
65
+
66
+ .sidebar-menu-btn {
67
+ display: flex;
68
+ align-items: center;
69
+ gap: 12px;
70
+ width: 100%;
71
+ padding: 12px 16px;
72
+ font-family: inherit;
73
+ font-size: 14px;
74
+ font-weight: 500;
75
+ color: var(--color-text-secondary, #64748B);
76
+ background: transparent;
77
+ border: none;
78
+ border-radius: 10px;
79
+ cursor: pointer;
80
+ transition: all 0.2s ease;
81
+ text-align: left;
82
+ }
83
+
84
+ .sidebar-menu-btn:hover {
85
+ background: var(--color-bg-secondary, #F8FAFC);
86
+ color: var(--color-primary, #1E3A5F);
87
+ }
88
+
89
+ .sidebar-menu-btn.active {
90
+ background: rgba(14, 165, 233, 0.1);
91
+ color: var(--color-accent, #0EA5E9);
92
+ }
93
+
94
+ .sidebar-menu-btn.active .sidebar-menu-icon {
95
+ color: var(--color-accent, #0EA5E9);
96
+ }
97
+
98
+ .sidebar-menu-btn.child {
99
+ padding-left: 48px;
100
+ font-size: 13px;
101
+ }
102
+
103
+ .sidebar-menu-icon {
104
+ display: flex;
105
+ align-items: center;
106
+ justify-content: center;
107
+ width: 20px;
108
+ height: 20px;
109
+ color: var(--color-text-muted, #94A3B8);
110
+ transition: color 0.2s ease;
111
+ }
112
+
113
+ .sidebar-menu-btn:hover .sidebar-menu-icon {
114
+ color: var(--color-primary, #1E3A5F);
115
+ }
116
+
117
+ .sidebar-menu-title {
118
+ flex: 1;
119
+ }
120
+
121
+ .sidebar-menu-arrow {
122
+ color: var(--color-text-muted, #94A3B8);
123
+ transition: transform 0.2s ease;
124
+ }
125
+
126
+ .sidebar-menu-arrow.expanded {
127
+ transform: rotate(90deg);
128
+ }
129
+
130
+ /* Submenu */
131
+ .sidebar-submenu {
132
+ list-style: none;
133
+ margin: 4px 0 0 0;
134
+ padding: 0;
135
+ }
136
+
137
+ /* Sidebar Footer */
138
+ .sidebar-footer {
139
+ padding: 16px;
140
+ border-top: 1px solid var(--color-border, #E2E8F0);
141
+ }
142
+
143
+ .sidebar-user {
144
+ display: flex;
145
+ align-items: center;
146
+ gap: 12px;
147
+ padding: 8px;
148
+ background: var(--color-bg-secondary, #F8FAFC);
149
+ border-radius: 12px;
150
+ }
151
+
152
+ .sidebar-user-avatar {
153
+ width: 40px;
154
+ height: 40px;
155
+ display: flex;
156
+ align-items: center;
157
+ justify-content: center;
158
+ background: linear-gradient(135deg, var(--color-primary, #1E3A5F), var(--color-accent, #0EA5E9));
159
+ color: white;
160
+ font-size: 16px;
161
+ font-weight: 600;
162
+ border-radius: 10px;
163
+ flex-shrink: 0;
164
+ }
165
+
166
+ .sidebar-user-info {
167
+ flex: 1;
168
+ min-width: 0;
169
+ display: flex;
170
+ flex-direction: column;
171
+ }
172
+
173
+ .sidebar-user-name {
174
+ font-size: 14px;
175
+ font-weight: 600;
176
+ color: var(--color-primary, #1E3A5F);
177
+ white-space: nowrap;
178
+ overflow: hidden;
179
+ text-overflow: ellipsis;
180
+ }
181
+
182
+ .sidebar-user-email {
183
+ font-size: 12px;
184
+ color: var(--color-text-muted, #94A3B8);
185
+ white-space: nowrap;
186
+ overflow: hidden;
187
+ text-overflow: ellipsis;
188
+ }
189
+
190
+ .sidebar-logout-btn {
191
+ display: flex;
192
+ align-items: center;
193
+ justify-content: center;
194
+ width: 36px;
195
+ height: 36px;
196
+ background: transparent;
197
+ border: none;
198
+ border-radius: 8px;
199
+ color: var(--color-text-muted, #94A3B8);
200
+ cursor: pointer;
201
+ transition: all 0.2s ease;
202
+ flex-shrink: 0;
203
+ }
204
+
205
+ .sidebar-logout-btn:hover {
206
+ background: #FEE2E2;
207
+ color: #DC2626;
208
+ }
209
+
210
+ .sidebar-login-btn {
211
+ display: flex;
212
+ align-items: center;
213
+ justify-content: center;
214
+ gap: 8px;
215
+ width: 100%;
216
+ padding: 12px;
217
+ font-family: inherit;
218
+ font-size: 14px;
219
+ font-weight: 600;
220
+ color: white;
221
+ background: var(--color-primary, #1E3A5F);
222
+ border: none;
223
+ border-radius: 10px;
224
+ cursor: pointer;
225
+ transition: all 0.2s ease;
226
+ }
227
+
228
+ .sidebar-login-btn:hover {
229
+ background: var(--color-accent, #0EA5E9);
230
+ }
231
+
232
+ /* Layout with Sidebar */
233
+ .has-sidebar {
234
+ margin-left: 260px;
235
+ }
236
+
237
+ .has-sidebar .main-content {
238
+ min-height: 100vh;
239
+ }
240
+
241
+ /* Responsive */
242
+ @media (max-width: 768px) {
243
+ .app-sidebar {
244
+ transform: translateX(-100%);
245
+ }
246
+
247
+ .app-sidebar.open {
248
+ transform: translateX(0);
249
+ }
250
+
251
+ .has-sidebar {
252
+ margin-left: 0;
253
+ }
254
+ }
@@ -0,0 +1,37 @@
1
+ /**
2
+ * AppSidebar - KOMCA 패턴
3
+ *
4
+ * 사이드바 네비게이션 컴포넌트
5
+ * Remote 앱 단독 실행 시 사용
6
+ */
7
+ import React from 'react';
8
+ export interface SidebarMenuItem {
9
+ id: string;
10
+ title: string;
11
+ path?: string;
12
+ icon?: React.ReactNode;
13
+ children?: SidebarMenuItem[];
14
+ }
15
+ export interface AppSidebarProps {
16
+ /** 앱 이름 */
17
+ appName?: string;
18
+ /** 로그인 여부 */
19
+ isAuthenticated?: boolean;
20
+ /** 사용자 이름 */
21
+ userName?: string;
22
+ /** 사용자 이메일 */
23
+ userEmail?: string;
24
+ /** 메뉴 아이템 */
25
+ menuItems?: SidebarMenuItem[];
26
+ /** 로그아웃 핸들러 */
27
+ onLogout?: () => void;
28
+ /** 네비게이션 핸들러 */
29
+ onNavigate?: (path: string) => void;
30
+ /** 현재 경로 */
31
+ currentPath?: string;
32
+ /** 커스텀 로고 */
33
+ logo?: React.ReactNode;
34
+ }
35
+ export declare function AppSidebar({ appName, isAuthenticated, userName, userEmail, menuItems, onLogout, onNavigate, currentPath, logo, }: AppSidebarProps): import("react/jsx-runtime").JSX.Element;
36
+ export default AppSidebar;
37
+ //# sourceMappingURL=AppSidebar.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AppSidebar.d.ts","sourceRoot":"","sources":["../../../src/components/navigation/AppSidebar.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAgC,MAAM,OAAO,CAAC;AAErD,MAAM,WAAW,eAAe;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,QAAQ,CAAC,EAAE,eAAe,EAAE,CAAC;CAChC;AAED,MAAM,WAAW,eAAe;IAC5B,WAAW;IACX,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,aAAa;IACb,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,aAAa;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa;IACb,SAAS,CAAC,EAAE,eAAe,EAAE,CAAC;IAC9B,eAAe;IACf,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,gBAAgB;IAChB,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,YAAY;IACZ,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa;IACb,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC1B;AAED,wBAAgB,UAAU,CAAC,EACvB,OAAe,EACf,eAAuB,EACvB,QAAQ,EACR,SAAS,EACT,SAAc,EACd,QAAQ,EACR,UAAU,EACV,WAAiB,EACjB,IAAI,GACP,EAAE,eAAe,2CAyIjB;AAED,eAAe,UAAU,CAAC"}
@@ -0,0 +1,41 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * AppSidebar - KOMCA 패턴
4
+ *
5
+ * 사이드바 네비게이션 컴포넌트
6
+ * Remote 앱 단독 실행 시 사용
7
+ */
8
+ import { useState, useCallback } from 'react';
9
+ export function AppSidebar({ appName = 'MFA', isAuthenticated = false, userName, userEmail, menuItems = [], onLogout, onNavigate, currentPath = '/', logo, }) {
10
+ const [expandedMenus, setExpandedMenus] = useState(new Set());
11
+ const toggleMenu = useCallback((menuId) => {
12
+ setExpandedMenus(prev => {
13
+ const next = new Set(prev);
14
+ if (next.has(menuId)) {
15
+ next.delete(menuId);
16
+ }
17
+ else {
18
+ next.add(menuId);
19
+ }
20
+ return next;
21
+ });
22
+ }, []);
23
+ const handleNavigate = useCallback((path) => {
24
+ onNavigate?.(path);
25
+ }, [onNavigate]);
26
+ const renderMenuItem = (item, depth = 0) => {
27
+ const hasChildren = item.children && item.children.length > 0;
28
+ const isExpanded = expandedMenus.has(item.id);
29
+ const isActive = item.path === currentPath;
30
+ return (_jsxs("li", { className: "sidebar-menu-item", children: [_jsxs("button", { className: `sidebar-menu-btn ${isActive ? 'active' : ''} ${depth > 0 ? 'child' : ''}`, onClick: () => {
31
+ if (hasChildren) {
32
+ toggleMenu(item.id);
33
+ }
34
+ else if (item.path) {
35
+ handleNavigate(item.path);
36
+ }
37
+ }, children: [item.icon && _jsx("span", { className: "sidebar-menu-icon", children: item.icon }), _jsx("span", { className: "sidebar-menu-title", children: item.title }), hasChildren && (_jsx("svg", { className: `sidebar-menu-arrow ${isExpanded ? 'expanded' : ''}`, width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: _jsx("path", { d: "m9 18 6-6-6-6" }) }))] }), hasChildren && isExpanded && (_jsx("ul", { className: "sidebar-submenu", children: item.children.map(child => renderMenuItem(child, depth + 1)) }))] }, item.id));
38
+ };
39
+ return (_jsxs("aside", { className: "app-sidebar", children: [_jsx("div", { className: "sidebar-header", children: _jsxs("button", { className: "sidebar-logo", onClick: () => handleNavigate('/'), children: [logo || (_jsxs("svg", { viewBox: "0 0 48 48", fill: "none", width: "32", height: "32", children: [_jsx("rect", { x: "20", y: "2", width: "8", height: "16", rx: "4", fill: "#0EA5E9" }), _jsx("rect", { x: "6", y: "16", width: "36", height: "6", rx: "3", fill: "#0EA5E9" }), _jsx("ellipse", { cx: "24", cy: "36", rx: "18", ry: "12", fill: "#0EA5E9" }), _jsx("ellipse", { cx: "17", cy: "36", rx: "4", ry: "6", fill: "#FFFFFF" }), _jsx("ellipse", { cx: "31", cy: "36", rx: "4", ry: "6", fill: "#FFFFFF" })] })), _jsx("span", { className: "sidebar-app-name", children: appName })] }) }), _jsx("nav", { className: "sidebar-nav", children: menuItems.length > 0 ? (_jsx("ul", { className: "sidebar-menu", children: menuItems.map(item => renderMenuItem(item)) })) : (_jsx("ul", { className: "sidebar-menu", children: _jsx("li", { className: "sidebar-menu-item", children: _jsxs("button", { className: `sidebar-menu-btn ${currentPath === '/' ? 'active' : ''}`, onClick: () => handleNavigate('/'), children: [_jsx("span", { className: "sidebar-menu-icon", children: _jsxs("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [_jsx("path", { d: "m3 9 9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" }), _jsx("polyline", { points: "9 22 9 12 15 12 15 22" })] }) }), _jsx("span", { className: "sidebar-menu-title", children: "\uD648" })] }) }) })) }), _jsx("div", { className: "sidebar-footer", children: isAuthenticated ? (_jsxs("div", { className: "sidebar-user", children: [_jsx("div", { className: "sidebar-user-avatar", children: userName?.charAt(0).toUpperCase() || 'U' }), _jsxs("div", { className: "sidebar-user-info", children: [_jsx("span", { className: "sidebar-user-name", children: userName || '사용자' }), userEmail && _jsx("span", { className: "sidebar-user-email", children: userEmail })] }), _jsx("button", { className: "sidebar-logout-btn", onClick: onLogout, title: "\uB85C\uADF8\uC544\uC6C3", children: _jsxs("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [_jsx("path", { d: "M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4" }), _jsx("polyline", { points: "16 17 21 12 16 7" }), _jsx("line", { x1: "21", y1: "12", x2: "9", y2: "12" })] }) })] })) : (_jsxs("button", { className: "sidebar-login-btn", onClick: () => handleNavigate('/login'), children: [_jsxs("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [_jsx("path", { d: "M15 3h4a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2h-4" }), _jsx("polyline", { points: "10 17 15 12 10 7" }), _jsx("line", { x1: "15", y1: "12", x2: "3", y2: "12" })] }), "\uB85C\uADF8\uC778"] })) })] }));
40
+ }
41
+ export default AppSidebar;
@@ -1,3 +1,4 @@
1
1
  export * from './StickyNav';
2
2
  export * from './AppNavbar';
3
+ export * from './AppSidebar';
3
4
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/navigation/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/navigation/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC"}
@@ -1,2 +1,3 @@
1
1
  export * from './StickyNav';
2
2
  export * from './AppNavbar';
3
+ export * from './AppSidebar';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sonhoseong/mfa-lib",
3
- "version": "1.3.0",
3
+ "version": "1.3.1",
4
4
  "description": "MFA 공통 라이브러리 - KOMCA 패턴",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",