@nine-lab/nine-ux 0.1.140 → 0.1.142

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.
@@ -1,156 +1,377 @@
1
- /* --- [1] 부모 컴포넌트: nine-side-menu (Shadow DOM 내부용) --- */
1
+ @charset "utf-8";
2
+
3
+ /* ==========================================================================
4
+ [MAIN INTEGRATED SIDE MENU COMPONENT]
5
+ ========================================================================== */
2
6
  :host(nine-side-menu) {
3
- opacity: 0.95; position: fixed; top: 0; bottom: 0; left: 0;
4
- display: flex; flex-direction: column; z-index: 1002;
5
- color: #fff; font-weight: 200; background: #181A31;
7
+ opacity: 0.95;
8
+ position: fixed;
9
+ top: 0;
10
+ bottom: 0;
11
+ left: 0;
12
+ display: block;
13
+ z-index: 1002;
14
+ color: #fff;
15
+ font-weight: 200;
16
+ background: #181A31;
17
+ --background: #333;
18
+ -webkit-box-shadow: 4px 4px 10px rgba(69, 65, 78, .06);
19
+ -moz-box-shadow: 4px 4px 10px rgba(69, 65, 78, .06);
6
20
  box-shadow: 4px 4px 10px rgba(69, 65, 78, .06);
7
- overflow: hidden; width: var(--min-width);
21
+ overflow: hidden;
22
+ width: var(--min-width);
23
+ --transition: all .3s;
8
24
  transition: width 0.2s ease-in-out;
9
- }
10
25
 
11
- :host(nine-side-menu.collapse) { width: var(--min-width); }
12
- :host(nine-side-menu:not(.collapse)), :host(nine-side-menu.hover) { width: var(--max-width); }
26
+ /* 상태별 전체 너비 및 바운스 애니메이션 */
27
+ &.collapse {
28
+ width: var(--min-width);
29
+ }
30
+ &:not(.collapse) {
31
+ width: var(--max-width);
32
+ }
33
+ &.collapse.hover {
34
+ width: var(--max-width);
35
+ animation: nineMenuBounce 0.5s ease-in-out 1;
36
+ }
13
37
 
14
- /* 내부 레이아웃 */
15
- .body-wrapper {
16
- display: flex; flex-direction: column; align-items: flex-start;
17
- margin-top: 32px; width: 100%; flex: 1; overflow-y: auto;
18
- }
38
+ /* 본문 스크롤 감싸는 영역 */
39
+ .body-wrapper {
40
+ display: flex;
41
+ flex-direction: column;
42
+ align-items: flex-start;
43
+ margin-top: 32px;
44
+ height: 100%;
45
+ overflow: auto;
46
+ }
19
47
 
20
- /* --- [2] 하위 요소 스타일 (Light DOM 제어) --- */
48
+ /* --------------------------------------------------------------------------
49
+ [1] 헤더 영역 (nine-side-menu-head)
50
+ -------------------------------------------------------------------------- */
51
+ nine-side-menu-head {
52
+ display: flex;
53
+ align-items: center;
54
+ padding: 10px;
55
+ height: 40px;
56
+ justify-content: center;
21
57
 
22
- /* 슬롯에 들어온 자식들의 공통 스타일 */
23
- ::slotted(nine-side-menu-head), ::slotted(nine-side-menu-foot) {
24
- display: flex; align-items: center; justify-content: center;
25
- padding: 10px; height: 50px; box-sizing: border-box; width: 100%;
26
- }
58
+ .logo-box {
59
+ color: #f0f0f0;
60
+ white-space: nowrap;
61
+ overflow: hidden;
62
+ text-overflow: ellipsis;
63
+ text-align: center;
64
+ width: 100%;
65
+ }
27
66
 
28
- ::slotted(nine-side-menu-item) {
29
- display: flex; position: relative; padding: 0; margin: 0;
30
- cursor: pointer; color: #999; font-weight: bold;
31
- max-height: 64px; opacity: 1; box-sizing: border-box; width: 100%;
32
- transition: max-height 0.3s, opacity 0.3s, height 0.3s;
33
- }
67
+ .icon-box {
68
+ display: flex;
69
+ align-items: center;
70
+ overflow: hidden;
34
71
 
35
- /* --- [3] 하위 요소 내부 UI 제어 (중요: 자식 내부는 슬롯 선택자로 제어) --- */
72
+ svg {
73
+ display: none;
74
+ opacity: 0;
75
+ transition: opacity 0.3s ease-in-out;
76
+ }
77
+ }
36
78
 
79
+ .icon {
80
+ fill: var(--icon-color, #eee);
81
+ cursor: pointer;
82
+ transition: opacity 0.3s ease-in-out;
83
+ }
84
+ }
37
85
 
38
- /* Collapse 상태 UI 숨김 처리 */
39
- :host(.collapse:not(.hover)) ::slotted(nine-side-menu-head) .logo-box { width: 0; opacity: 0; }
40
- :host(.collapse:not(.hover)) ::slotted(nine-side-menu-item) .menubar { display: none; }
41
- :host(.collapse:not(.hover)) ::slotted(nine-side-menu-item) .expand-icon { display: none; }
86
+ /* 헤더 상태별 유기적 스위칭 명세 */
87
+ &.collapse nine-side-menu-head .logo-box {
88
+ width: 0;
89
+ justify-content: center;
90
+ transition: width 0.5s ease-out;
91
+ }
92
+ &.collapse.hover nine-side-menu-head .logo-box,
93
+ &:not(.collapse) nine-side-menu-head .logo-box {
94
+ width: 100%;
95
+ justify-content: space-between;
96
+ }
42
97
 
43
- /* Item 상세 스타일 */
44
- ::slotted(nine-side-menu-item) li { display: flex; align-items: center; width: 100%; list-style: none; padding: 0 12px; }
45
- ::slotted(nine-side-menu-item).active { color: #9FF2FF; background: rgba(255,255,255,0.05); }
46
- ::slotted(nine-side-menu-item).group { height: 48px; }
47
- ::slotted(nine-side-menu-item).group .menubar { font-weight: 700; color: #fff; }
48
- ::slotted(nine-side-menu-item):not(.group) { height: 32px; }
49
- ::slotted(nine-side-menu-item):not(.group) li::before { content: '•'; margin-right: 8px; margin-left: 10px; }
98
+ &.collapse nine-side-menu-head .icon:nth-of-type(1) {
99
+ display: block;
100
+ opacity: 1;
101
+ }
102
+ &:not(.collapse) {
103
+ nine-side-menu-head .icon:nth-of-type(1) {
104
+ display: none;
105
+ opacity: 0;
106
+ }
107
+ nine-side-menu-head .icon:nth-of-type(2) {
108
+ display: block;
109
+ opacity: 1;
110
+ }
111
+ nine-side-menu-head .icon:nth-of-type(3) {
112
+ display: none;
113
+ opacity: 0;
114
+ }
115
+ nine-side-menu-head .icon-box:hover {
116
+ .icon:nth-of-type(2) {
117
+ display: none;
118
+ opacity: 0;
119
+ }
120
+ .icon:nth-of-type(3) {
121
+ display: block;
122
+ opacity: 1;
123
+ }
124
+ }
125
+ }
50
126
 
51
- ::slotted(nine-side-menu-item) .icon {
52
- width: 18px; height: 18px; background-repeat: no-repeat; background-size: contain; margin-right: 8px;
53
- }
127
+ /* --------------------------------------------------------------------------
128
+ [2] 바디 컨테이너 영역 (nine-side-menu-body)
129
+ -------------------------------------------------------------------------- */
130
+ nine-side-menu-body {
131
+ display: flex;
132
+ align-items: center;
133
+ padding: 10px;
134
+ height: 40px;
135
+ }
54
136
 
55
- /* 애니메이션 */
56
- @keyframes bounce { 0%, 20%, 50%, 80%, 100% { transform: translateX(0); } 40% { transform: translateX(-5px); } 60% { transform: translateX(5px); } }
137
+ /* --------------------------------------------------------------------------
138
+ [3] 푸터 영역 (nine-side-menu-foot)
139
+ -------------------------------------------------------------------------- */
140
+ nine-side-menu-foot {
141
+ display: flex;
142
+ align-items: center;
143
+ padding: 10px;
144
+ height: 40px;
145
+ justify-content: center;
57
146
 
147
+ .logo-box {
148
+ color: #f0f0f0;
149
+ white-space: nowrap;
150
+ overflow: hidden;
151
+ text-overflow: ellipsis;
152
+ text-align: center;
153
+ width: 100%;
154
+ }
58
155
 
156
+ .icon-box {
157
+ display: flex;
158
+ align-items: center;
159
+ overflow: hidden;
59
160
 
161
+ svg {
162
+ display: none;
163
+ opacity: 0;
164
+ transition: opacity 0.3s ease-in-out;
165
+ }
166
+ }
60
167
 
168
+ .icon {
169
+ fill: var(--icon-color, #eee);
170
+ cursor: pointer;
171
+ transition: opacity 0.3s ease-in-out;
172
+ }
173
+ }
61
174
 
62
- /* ==========================================================================
63
- [1] 기본 컴포넌트 구조 스타일 (:host 대신 태그명 직접 지정)
64
- ========================================================================== */
65
- nine-side-menu-head {
66
- display: flex;
67
- align-items: center; /* 수직 중앙 정렬 */
68
- padding: 10px; /* 여백 추가 */
69
- height: 40px;
70
- justify-content: center;
71
- }
175
+ /* 푸터 상태별 유기적 스위칭 명세 */
176
+ &.collapse nine-side-menu-foot .logo-box {
177
+ width: 0;
178
+ justify-content: center;
179
+ transition: width 0.5s ease-out;
180
+ }
181
+ &.collapse.hover nine-side-menu-foot .logo-box,
182
+ &:not(.collapse) nine-side-menu-foot .logo-box {
183
+ width: 100%;
184
+ justify-content: space-between;
185
+ }
72
186
 
73
- /* ==========================================================================
74
- [2] 로고 박스 (Logo Box) 영역 및 애니메이션
75
- ========================================================================== */
76
- nine-side-menu-head .logo-box {
77
- color: #f0f0f0;
78
- white-space: nowrap;
79
- overflow: hidden;
80
- text-overflow: ellipsis;
81
- text-align: center;
82
- width: 100%;
83
- }
187
+ &.collapse nine-side-menu-foot .icon:nth-of-type(1) {
188
+ display: block;
189
+ opacity: 1;
190
+ }
191
+ &:not(.collapse) {
192
+ nine-side-menu-foot .icon:nth-of-type(1) {
193
+ display: none;
194
+ opacity: 0;
195
+ }
196
+ nine-side-menu-foot .icon:nth-of-type(2) {
197
+ display: block;
198
+ opacity: 1;
199
+ }
200
+ nine-side-menu-foot .icon:nth-of-type(3) {
201
+ display: none;
202
+ opacity: 0;
203
+ }
204
+ nine-side-menu-foot .icon-box:hover {
205
+ .icon:nth-of-type(2) {
206
+ display: none;
207
+ opacity: 0;
208
+ }
209
+ .icon:nth-of-type(3) {
210
+ display: block;
211
+ opacity: 1;
212
+ }
213
+ }
214
+ }
84
215
 
85
- /* 접힌 상태(.collapse)일 때 로고 숨기기 */
86
- nine-side-menu.collapse nine-side-menu-head .logo-box {
87
- width: 0;
88
- transition: width 0.5s ease-out;
89
- }
216
+ /* --------------------------------------------------------------------------
217
+ [4] 메뉴 아이템 목록 영역 (nine-side-menu-item)
218
+ -------------------------------------------------------------------------- */
219
+ nine-side-menu-item {
220
+ padding: 0;
221
+ margin: 0;
222
+ position: relative;
223
+ display: flex;
224
+ align-items: unset;
225
+ background-color: unset;
226
+ cursor: pointer;
227
+ max-height: 64px;
228
+ opacity: 1;
229
+ transition: opacity 0.5s ease-out, height 0.5s ease-out, max-height 0.5s ease-in-out;
90
230
 
91
- /* 펼쳐진 상태이거나, 접혔어도 마우스 오버(hover) 상태이면 로고 보여주기 */
92
- nine-side-menu:not(.collapse) nine-side-menu-head .logo-box,
93
- nine-side-menu.hover nine-side-menu-head .logo-box {
94
- width: 100%;
95
- }
231
+ &.group {
232
+ height: 48px;
96
233
 
97
- /* ==========================================================================
98
- [3] 아이콘 박스 (Icon Box) 및 SVG 아이콘 제어
99
- ========================================================================== */
100
- nine-side-menu-head .icon-box {
101
- display: flex;
102
- align-items: center;
103
- overflow: hidden;
104
- }
234
+ .menubar { font-weight: 700; }
235
+ li { list-style-type: none; }
236
+ }
105
237
 
106
- nine-side-menu-head .icon {
107
- fill: var(--icon-color, #eee); /* 아이콘 색상 기본값 지정 */
108
- cursor: pointer;
109
- transition: opacity 0.3s ease-in-out;
238
+ &:not(.group) {
239
+ height: 32px;
110
240
 
111
- /* 💡 기본적으로 모든 아이콘은 숨겨두고 opacity를 0으로 세팅 */
112
- display: none;
113
- opacity: 0;
114
- }
241
+ .menubar { font-weight: 400; }
115
242
 
116
- /* --------------------------------------------------------------------------
117
- [케이스 A] 메뉴가 접혀있을 때 (.collapse)
118
- --------------------------------================================---------- */
119
- /* 첫 번째 아이콘(점 3개) 노출 */
120
- nine-side-menu.collapse nine-side-menu-head .icon:nth-of-type(1) {
121
- display: block;
122
- opacity: 1;
123
- }
243
+ li {
244
+ list-style-type: unset;
124
245
 
125
- /* --------------------------------------------------------------------------
126
- [케이스 B] 메뉴가 펼쳐져 있을 때 (:not(.collapse))
127
- --------------------------------================================---------- */
128
- /* 기본 상태: 두 번째 아이콘 노출, 첫 번째/세 번째 숨김 */
129
- nine-side-menu:not(.collapse) nine-side-menu-head .icon:nth-of-type(1) {
130
- display: none;
131
- opacity: 0;
132
- }
246
+ &::before {
247
+ content: '•';
248
+ margin-right: 8px;
249
+ margin-left: 16px;
250
+ }
251
+ }
133
252
 
134
- nine-side-menu:not(.collapse) nine-side-menu-head .icon:nth-of-type(2) {
135
- display: block;
136
- opacity: 1;
137
- }
253
+ .icon { display: none; }
254
+ }
138
255
 
139
- nine-side-menu:not(.collapse) nine-side-menu-head .icon:nth-of-type(3) {
140
- display: none;
141
- opacity: 0;
142
- }
256
+ &.active {
257
+ color: #9FF2FF;
258
+ }
259
+
260
+ &:hover {
261
+ filter: brightness(90%);
262
+ }
263
+
264
+ /* 아코디언 하이드 규칙 */
265
+ &.hide {
266
+ max-height: 0;
267
+ height: 0 !important;
268
+ opacity: 0;
269
+ padding: 0;
270
+ overflow: hidden;
271
+ }
272
+
273
+ .menubar {
274
+ display: inline-block;
275
+ white-space: nowrap;
276
+ overflow: hidden;
277
+ text-overflow: ellipsis;
278
+ transition: height 0.5s ease-out, padding 0.5s ease-out, width 0.5s ease-out, padding-left 0.5s ease-out;
279
+ text-align: left;
280
+ margin-left: 8px;
281
+ }
282
+
283
+ li {
284
+ display: flex;
285
+ align-items: center;
286
+ padding: 0;
287
+ margin: 0;
288
+ position: relative;
289
+ color: unset;
290
+ font-weight: bold;
291
+ width: calc(100% + 36px);
292
+ }
143
293
 
144
- /* --------------------------------------------------------------------------
145
- [케이스 C] 펼쳐진 상태에서 아이콘 박스에 마우스 올렸을 때 (:hover)
146
- --------------------------------================================---------- */
147
- /* 두 번째 아이콘 숨기고, 세 번째 아이콘 노출 */
148
- nine-side-menu:not(.collapse) nine-side-menu-head .icon-box:hover .icon:nth-of-type(2) {
149
- display: none;
150
- opacity: 0;
294
+ .icon {
295
+ position: relative;
296
+ left: 0;
297
+ width: 16px;
298
+ height: 16px;
299
+ background-repeat: no-repeat;
300
+ background-position: center center;
301
+ background-size: contain;
302
+ margin-left: 8px;
303
+ transition: width 0.5s ease-out, height 0.5s ease-out;
304
+
305
+ &.icon-home {
306
+ background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="lightgray" viewBox="0 0 16 16"><path d="M6.5 14.5v-3.505c0-.245.25-.495.5-.495h2c.25 0 .5.25.5.5v3.5a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5v-7a.5.5 0 0 0-.146-.354L13 5.793V2.5a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v1.293L8.354 1.146a.5.5 0 0 0-.708 0l-6 6A.5.5 0 0 0 1.5 7.5v7a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5"/></svg>');
307
+ }
308
+
309
+ &.icon-base {
310
+ background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="white" viewBox="0 0 16 16"><path d="M0 .5A.5.5 0 0 1 .5 0h15a.5.5 0 0 1 .5.5v3a.5.5 0 0 1-.5.5H14v2h1.5a.5.5 0 0 1 .5.5v3a.5.5 0 0 1-.5.5H14v2h1.5a.5.5 0 0 1-.5.5H.5a.5.5 0 0 1-.5-.5v-3a.5.5 0 0 1 .5-.5H2v-2H.5a.5.5 0 0 1-.5-.5v-3A.5.5 0 0 1 .5 6H2V4H.5a.5.5 0 0 1-.5-.5zM3 4v2h4.5V4zm5.5 0v2H13V4zM3 10v2h4.5v-2zm5.5 0v2H13v-2zM1 1v2h3.5V1zm4.5 0v2h5V1zm6 0v2H15V1zM1 7v2h3.5V7zm4.5 0v2h5V7zm6 0v2H15V7zM1 13v2h3.5v-2zm4.5 0v2h5v-2zm6 0v2H15v-2z"/></svg>');
311
+ }
312
+ }
313
+
314
+ .expand-icon {
315
+ width: 8px;
316
+ height: 8px;
317
+ position: absolute;
318
+ right: 16px;
319
+ background-repeat: no-repeat;
320
+ background-position: center;
321
+ background-size: auto;
322
+ background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="8" height="8" fill="white" class="bi bi-caret-down-fill" viewBox="0 0 16 16"><path d="M7.247 11.14 2.451 5.658C1.885 5.013 2.345 4 3.204 4h9.592a1 1 0 0 1 .753 1.659l-4.796 5.48a1 1 0 0 1-1.506 0z"/></svg>');
323
+ animation: nineMenuRotateOut 0.3s ease-in-out forwards;
324
+ }
325
+
326
+ &:not(.expand) .expand-icon {
327
+ transform: rotate(180deg);
328
+ animation: nineMenuRotateIn 0.3s ease-in-out forwards;
329
+ }
330
+
331
+ a {
332
+ color: #999;
333
+ text-decoration: none;
334
+
335
+ &:hover {
336
+ color: #ccc;
337
+ text-decoration: underline;
338
+ }
339
+ }
340
+ }
341
+
342
+ /* 아이템 상태별 축소 명세 */
343
+ &.collapse:not(.hover) {
344
+ nine-side-menu-item:not(.group) {
345
+ opacity: 0;
346
+ height: 0;
347
+ }
348
+ nine-side-menu-item .menubar {
349
+ width: 0;
350
+ padding-left: unset;
351
+ display: none;
352
+ }
353
+ nine-side-menu-item li {
354
+ justify-content: center;
355
+ }
356
+ nine-side-menu-item .icon {
357
+ width: 20px;
358
+ height: 20px;
359
+ margin-left: 4px;
360
+ }
361
+ nine-side-menu-item .expand-icon {
362
+ display: none;
363
+ }
364
+ }
151
365
  }
152
366
 
153
- nine-side-menu:not(.collapse) nine-side-menu-head .icon-box:hover .icon:nth-of-type(3) {
154
- display: block;
155
- opacity: 1;
367
+ /* ==========================================================================
368
+ [GLOBAL KEYFRAMES]
369
+ ========================================================================== */
370
+ @keyframes nineMenuRotateIn {
371
+ from { transform: rotate(0deg); }
372
+ to { transform: rotate(180deg); }
373
+ }
374
+ @keyframes nineMenuRotateOut {
375
+ from { transform: rotate(180deg); }
376
+ to { transform: rotate(0deg); }
156
377
  }