open-grid 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.
Files changed (70) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +366 -0
  3. package/dist/OpenGrid-CAodJLLm.cjs +90 -0
  4. package/dist/OpenGrid-CAodJLLm.cjs.map +1 -0
  5. package/dist/OpenGrid-CfZy3e7Q.js +4376 -0
  6. package/dist/OpenGrid-CfZy3e7Q.js.map +1 -0
  7. package/dist/open-grid-base.css +1595 -0
  8. package/dist/open-grid-react.cjs +2 -0
  9. package/dist/open-grid-react.cjs.map +1 -0
  10. package/dist/open-grid-react.js +91 -0
  11. package/dist/open-grid-react.js.map +1 -0
  12. package/dist/open-grid-themes.css +630 -0
  13. package/dist/open-grid-vue.cjs +2 -0
  14. package/dist/open-grid-vue.cjs.map +1 -0
  15. package/dist/open-grid-vue.js +84 -0
  16. package/dist/open-grid-vue.js.map +1 -0
  17. package/dist/open-grid.cjs +7 -0
  18. package/dist/open-grid.cjs.map +1 -0
  19. package/dist/open-grid.js +311 -0
  20. package/dist/open-grid.js.map +1 -0
  21. package/dist/types/bench/perf.d.ts +16 -0
  22. package/dist/types/core/CellEditManager.d.ts +46 -0
  23. package/dist/types/core/CellEventHandler.d.ts +28 -0
  24. package/dist/types/core/CellTypeRegistry.d.ts +5 -0
  25. package/dist/types/core/ColumnLayout.d.ts +41 -0
  26. package/dist/types/core/ContextMenu.d.ts +33 -0
  27. package/dist/types/core/DataLayer.d.ts +65 -0
  28. package/dist/types/core/EventEmitter.d.ts +11 -0
  29. package/dist/types/core/ExportManager.d.ts +28 -0
  30. package/dist/types/core/FilterPanel.d.ts +20 -0
  31. package/dist/types/core/FilterSelect.d.ts +96 -0
  32. package/dist/types/core/FindBarManager.d.ts +26 -0
  33. package/dist/types/core/FooterManager.d.ts +16 -0
  34. package/dist/types/core/FormulaEngine.d.ts +10 -0
  35. package/dist/types/core/GridRenderer.d.ts +42 -0
  36. package/dist/types/core/GroupEngine.d.ts +47 -0
  37. package/dist/types/core/GroupTreeManager.d.ts +43 -0
  38. package/dist/types/core/KeyboardManager.d.ts +26 -0
  39. package/dist/types/core/MaskingEngine.d.ts +25 -0
  40. package/dist/types/core/MathUtils.d.ts +26 -0
  41. package/dist/types/core/MergeEngine.d.ts +35 -0
  42. package/dist/types/core/OGDecimal.d.ts +49 -0
  43. package/dist/types/core/OpenGrid.d.ts +213 -0
  44. package/dist/types/core/OrgChart.d.ts +35 -0
  45. package/dist/types/core/Pagination.d.ts +34 -0
  46. package/dist/types/core/PivotEngine.d.ts +20 -0
  47. package/dist/types/core/RowDragDrop.d.ts +18 -0
  48. package/dist/types/core/RowManager.d.ts +30 -0
  49. package/dist/types/core/SortFilterManager.d.ts +36 -0
  50. package/dist/types/core/TreeEngine.d.ts +34 -0
  51. package/dist/types/core/TriggerManager.d.ts +10 -0
  52. package/dist/types/core/VirtualScroll.d.ts +31 -0
  53. package/dist/types/core/WorksheetManager.d.ts +24 -0
  54. package/dist/types/core/XmlConverter.d.ts +88 -0
  55. package/dist/types/core/editors/CellEditor.d.ts +55 -0
  56. package/dist/types/core/editors/DateEditor.d.ts +15 -0
  57. package/dist/types/core/editors/SelectEditor.d.ts +21 -0
  58. package/dist/types/core/renderers/CellRenderer.d.ts +138 -0
  59. package/dist/types/core/types.d.ts +497 -0
  60. package/dist/types/index.d.ts +9 -0
  61. package/dist/types/react/OpenGrid.d.ts +34 -0
  62. package/dist/types/react/index.d.ts +2 -0
  63. package/dist/types/vue/OpenGrid.vue.d.ts +80 -0
  64. package/dist/types/vue/index.d.ts +2 -0
  65. package/dist/types/vue/types.d.ts +28 -0
  66. package/dist/xlsx.min-Bx-LxWOf.cjs +138 -0
  67. package/dist/xlsx.min-Bx-LxWOf.cjs.map +1 -0
  68. package/dist/xlsx.min-Wavxcamn.js +11907 -0
  69. package/dist/xlsx.min-Wavxcamn.js.map +1 -0
  70. package/package.json +102 -0
@@ -0,0 +1,1595 @@
1
+ /* ============================================================
2
+ OPEN_GRID — Base CSS Variables & Layout
3
+ 레이어: Infrastructure (스타일 시스템)
4
+ 설계 근거: CSS Variables로 테마 전환, 런타임 오버라이드 지원
5
+ ============================================================ */
6
+
7
+ :root {
8
+ /* Primary */
9
+ --og-primary: #1976d2;
10
+ --og-primary-light: #e3f2fd;
11
+ --og-primary-dark: #1565c0;
12
+
13
+ /* 헤더 */
14
+ --og-header-bg: #f5f5f5;
15
+ --og-header-color: #333333;
16
+ --og-header-hover-bg: #ebebeb;
17
+ --og-header-sort-color: #1976d2;
18
+
19
+ /* 행 */
20
+ --og-row-bg: #ffffff;
21
+ --og-row-alt-bg: #fafafa;
22
+ --og-row-hover-bg: #e3f2fd;
23
+ --og-row-color: #212121;
24
+
25
+ /* 선택 */
26
+ --og-row-selected-bg: #bbdefb;
27
+ --og-row-selected-color:#0d47a1;
28
+
29
+ /* 상태 행 */
30
+ --og-row-added-bg: #e8f5e9;
31
+ --og-row-added-color: #1b5e20;
32
+ --og-row-edited-bg: #fff8e1;
33
+ --og-row-edited-color: #bf360c; /* WCAG 1.4.3: #e65100(3.57:1)→#bf360c(5.27:1) ✅ */
34
+ --og-row-removed-bg: #ffebee;
35
+ --og-row-removed-color: #b71c1c;
36
+
37
+ /* 그룹 행 */
38
+ --og-group-bg: #e8eaf6;
39
+ --og-group-color: #283593;
40
+ --og-group-border: #c5cae9;
41
+
42
+ /* 트리 */
43
+ --og-tree-toggle-color: #555555;
44
+ --og-tree-line-color: #d0d0d0;
45
+ --og-tree-folder-color: #f9a825; /* amber — 열린 폴더 */
46
+ --og-tree-folder-closed-color: #8eacc4; /* blue-gray — 닫힌 폴더 */
47
+ --og-tree-file-color: #78909c; /* blue-grey — 파일 아이콘 */
48
+ --og-tree-hover-bg: rgba(25,118,210,0.06);
49
+ --og-tree-indent-guide: #e0e0e0; /* legacy 호환 */
50
+
51
+ /* 셀 병합 */
52
+ --og-merge-bg: #fff9c4;
53
+ --og-merge-border: #f9a825;
54
+
55
+ /* 셀 / 보더 */
56
+ --og-cell-padding: 0 8px;
57
+ --og-border-color: #e0e0e0;
58
+ --og-focus-border: #1976d2;
59
+
60
+ /* 폰트 */
61
+ --og-font-size: 13px;
62
+ --og-font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
63
+
64
+ /* 크기 */
65
+ --og-row-height: 32px;
66
+ --og-header-height: 34px;
67
+ --og-scrollbar-size: 8px;
68
+
69
+ /* 페이징 */
70
+ --og-pagination-bg: #ffffff;
71
+ --og-pagination-btn-bg: #ffffff;
72
+ --og-pagination-btn-color:#333333;
73
+
74
+ /* 입력 */
75
+ --og-input-bg: #ffffff;
76
+ --og-input-color: #212121;
77
+ --og-input-border: #1976d2;
78
+
79
+ /* 필터 */
80
+ --og-filter-bg: #ffffff;
81
+ --og-filter-color: #212121;
82
+
83
+ /* ── 프리미엄 디자인 시스템 ── */
84
+
85
+ /* 그림자 */
86
+ --og-shadow-sm: 0 1px 3px rgba(0,0,0,0.07), 0 1px 2px rgba(0,0,0,0.04);
87
+ --og-shadow-md: 0 4px 14px rgba(0,0,0,0.09), 0 2px 6px rgba(0,0,0,0.05);
88
+ --og-shadow-lg: 0 8px 24px rgba(0,0,0,0.10), 0 4px 10px rgba(0,0,0,0.06);
89
+
90
+ /* 전환 */
91
+ --og-transition-fast: 0.12s ease;
92
+ --og-transition-base: 0.18s ease;
93
+
94
+ /* 컨테이너 반경 */
95
+ --og-container-radius: 8px;
96
+
97
+ /* 헤더 타이포그래피 */
98
+ --og-header-font-weight: 600;
99
+ --og-header-letter-spacing: 0.025em;
100
+
101
+ /* 행 좌측 강조선 */
102
+ --og-row-accent-color: var(--og-primary);
103
+ --og-row-accent-width: 3px;
104
+
105
+ /* 로딩 스피너 트랙 (라이트 테마용) */
106
+ --og-spinner-track: rgba(0,0,0,0.10);
107
+ --og-spinner-color: var(--og-primary);
108
+ }
109
+
110
+ /* ============================================================
111
+ 컨테이너
112
+ ============================================================ */
113
+ .og-container {
114
+ font-family: var(--og-font-family);
115
+ font-size: var(--og-font-size);
116
+ color: var(--og-row-color);
117
+ display: flex;
118
+ flex-direction: column;
119
+ overflow: hidden;
120
+ box-sizing: border-box;
121
+ border: 1px solid var(--og-border-color);
122
+ border-radius: var(--og-container-radius);
123
+ background: var(--og-row-bg);
124
+ box-shadow: var(--og-shadow-sm);
125
+ }
126
+
127
+ /* ============================================================
128
+ 헤더
129
+ ============================================================ */
130
+ .og-header {
131
+ flex-shrink: 0;
132
+ overflow: hidden;
133
+ border-bottom: 1px solid var(--og-border-color);
134
+ scrollbar-width: none;
135
+ }
136
+ .og-header::-webkit-scrollbar { display: none; }
137
+
138
+ .og-header-table {
139
+ table-layout: fixed;
140
+ border-collapse: collapse;
141
+ width: 100%;
142
+ }
143
+
144
+ .og-header-cell {
145
+ padding: var(--og-cell-padding);
146
+ height: var(--og-header-height);
147
+ background: var(--og-header-bg);
148
+ color: var(--og-header-color);
149
+ text-align: center;
150
+ white-space: nowrap;
151
+ overflow: hidden;
152
+ text-overflow: ellipsis;
153
+ border-right: 1px solid var(--og-border-color);
154
+ border-bottom: 1px solid var(--og-border-color);
155
+ user-select: none;
156
+ box-sizing: border-box;
157
+ position: relative;
158
+ font-weight: var(--og-header-font-weight);
159
+ letter-spacing: var(--og-header-letter-spacing);
160
+ transition: background var(--og-transition-fast), color var(--og-transition-fast);
161
+ }
162
+ .og-header-cell:hover { background: var(--og-header-hover-bg); }
163
+ .og-header-cell.og-sortable{ cursor: pointer; }
164
+
165
+ .og-header-cell .og-sort-icon {
166
+ display: inline-block;
167
+ margin-left: 4px;
168
+ font-size: 11px;
169
+ font-weight: bold;
170
+ color: var(--og-header-sort-color);
171
+ }
172
+
173
+ /* 컬럼 리사이즈 핸들 */
174
+ .og-resize-handle {
175
+ position: absolute;
176
+ right: 0; top: 0; bottom: 0;
177
+ width: 5px;
178
+ cursor: col-resize;
179
+ z-index: 1;
180
+ transition: background var(--og-transition-fast);
181
+ }
182
+ .og-resize-handle:hover { background: var(--og-primary); opacity: 0.7; }
183
+
184
+ /* ============================================================
185
+ 바디
186
+ ============================================================ */
187
+ .og-body-wrapper {
188
+ flex: 1;
189
+ overflow: auto;
190
+ position: relative;
191
+ /* Sprint 39: 모바일 터치 스크롤 */
192
+ touch-action: pan-x pan-y;
193
+ -webkit-overflow-scrolling: touch;
194
+ }
195
+ .og-body-wrapper::-webkit-scrollbar { width: 8px; height: 8px; }
196
+ .og-body-wrapper::-webkit-scrollbar-track { background: transparent; }
197
+ .og-body-wrapper::-webkit-scrollbar-thumb { background: rgba(0,0,0,0.18); border-radius: 8px; border: 2px solid transparent; background-clip: content-box; }
198
+ .og-body-wrapper::-webkit-scrollbar-thumb:hover { background: rgba(0,0,0,0.30); border: 2px solid transparent; background-clip: content-box; }
199
+ .og-body-wrapper::-webkit-scrollbar-corner { background: transparent; }
200
+
201
+ .og-body { position: relative; }
202
+
203
+ /* ============================================================
204
+
205
+ ============================================================ */
206
+ .og-row {
207
+ position: absolute;
208
+ left: 0;
209
+ right: 0;
210
+ display: flex;
211
+ align-items: center;
212
+ border-bottom: 1px solid var(--og-border-color);
213
+ background: var(--og-row-bg);
214
+ color: var(--og-row-color);
215
+ box-sizing: border-box;
216
+ cursor: default;
217
+ transition: background var(--og-transition-fast), box-shadow var(--og-transition-fast);
218
+ }
219
+ .og-row:nth-child(even) { background: var(--og-row-alt-bg); }
220
+ .og-row:hover { background: var(--og-row-hover-bg) !important; box-shadow: inset var(--og-row-accent-width) 0 0 var(--og-row-accent-color); }
221
+
222
+ /* 선택된 행 — 배경 + 텍스트 모두 강제 적용 */
223
+ .og-row.og-selected {
224
+ background: var(--og-row-selected-bg) !important;
225
+ color: var(--og-row-selected-color) !important;
226
+ }
227
+
228
+ /* 상태 행 */
229
+ .og-row.og-state-added {
230
+ background: var(--og-row-added-bg) !important;
231
+ color: var(--og-row-added-color) !important;
232
+ }
233
+ .og-row.og-state-edited {
234
+ background: var(--og-row-edited-bg) !important;
235
+ color: var(--og-row-edited-color) !important;
236
+ }
237
+ .og-row.og-state-removed {
238
+ background: var(--og-row-removed-bg) !important;
239
+ color: var(--og-row-removed-color) !important;
240
+ text-decoration: line-through;
241
+ opacity: 0.75;
242
+ }
243
+
244
+ /* ============================================================
245
+
246
+ ============================================================ */
247
+ .og-cell {
248
+ padding: var(--og-cell-padding);
249
+ height: 100%;
250
+ display: flex;
251
+ align-items: center;
252
+ justify-content: flex-start;
253
+ border-right: 1px solid var(--og-border-color);
254
+ overflow: hidden;
255
+ white-space: nowrap;
256
+ text-overflow: ellipsis;
257
+ box-sizing: border-box;
258
+ flex-shrink: 0;
259
+ color: inherit;
260
+ }
261
+ .og-cell:last-child { border-right: none; }
262
+ .og-cell--center { justify-content: center; }
263
+ .og-cell--right { justify-content: flex-end; }
264
+ .og-cell--editable { cursor: text; }
265
+
266
+ /* WCAG 2.2 2.4.13: 포커스 셀 outline 최소 3px, 대비 3:1 이상 */
267
+ .og-cell.og-cell-focused {
268
+ outline: 3px solid var(--og-focus-border);
269
+ outline-offset: -2px;
270
+ position: relative;
271
+ z-index: 3; /* frozen 셀 z-index(2)보다 높여 가림 방지 */
272
+ }
273
+ .og-cell.og-editing { padding: 0; }
274
+
275
+ /* 인라인 에디터 입력 */
276
+ .og-cell input.og-cell-input,
277
+ .og-cell select.og-cell-select,
278
+ .og-cell textarea.og-cell-textarea {
279
+ width: 100%;
280
+ height: 100%;
281
+ border: 2px solid var(--og-input-border);
282
+ outline: none;
283
+ padding: 0 6px;
284
+ font-size: var(--og-font-size);
285
+ font-family: var(--og-font-family);
286
+ background: var(--og-input-bg);
287
+ color: var(--og-input-color);
288
+ box-sizing: border-box;
289
+ border-radius: 2px;
290
+ }
291
+
292
+ /* 숫자·체크박스 정렬 */
293
+ .og-cell.og-type-number { justify-content: flex-end; }
294
+ .og-cell.og-type-checkbox { justify-content: center; }
295
+
296
+ /* 엑스트라 컬럼 — og-cell과 함께 사용: 공통 레이아웃(height/display/border/box-sizing/flex-shrink)은 og-cell에서 상속 */
297
+ .og-col-state {
298
+ width: 24px;
299
+ min-width: 24px;
300
+ justify-content: center;
301
+ padding: 0;
302
+ font-size: 12px;
303
+ }
304
+ .og-col-rownum {
305
+ width: 44px;
306
+ min-width: 44px;
307
+ justify-content: center;
308
+ padding: 0;
309
+ font-size: 11px;
310
+ color: #999;
311
+ }
312
+ .og-col-check {
313
+ width: 36px;
314
+ min-width: 36px;
315
+ justify-content: center;
316
+ padding: 0;
317
+ }
318
+ .og-col-check input[type="checkbox"] { cursor: pointer; width: 16px; height: 16px; }
319
+
320
+ /* ============================================================
321
+ 고정 컬럼 (Frozen)
322
+ ============================================================ */
323
+ .og-frozen {
324
+ position: sticky !important;
325
+ z-index: 3 !important;
326
+ background: var(--og-header-bg) !important;
327
+ }
328
+ .og-frozen-cell {
329
+ position: sticky !important;
330
+ z-index: 1 !important;
331
+ background: inherit;
332
+ }
333
+ .og-frozen-last {
334
+ box-shadow: 3px 0 6px rgba(0,0,0,0.14) !important;
335
+ }
336
+
337
+ /* ============================================================
338
+ 그룹 행
339
+ ============================================================ */
340
+ .og-group-row {
341
+ position: absolute;
342
+ left: 0;
343
+ right: 0;
344
+ display: flex;
345
+ align-items: center;
346
+ background: var(--og-group-bg) !important;
347
+ color: var(--og-group-color) !important;
348
+ font-weight: 600;
349
+ border-bottom: 2px solid var(--og-group-border);
350
+ padding: 0 8px;
351
+ box-sizing: border-box;
352
+ cursor: pointer;
353
+ user-select: none;
354
+ }
355
+ .og-group-row:hover { filter: brightness(0.96); }
356
+ .og-group-arrow { margin-right: 4px; font-size: 11px; color: var(--og-primary, #1976d2); }
357
+ .og-group-label { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
358
+ .og-group-sum { text-align: right; padding: 0 8px; font-size: 11px; color: #555; flex-shrink: 0; }
359
+
360
+ /* ============================================================
361
+ 트리 — 탐색기(Explorer) / Git Tree 스타일
362
+ ─ 구조: [가이드│…][연결선├─/└─][토글chevron][폴더/파일아이콘][내용]
363
+ ============================================================ */
364
+
365
+ /* 트리 첫 번째 컬럼 래퍼 */
366
+ .og-tree-cell {
367
+ display: flex;
368
+ align-items: center;
369
+ height: 100%;
370
+ min-width: 0;
371
+ padding-right: 4px;
372
+ overflow: hidden;
373
+ }
374
+
375
+ /* ─── 가이드 라인 (│ 세로선, 공백) ────────────────────────── */
376
+ .og-tree-guide {
377
+ display: inline-flex;
378
+ align-items: stretch;
379
+ width: 16px;
380
+ min-width: 16px;
381
+ height: 100%;
382
+ flex-shrink: 0;
383
+ position: relative;
384
+ }
385
+ /* │ 표시: 형제가 아직 남아있는 조상 레벨 */
386
+ .og-tree-guide--line::before {
387
+ content: '';
388
+ position: absolute;
389
+ left: 7px;
390
+ top: 0;
391
+ bottom: 0;
392
+ width: 1px;
393
+ background: var(--og-tree-line-color, #d0d0d0);
394
+ }
395
+
396
+ /* ─── 연결선 (├─ 또는 └─) ─────────────────────────────────── */
397
+ .og-tree-connector {
398
+ display: inline-flex;
399
+ align-items: center;
400
+ width: 16px;
401
+ min-width: 16px;
402
+ height: 100%;
403
+ flex-shrink: 0;
404
+ position: relative;
405
+ }
406
+
407
+ /* ├─ : 세로선 전체 + 수평선 */
408
+ .og-tree-connector--mid::before {
409
+ content: '';
410
+ position: absolute;
411
+ left: 7px;
412
+ top: 0;
413
+ bottom: 0;
414
+ width: 1px;
415
+ background: var(--og-tree-line-color, #d0d0d0);
416
+ }
417
+ .og-tree-connector--mid::after {
418
+ content: '';
419
+ position: absolute;
420
+ left: 7px;
421
+ top: 50%;
422
+ width: 9px;
423
+ height: 1px;
424
+ background: var(--og-tree-line-color, #d0d0d0);
425
+ }
426
+
427
+ /* └─ : 위→중간 세로선 + 수평선 */
428
+ .og-tree-connector--last::before {
429
+ content: '';
430
+ position: absolute;
431
+ left: 7px;
432
+ top: 0;
433
+ bottom: 50%;
434
+ width: 1px;
435
+ background: var(--og-tree-line-color, #d0d0d0);
436
+ }
437
+ .og-tree-connector--last::after {
438
+ content: '';
439
+ position: absolute;
440
+ left: 7px;
441
+ top: 50%;
442
+ width: 9px;
443
+ height: 1px;
444
+ background: var(--og-tree-line-color, #d0d0d0);
445
+ }
446
+
447
+ /* ─── 토글 버튼 래퍼 ───────────────────────────────────────── */
448
+ .og-tree-toggle-wrap {
449
+ display: inline-flex;
450
+ align-items: center;
451
+ justify-content: center;
452
+ width: 18px;
453
+ min-width: 18px;
454
+ height: 18px;
455
+ flex-shrink: 0;
456
+ }
457
+
458
+ /* chevron 버튼 */
459
+ .og-tree-toggle-btn {
460
+ background: none;
461
+ border: none;
462
+ cursor: pointer;
463
+ padding: 1px;
464
+ color: var(--og-tree-toggle-color, #555);
465
+ display: flex;
466
+ align-items: center;
467
+ justify-content: center;
468
+ width: 18px;
469
+ height: 18px;
470
+ border-radius: 3px;
471
+ font-size: 11px;
472
+ line-height: 1;
473
+ transition: background 0.12s, color 0.12s;
474
+ flex-shrink: 0;
475
+ }
476
+ .og-tree-toggle-btn:hover {
477
+ background: var(--og-tree-hover-bg, rgba(25,118,210,0.08));
478
+ color: var(--og-primary, #1976d2);
479
+ }
480
+ .og-tree-toggle-btn:focus-visible {
481
+ outline: 2px solid var(--og-focus-border, #1976d2);
482
+ outline-offset: 1px;
483
+ }
484
+
485
+ /* 리프 노드 닷 */
486
+ .og-tree-leaf-dot {
487
+ display: inline-block;
488
+ width: 18px;
489
+ height: 18px;
490
+ flex-shrink: 0;
491
+ position: relative;
492
+ }
493
+ .og-tree-leaf-dot::after {
494
+ content: '';
495
+ position: absolute;
496
+ left: 50%;
497
+ top: 50%;
498
+ transform: translate(-50%, -50%);
499
+ width: 5px;
500
+ height: 5px;
501
+ border-radius: 50%;
502
+ background: var(--og-tree-line-color, #d0d0d0);
503
+ }
504
+
505
+ /* ─── 노드 아이콘 (폴더 / 파일) ───────────────────────────── */
506
+ .og-tree-node-icon {
507
+ font-size: 14px;
508
+ margin: 0 5px 0 3px;
509
+ flex-shrink: 0;
510
+ line-height: 1;
511
+ transition: color 0.12s;
512
+ }
513
+ .og-tree-node-icon--branch {
514
+ color: var(--og-tree-folder-closed-color, #8eacc4); /* 닫힘: blue-gray */
515
+ }
516
+ .og-tree-node-icon--branch.og-tree-node-icon--open {
517
+ color: var(--og-tree-folder-color, #f9a825); /* 열림: amber */
518
+ filter: brightness(1.05);
519
+ }
520
+ .og-tree-node-icon--leaf {
521
+ color: var(--og-tree-file-color, #78909c);
522
+ }
523
+
524
+ /* 폴더 아이콘이 토글 버튼 역할 — 클릭/키보드 지원 */
525
+ .og-tree-node-icon--toggle {
526
+ cursor: pointer;
527
+ font-size: 15px;
528
+ transition: color 0.15s, filter 0.15s;
529
+ }
530
+ /* 닫힘 hover: blue-gray → primary blue */
531
+ .og-tree-node-icon--branch.og-tree-node-icon--toggle:not(.og-tree-node-icon--open):hover {
532
+ color: var(--og-primary, #1976d2) !important;
533
+ filter: none !important;
534
+ }
535
+ /* 열림 hover: amber → 좀 더 밝게 */
536
+ .og-tree-node-icon--branch.og-tree-node-icon--toggle.og-tree-node-icon--open:hover {
537
+ filter: brightness(1.2) !important;
538
+ }
539
+ .og-tree-node-icon--toggle:focus-visible {
540
+ outline: 2px solid var(--og-focus-border, #1976d2);
541
+ border-radius: 2px;
542
+ outline-offset: 2px;
543
+ }
544
+
545
+ /* 행 호버 시 폴더/파일 아이콘 약간 밝게 (토글 아이콘은 자체 hover로 처리) */
546
+ .og-row:hover .og-tree-node-icon--branch:not(.og-tree-node-icon--toggle) { filter: brightness(1.15); }
547
+ .og-row:hover .og-tree-node-icon--leaf { color: var(--og-primary, #1976d2); }
548
+
549
+ /* ─── 하위 호환: 기존 og-tree-toggle, og-tree-indent 유지 ─── */
550
+ .og-tree-toggle { display: none; } /* 더 이상 사용 안 함 */
551
+ .og-tree-indent { display: inline-block; flex-shrink: 0; }
552
+
553
+ /* ============================================================
554
+ 드래그 핸들
555
+ ============================================================ */
556
+ .og-drag-handle {
557
+ cursor: grab;
558
+ color: #bbb;
559
+ flex-shrink: 0;
560
+ display: flex;
561
+ align-items: center;
562
+ justify-content: center;
563
+ }
564
+ .og-drag-handle:hover { color: var(--og-primary); }
565
+ .og-drag-handle:active { cursor: grabbing; }
566
+
567
+ /* ============================================================
568
+ 필터 레이어
569
+ ============================================================ */
570
+ .og-filter-layer {
571
+ position: absolute;
572
+ z-index: 100;
573
+ background: var(--og-filter-bg);
574
+ color: var(--og-filter-color);
575
+ border: 1px solid var(--og-border-color);
576
+ border-radius: 4px;
577
+ box-shadow: var(--og-shadow-md);
578
+ padding: 10px;
579
+ min-width: 200px;
580
+ }
581
+ .og-filter-layer input,
582
+ .og-filter-layer select {
583
+ background: var(--og-input-bg);
584
+ color: var(--og-input-color);
585
+ border: 1px solid var(--og-border-color);
586
+ border-radius: 3px;
587
+ padding: 3px 6px;
588
+ font-size: 12px;
589
+ }
590
+
591
+ /* ============================================================
592
+ 빈 데이터 / 로딩
593
+ ============================================================ */
594
+ .og-empty-message {
595
+ display: flex;
596
+ flex-direction: column;
597
+ align-items: center;
598
+ justify-content: center;
599
+ height: 120px;
600
+ gap: 10px;
601
+ color: #b0bec5;
602
+ font-size: 13px;
603
+ letter-spacing: 0.01em;
604
+ user-select: none;
605
+ }
606
+ .og-empty-message::before {
607
+ content: '⊘';
608
+ font-size: 26px;
609
+ opacity: 0.45;
610
+ line-height: 1;
611
+ display: block;
612
+ }
613
+ .og-loading-mask {
614
+ position: absolute;
615
+ inset: 0;
616
+ background: rgba(255,255,255,0.76);
617
+ display: flex;
618
+ align-items: center;
619
+ justify-content: center;
620
+ z-index: 50;
621
+ }
622
+ .og-loading-mask::after {
623
+ content: '';
624
+ width: 28px;
625
+ height: 28px;
626
+ border: 3px solid var(--og-spinner-track);
627
+ border-top-color: var(--og-spinner-color);
628
+ border-radius: 50%;
629
+ animation: og-spin 0.7s linear infinite;
630
+ flex-shrink: 0;
631
+ }
632
+ @keyframes og-spin {
633
+ to { transform: rotate(360deg); }
634
+ }
635
+
636
+ /* ============================================================
637
+ 푸터 / 페이징
638
+ ============================================================ */
639
+ .og-footer {
640
+ flex-shrink: 0;
641
+ border-top: 1px solid var(--og-border-color);
642
+ background: var(--og-header-bg);
643
+ color: var(--og-header-color);
644
+ }
645
+ .og-pagination {
646
+ display: flex;
647
+ align-items: center;
648
+ justify-content: center;
649
+ gap: 3px;
650
+ padding: 8px 12px;
651
+ background: var(--og-pagination-bg);
652
+ border-top: 1px solid var(--og-border-color);
653
+ }
654
+ .og-pagination button {
655
+ min-width: 30px;
656
+ height: 28px;
657
+ padding: 0 9px;
658
+ border: 1px solid transparent;
659
+ border-radius: 6px;
660
+ background: transparent;
661
+ color: var(--og-pagination-btn-color);
662
+ cursor: pointer;
663
+ font-size: 12px;
664
+ font-weight: 500;
665
+ font-family: var(--og-font-family);
666
+ transition: all var(--og-transition-fast);
667
+ display: inline-flex;
668
+ align-items: center;
669
+ justify-content: center;
670
+ line-height: 1;
671
+ }
672
+ .og-pagination button:hover { background: var(--og-primary-light); border-color: var(--og-border-color); color: var(--og-primary); }
673
+ .og-pagination button.og-active{ background: var(--og-primary); color: #fff; border-color: var(--og-primary); font-weight: 600; box-shadow: 0 2px 6px rgba(0,0,0,0.16); }
674
+ .og-pagination button:disabled { opacity: 0.35; cursor: not-allowed; }
675
+
676
+ /* ============================================================
677
+ 위젯 렌더러 (ProgressRenderer / SparklineRenderer / SwitchRenderer / RatingRenderer)
678
+ ============================================================ */
679
+
680
+ /* 프로그레스바 */
681
+ .og-progress-track {
682
+ flex: 1;
683
+ height: 10px;
684
+ background: #e0e0e0;
685
+ border-radius: 5px;
686
+ overflow: hidden;
687
+ }
688
+ .og-progress-fill {
689
+ height: 100%;
690
+ border-radius: 5px;
691
+ transition: width 0.3s ease;
692
+ }
693
+
694
+ /* 스위치 */
695
+ .og-switch {
696
+ display: inline-block;
697
+ width: 34px;
698
+ height: 18px;
699
+ border-radius: 9px;
700
+ background: #bdbdbd;
701
+ position: relative;
702
+ transition: background 0.2s;
703
+ cursor: default;
704
+ flex-shrink: 0;
705
+ }
706
+ .og-switch--on { background: var(--og-primary, #1976d2); }
707
+
708
+ /* 피벗 테이블 컨트롤 */
709
+ .og-pivot-controls {
710
+ display: flex;
711
+ gap: 12px;
712
+ flex-wrap: wrap;
713
+ align-items: flex-end;
714
+ padding: 12px;
715
+ background: #f9f9f9;
716
+ border: 1px solid #e0e0e0;
717
+ border-radius: 6px;
718
+ margin-bottom: 10px;
719
+ }
720
+ .og-pivot-field-group {
721
+ display: flex;
722
+ flex-direction: column;
723
+ gap: 4px;
724
+ min-width: 120px;
725
+ }
726
+ .og-pivot-field-group label {
727
+ font-size: 11px;
728
+ font-weight: 600;
729
+ color: #555;
730
+ text-transform: uppercase;
731
+ letter-spacing: 0.5px;
732
+ }
733
+ .og-pivot-field-group select {
734
+ padding: 4px 8px;
735
+ border: 1px solid #ccc;
736
+ border-radius: 4px;
737
+ font-size: 13px;
738
+ background: #fff;
739
+ }
740
+
741
+ /* ============================================================
742
+ 접근성 (WCAG 2.2 / 장차법)
743
+ ============================================================ */
744
+
745
+ /* aria-live 알림 영역: 시각적으로 숨기고 스크린리더에만 노출 */
746
+ .og-live-region {
747
+ position: absolute;
748
+ width: 1px;
749
+ height: 1px;
750
+ margin: -1px;
751
+ padding: 0;
752
+ overflow: hidden;
753
+ clip: rect(0,0,0,0);
754
+ white-space: nowrap;
755
+ border: 0;
756
+ }
757
+
758
+ /* 컨테이너 포커스 (WCAG 2.4.7 Focus Visible) */
759
+ .og-container:focus-visible {
760
+ outline: 3px solid var(--og-focus-border);
761
+ outline-offset: 1px;
762
+ }
763
+
764
+ /* 헤더 셀 키보드 포커스 (WCAG 2.1.1 Keyboard) */
765
+ .og-header-cell:focus-visible {
766
+ outline: 3px solid var(--og-focus-border);
767
+ outline-offset: -2px;
768
+ z-index: 5;
769
+ position: relative;
770
+ }
771
+
772
+ /* 정렬 방향 CSS content (스크린리더는 aria-sort로 읽음) */
773
+ [role="columnheader"][aria-sort="ascending"]::after { content: ' ▲'; font-size: 9px; color: var(--og-primary); vertical-align: middle; }
774
+ [role="columnheader"][aria-sort="descending"]::after { content: ' ▼'; font-size: 9px; color: var(--og-primary); vertical-align: middle; }
775
+
776
+ /* WCAG 2.5.8: 터치 타겟 최소 24×24px */
777
+ .og-filter-icon {
778
+ min-width: 24px;
779
+ min-height: 24px;
780
+ display: inline-flex;
781
+ align-items: center;
782
+ justify-content: center;
783
+ }
784
+ .og-drag-handle {
785
+ min-width: 24px;
786
+ min-height: 24px;
787
+ }
788
+
789
+ /* 체크박스: 명시적 크기 + 포커스 표시 */
790
+ .og-col-check input[type="checkbox"]:focus-visible,
791
+ .og-header-cell input[type="checkbox"]:focus-visible {
792
+ outline: 3px solid var(--og-focus-border);
793
+ outline-offset: 2px;
794
+ }
795
+
796
+ /* ============================================================
797
+ Sprint 38: 캐스케이딩 필터 셀렉트 (.og-filter-select)
798
+ ============================================================ */
799
+ .og-filter-select {
800
+ display: flex;
801
+ align-items: center;
802
+ gap: 8px;
803
+ padding: 6px 10px;
804
+ border: none;
805
+ border-bottom: 1px solid var(--og-border-color, #e0e0e0);
806
+ background: var(--og-header-bg, #f5f5f5);
807
+ font-family: var(--og-font-family, -apple-system, sans-serif);
808
+ font-size: var(--og-font-size, 13px);
809
+ margin: 0;
810
+ flex-shrink: 0;
811
+ flex-wrap: wrap;
812
+ }
813
+ .og-filter-select-legend {
814
+ font-size: 11px;
815
+ font-weight: 600;
816
+ color: var(--og-header-color, #333);
817
+ text-transform: uppercase;
818
+ letter-spacing: 0.5px;
819
+ padding: 0 4px;
820
+ float: left;
821
+ width: 100%;
822
+ margin-bottom: 4px;
823
+ }
824
+ .og-filter-select-row {
825
+ display: flex;
826
+ align-items: center;
827
+ gap: 8px;
828
+ flex-wrap: wrap;
829
+ flex: 1;
830
+ }
831
+ .og-filter-select-group {
832
+ display: flex;
833
+ align-items: center;
834
+ gap: 4px;
835
+ }
836
+ .og-filter-select-label {
837
+ font-size: 12px;
838
+ color: var(--og-header-color, #555);
839
+ white-space: nowrap;
840
+ }
841
+ .og-filter-select-sel {
842
+ padding: 3px 6px;
843
+ border: 1px solid var(--og-border-color, #e0e0e0);
844
+ border-radius: 3px;
845
+ font-size: 12px;
846
+ font-family: inherit;
847
+ background: var(--og-input-bg, #fff);
848
+ color: var(--og-input-color, #212121);
849
+ cursor: pointer;
850
+ min-width: 80px;
851
+ max-width: 160px;
852
+ }
853
+ .og-filter-select-sel:disabled {
854
+ opacity: 0.5;
855
+ cursor: not-allowed;
856
+ }
857
+ .og-filter-select-sel:focus-visible {
858
+ outline: 2px solid var(--og-focus-border, #1976d2);
859
+ outline-offset: 1px;
860
+ }
861
+ .og-filter-select-reset {
862
+ padding: 3px 10px;
863
+ border: 1px solid var(--og-border-color, #e0e0e0);
864
+ border-radius: 3px;
865
+ font-size: 12px;
866
+ font-family: inherit;
867
+ background: var(--og-row-bg, #fff);
868
+ color: var(--og-header-color, #555);
869
+ cursor: pointer;
870
+ white-space: nowrap;
871
+ transition: background 0.1s, border-color 0.1s;
872
+ }
873
+ .og-filter-select-reset:hover {
874
+ background: var(--og-primary-light, #e3f2fd);
875
+ border-color: var(--og-primary, #1976d2);
876
+ color: var(--og-primary, #1976d2);
877
+ }
878
+ .og-filter-select-reset:focus-visible {
879
+ outline: 2px solid var(--og-focus-border, #1976d2);
880
+ outline-offset: 1px;
881
+ }
882
+ @media print { .og-filter-select { display: none !important; } }
883
+
884
+ /* ============================================================
885
+ 인쇄
886
+ ============================================================ */
887
+ @media print {
888
+ .og-loading-mask,
889
+ .og-pagination,
890
+ .og-filter-icon,
891
+ .og-resize-handle,
892
+ .og-drag-handle { display: none !important; }
893
+ }
894
+
895
+ /* ============================================================
896
+ F3: 컨텍스트 메뉴 (우클릭 팝업)
897
+ 설계: .og-container 자식으로 추가 → CSS 변수 자동 상속
898
+ --og-cm-* 변수로 테마별 미세 조정 지원
899
+ ============================================================ */
900
+
901
+ /* 컨텍스트 메뉴 전용 변수 — 기본값은 그리드 변수 참조 */
902
+ .og-container {
903
+ --og-cm-bg: var(--og-row-bg);
904
+ --og-cm-color: var(--og-row-color);
905
+ --og-cm-border: var(--og-border-color);
906
+ --og-cm-hover-bg: var(--og-row-hover-bg);
907
+ --og-cm-hover-color: var(--og-primary);
908
+ --og-cm-icon-color: var(--og-primary);
909
+ --og-cm-divider: var(--og-border-color);
910
+ --og-cm-shadow: 0 4px 20px rgba(0, 0, 0, 0.12), 0 1px 4px rgba(0, 0, 0, 0.08);
911
+ --og-cm-radius: 6px;
912
+ --og-cm-min-width: 168px;
913
+ }
914
+
915
+ /* 다크 테마: 그림자 강화 */
916
+ [data-og-theme="dark"] {
917
+ --og-cm-shadow: 0 4px 24px rgba(0, 0, 0, 0.55), 0 1px 6px rgba(0, 0, 0, 0.35);
918
+ }
919
+
920
+ .og-context-menu {
921
+ min-width: var(--og-cm-min-width);
922
+ background: var(--og-cm-bg);
923
+ color: var(--og-cm-color);
924
+ border: 1px solid var(--og-cm-border);
925
+ border-radius: var(--og-cm-radius);
926
+ box-shadow: var(--og-cm-shadow);
927
+ padding: 4px 0;
928
+ font-family: var(--og-font-family);
929
+ font-size: var(--og-font-size);
930
+ user-select: none;
931
+ outline: none;
932
+ animation: og-cm-appear 0.1s ease-out;
933
+ }
934
+
935
+ @keyframes og-cm-appear {
936
+ from { opacity: 0; transform: scale(0.95) translateY(-4px); }
937
+ to { opacity: 1; transform: scale(1) translateY(0); }
938
+ }
939
+
940
+ .og-cm-item {
941
+ display: flex;
942
+ align-items: center;
943
+ gap: 8px;
944
+ width: 100%;
945
+ padding: 7px 16px;
946
+ background: transparent;
947
+ border: none;
948
+ color: var(--og-cm-color);
949
+ font-family: inherit;
950
+ font-size: inherit;
951
+ text-align: left;
952
+ cursor: pointer;
953
+ white-space: nowrap;
954
+ transition: background 0.1s, color 0.1s;
955
+ box-sizing: border-box;
956
+ }
957
+
958
+ .og-cm-item:hover,
959
+ .og-cm-item:focus {
960
+ background: var(--og-cm-hover-bg);
961
+ color: var(--og-cm-hover-color);
962
+ outline: none;
963
+ }
964
+
965
+ .og-cm-item:focus-visible {
966
+ outline: 2px solid var(--og-focus-border);
967
+ outline-offset: -2px;
968
+ }
969
+
970
+ .og-cm-item.og-cm-disabled {
971
+ opacity: 0.4;
972
+ cursor: not-allowed;
973
+ pointer-events: none;
974
+ }
975
+
976
+ .og-cm-icon {
977
+ width: 18px;
978
+ text-align: center;
979
+ font-style: normal;
980
+ color: var(--og-cm-icon-color);
981
+ flex-shrink: 0;
982
+ }
983
+
984
+ .og-cm-label {
985
+ flex: 1;
986
+ }
987
+
988
+ .og-cm-divider {
989
+ height: 1px;
990
+ background: var(--og-cm-divider);
991
+ margin: 4px 0;
992
+ }
993
+
994
+ /* ============================================================
995
+ F3: 찾기 바 (.og-find-bar)
996
+ 그리드 헤더 위에 슬라이드 인 방식으로 표시
997
+ ============================================================ */
998
+ .og-find-bar {
999
+ display: flex;
1000
+ align-items: center;
1001
+ gap: 6px;
1002
+ padding: 5px 10px;
1003
+ background: var(--og-header-bg);
1004
+ border-bottom: 1px solid var(--og-border-color);
1005
+ font-family: var(--og-font-family);
1006
+ font-size: var(--og-font-size);
1007
+ animation: og-find-slide 0.15s ease-out;
1008
+ }
1009
+
1010
+ .og-find-bar[hidden] { display: none; }
1011
+
1012
+ @keyframes og-find-slide {
1013
+ from { opacity: 0; transform: translateY(-6px); }
1014
+ to { opacity: 1; transform: translateY(0); }
1015
+ }
1016
+
1017
+ .og-find-label {
1018
+ color: var(--og-header-color);
1019
+ font-weight: 600;
1020
+ white-space: nowrap;
1021
+ }
1022
+
1023
+ .og-find-input {
1024
+ flex: 1;
1025
+ max-width: 260px;
1026
+ padding: 4px 8px;
1027
+ background: var(--og-input-bg);
1028
+ color: var(--og-input-color);
1029
+ border: 1px solid var(--og-border-color);
1030
+ border-radius: 4px;
1031
+ font-family: inherit;
1032
+ font-size: inherit;
1033
+ outline: none;
1034
+ transition: border-color 0.15s;
1035
+ }
1036
+
1037
+ .og-find-input:focus {
1038
+ border-color: var(--og-focus-border);
1039
+ box-shadow: 0 0 0 2px color-mix(in srgb, var(--og-focus-border) 20%, transparent);
1040
+ }
1041
+
1042
+ .og-find-count {
1043
+ color: var(--og-header-color);
1044
+ font-size: 12px;
1045
+ white-space: nowrap;
1046
+ min-width: 40px;
1047
+ }
1048
+
1049
+ .og-find-close {
1050
+ display: flex;
1051
+ align-items: center;
1052
+ justify-content:center;
1053
+ width: 24px;
1054
+ height: 24px;
1055
+ background: transparent;
1056
+ border: none;
1057
+ border-radius: 4px;
1058
+ color: var(--og-header-color);
1059
+ font-size: 16px;
1060
+ cursor: pointer;
1061
+ transition: background 0.1s;
1062
+ padding: 0;
1063
+ line-height: 1;
1064
+ }
1065
+
1066
+ .og-find-close:hover { background: var(--og-row-hover-bg); }
1067
+
1068
+ /* 인쇄 시 컨텍스트 메뉴·찾기 바 숨김 */
1069
+ @media print {
1070
+ .og-context-menu,
1071
+ .og-find-bar { display: none !important; }
1072
+ }
1073
+
1074
+ /* ============================================================
1075
+ F2: 워크시트 탭바 (.og-sheet-tabs)
1076
+ 설계: .og-container 하단에 고정. 탭 클릭 시 시트 전환.
1077
+ CSS 변수 자동 상속 — 테마별 색상 일치.
1078
+ ============================================================ */
1079
+
1080
+ .og-container {
1081
+ --og-tab-bg: var(--og-header-bg);
1082
+ --og-tab-color: var(--og-header-color);
1083
+ --og-tab-border: var(--og-border-color);
1084
+ --og-tab-active-bg: var(--og-row-bg);
1085
+ --og-tab-active-color: var(--og-primary);
1086
+ --og-tab-hover-bg: var(--og-row-hover-bg);
1087
+ --og-tab-add-color: var(--og-primary);
1088
+ }
1089
+
1090
+ .og-sheet-tabs {
1091
+ display: flex;
1092
+ align-items: stretch;
1093
+ gap: 2px;
1094
+ padding: 0 8px;
1095
+ background: var(--og-tab-bg);
1096
+ border-top: 1px solid var(--og-tab-border);
1097
+ min-height: 30px;
1098
+ overflow-x: auto;
1099
+ overflow-y: hidden;
1100
+ scrollbar-width: thin;
1101
+ flex-shrink: 0;
1102
+ font-family: var(--og-font-family);
1103
+ font-size: calc(var(--og-font-size) - 1px);
1104
+ }
1105
+
1106
+ .og-sheet-tabs::-webkit-scrollbar { height: 4px; }
1107
+ .og-sheet-tabs::-webkit-scrollbar-thumb { background: var(--og-tab-border); border-radius: 2px; }
1108
+ .og-sheet-tabs::-webkit-scrollbar-track { background: transparent; }
1109
+
1110
+ .og-sheet-tab {
1111
+ display: flex;
1112
+ align-items: center;
1113
+ padding: 0 14px;
1114
+ background: transparent;
1115
+ border: none;
1116
+ border-bottom: 2px solid transparent;
1117
+ color: var(--og-tab-color);
1118
+ font-family: inherit;
1119
+ font-size: inherit;
1120
+ cursor: pointer;
1121
+ white-space: nowrap;
1122
+ transition: background 0.12s, border-color 0.12s, color 0.12s;
1123
+ border-radius: 3px 3px 0 0;
1124
+ margin-top: 3px;
1125
+ }
1126
+
1127
+ .og-sheet-tab:hover {
1128
+ background: var(--og-tab-hover-bg);
1129
+ }
1130
+
1131
+ .og-sheet-tab--active {
1132
+ background: var(--og-tab-active-bg);
1133
+ color: var(--og-tab-active-color);
1134
+ border-bottom: 2px solid var(--og-tab-active-color);
1135
+ font-weight: 600;
1136
+ }
1137
+
1138
+ .og-sheet-tab-rename {
1139
+ padding: 2px 6px;
1140
+ margin: auto 0;
1141
+ background: var(--og-input-bg);
1142
+ color: var(--og-input-color);
1143
+ border: 1px solid var(--og-focus-border);
1144
+ border-radius:3px;
1145
+ font-family: inherit;
1146
+ font-size: inherit;
1147
+ outline: none;
1148
+ min-width: 60px;
1149
+ max-width: 120px;
1150
+ }
1151
+
1152
+ .og-sheet-add {
1153
+ display: flex;
1154
+ align-items: center;
1155
+ justify-content: center;
1156
+ width: 26px;
1157
+ margin: auto 0 auto 4px;
1158
+ background: transparent;
1159
+ border: 1px solid transparent;
1160
+ border-radius:4px;
1161
+ color: var(--og-tab-add-color);
1162
+ font-size: 18px;
1163
+ line-height: 1;
1164
+ cursor: pointer;
1165
+ transition: background 0.12s, border-color 0.12s;
1166
+ padding: 0;
1167
+ flex-shrink: 0;
1168
+ }
1169
+
1170
+ .og-sheet-add:hover {
1171
+ background: var(--og-tab-hover-bg);
1172
+ border-color: var(--og-tab-border);
1173
+ }
1174
+
1175
+ /* 인쇄 시 탭바 숨김 */
1176
+ @media print {
1177
+ .og-sheet-tabs { display: none !important; }
1178
+ }
1179
+
1180
+ /* ============================================================
1181
+ OrgChart — 조직도 컴포넌트
1182
+ ============================================================ */
1183
+ .og-orgchart {
1184
+ overflow: auto;
1185
+ position: relative;
1186
+ background: var(--og-bg, #fff);
1187
+ padding: 24px 16px 32px;
1188
+ box-sizing: border-box;
1189
+ }
1190
+
1191
+ .og-orgchart-wrap {
1192
+ position: relative;
1193
+ }
1194
+
1195
+ /* SVG 연결선 */
1196
+ .og-orgchart-line {
1197
+ stroke: var(--og-tree-line-color, #bdbdbd);
1198
+ stroke-width: 1.5;
1199
+ }
1200
+
1201
+ /* 노드 카드 */
1202
+ .og-orgchart-node {
1203
+ position: absolute;
1204
+ border: 1.5px solid var(--og-border-color, #e0e0e0);
1205
+ border-radius: 8px;
1206
+ background: var(--og-row-bg, #fff);
1207
+ box-shadow: 0 1px 4px rgba(0,0,0,0.08);
1208
+ display: flex;
1209
+ flex-direction: column;
1210
+ justify-content: center;
1211
+ overflow: hidden;
1212
+ cursor: pointer;
1213
+ transition: box-shadow 0.15s, border-color 0.15s, background 0.15s;
1214
+ box-sizing: border-box;
1215
+ padding: 8px 12px;
1216
+ user-select: none;
1217
+ }
1218
+ .og-orgchart-node:hover {
1219
+ border-color: var(--og-primary, #1976d2);
1220
+ box-shadow: 0 3px 10px rgba(25,118,210,0.15);
1221
+ }
1222
+ .og-orgchart-node--selected {
1223
+ border-color: var(--og-primary, #1976d2);
1224
+ box-shadow: 0 0 0 3px rgba(25,118,210,0.18);
1225
+ }
1226
+
1227
+ /* 카드 안 컨텐츠 */
1228
+ .og-orgchart-node-content {
1229
+ flex: 1;
1230
+ display: flex;
1231
+ flex-direction: column;
1232
+ justify-content: center;
1233
+ gap: 3px;
1234
+ overflow: hidden;
1235
+ min-height: 0;
1236
+ }
1237
+ .og-orgchart-col {
1238
+ font-size: 13px;
1239
+ line-height: 1.35;
1240
+ overflow: hidden;
1241
+ text-overflow: ellipsis;
1242
+ white-space: nowrap;
1243
+ color: var(--og-row-color, #212121);
1244
+ }
1245
+
1246
+ /* 기본 스타일 헬퍼 — 예제에서 className으로 사용 */
1247
+ .og-orgchart-col-name {
1248
+ font-weight: 600;
1249
+ font-size: 14px;
1250
+ color: var(--og-row-color, #1a1a1a);
1251
+ }
1252
+ .og-orgchart-col-sub {
1253
+ font-size: 12px;
1254
+ color: var(--og-header-color, #757575);
1255
+ opacity: 0.75;
1256
+ }
1257
+
1258
+ /* 토글 버튼 (카드 하단 중앙 ⊕/⊖) */
1259
+ .og-orgchart-toggle {
1260
+ position: absolute;
1261
+ bottom: -11px;
1262
+ left: 50%;
1263
+ transform: translateX(-50%);
1264
+ background: var(--og-row-bg, #fff);
1265
+ border: 1.5px solid var(--og-border-color, #e0e0e0);
1266
+ border-radius: 50%;
1267
+ width: 22px;
1268
+ height: 22px;
1269
+ display: flex;
1270
+ align-items: center;
1271
+ justify-content: center;
1272
+ cursor: pointer;
1273
+ font-size: 13px;
1274
+ color: var(--og-primary, #1976d2);
1275
+ z-index: 2;
1276
+ padding: 0;
1277
+ line-height: 1;
1278
+ transition: background 0.12s, color 0.12s, border-color 0.12s;
1279
+ }
1280
+ .og-orgchart-toggle:hover {
1281
+ background: var(--og-primary, #1976d2);
1282
+ color: #fff;
1283
+ border-color: var(--og-primary, #1976d2);
1284
+ }
1285
+
1286
+
1287
+ /* 컬럼 드래그 리오더 */
1288
+ .og-col-dragging {
1289
+ opacity: 0.5;
1290
+ cursor: grabbing !important;
1291
+ }
1292
+ .og-col-drop-over {
1293
+ background: var(--og-primary-light, #e3f2fd) !important;
1294
+ border-left: 2px solid var(--og-primary, #1976d2) !important;
1295
+ }
1296
+
1297
+ /* 합계 푸터 바 */
1298
+ .og-footer-bar {
1299
+ display: flex;
1300
+ align-items: stretch;
1301
+ border-top: 2px solid var(--og-primary, #1976d2);
1302
+ background: var(--og-header-bg, #f5f5f5);
1303
+ overflow: hidden;
1304
+ flex-shrink: 0;
1305
+ font-size: 13px;
1306
+ font-weight: 600;
1307
+ }
1308
+ .og-footer-bar > div {
1309
+ padding: 4px 8px;
1310
+ box-sizing: border-box;
1311
+ white-space: nowrap;
1312
+ overflow: hidden;
1313
+ text-overflow: ellipsis;
1314
+ border-right: 1px solid var(--og-border-color, #e0e0e0);
1315
+ }
1316
+
1317
+ /* 그룹 행 — 컬럼 정렬 소계 */
1318
+ .og-group-row {
1319
+ position: absolute;
1320
+ left: 0; right: 0;
1321
+ display: flex;
1322
+ align-items: stretch;
1323
+ cursor: pointer;
1324
+ user-select: none;
1325
+ background: var(--og-header-bg, #f5f5f5);
1326
+ border-bottom: 1px solid var(--og-border-color, #e0e0e0);
1327
+ font-size: 13px;
1328
+ transition: background 0.1s;
1329
+ }
1330
+ .og-group-row:hover {
1331
+ background: var(--og-primary-light, #e3f2fd);
1332
+ }
1333
+ .og-group-cell {
1334
+ flex-shrink: 0;
1335
+ padding: 2px 8px;
1336
+ box-sizing: border-box;
1337
+ overflow: hidden;
1338
+ border-right: 1px solid var(--og-border-color, #e0e0e0);
1339
+ display: flex;
1340
+ align-items: center;
1341
+ white-space: nowrap;
1342
+ }
1343
+ .og-group-state-cell {
1344
+ flex-shrink: 0;
1345
+ display: flex;
1346
+ align-items: center;
1347
+ justify-content: center;
1348
+ gap: 2px;
1349
+ font-size: 10px;
1350
+ font-weight: 700;
1351
+ border-right: 1px solid var(--og-border-color, #e0e0e0);
1352
+ }
1353
+
1354
+ /* 셀 병합 placeholder (hidden 셀 — 컬럼 너비 보존용) */
1355
+ .og-cell--merge-ph { visibility: hidden !important; }
1356
+
1357
+ /* ================================================================
1358
+ OPEN_GRID — UI/UX 개선 (UX-1 ~ UX-6) 2026-06-03
1359
+ ================================================================ */
1360
+
1361
+ /* ── UX-1: 셀 타입 CSS 클래스 완성 ──────────────────────────── */
1362
+
1363
+ /* 숫자 셀: 탭뷸러 정렬 (자릿수 정렬, 천단위 가독성 향상) */
1364
+ .og-cell-number {
1365
+ font-variant-numeric: tabular-nums;
1366
+ font-feature-settings: "tnum";
1367
+ }
1368
+
1369
+ /* 텍스트/날짜: 클래스 기반 overflow 처리 */
1370
+ .og-cell-text,
1371
+ .og-cell-date {
1372
+ overflow: hidden;
1373
+ text-overflow: ellipsis;
1374
+ white-space: nowrap;
1375
+ display: block;
1376
+ width: 100%;
1377
+ }
1378
+
1379
+ /* 링크 렌더러: 밑줄 오프셋 + 호버 강조 */
1380
+ .og-cell-link {
1381
+ text-underline-offset: 2px;
1382
+ }
1383
+ .og-cell-link:hover {
1384
+ text-decoration-thickness: 2px;
1385
+ }
1386
+
1387
+ /* 버튼 렌더러 CSS 클래스 (인라인 style 보완 — 테마 override 용) */
1388
+ .og-cell-btn {
1389
+ padding: 2px 10px;
1390
+ border: 1px solid var(--og-primary, #1976d2);
1391
+ border-radius: 4px;
1392
+ background: var(--og-row-bg, #fff);
1393
+ color: var(--og-primary, #1976d2);
1394
+ cursor: pointer;
1395
+ font-size: 12px;
1396
+ font-family: var(--og-font-family);
1397
+ font-weight: 500;
1398
+ white-space: nowrap;
1399
+ transition: background var(--og-transition-fast), box-shadow var(--og-transition-fast);
1400
+ display: inline-flex;
1401
+ align-items: center;
1402
+ justify-content: center;
1403
+ line-height: 1;
1404
+ }
1405
+ .og-cell-btn:hover {
1406
+ background: var(--og-primary-light, #e3f2fd);
1407
+ box-shadow: 0 1px 4px rgba(25,118,210,0.15);
1408
+ }
1409
+ .og-cell-btn:active {
1410
+ filter: brightness(0.95);
1411
+ }
1412
+
1413
+ /* 체크박스/라디오 셀: accent-color CSS 변수 연동 */
1414
+ .og-cell-checkbox input[type="checkbox"],
1415
+ .og-cell-radio input[type="radio"] {
1416
+ accent-color: var(--og-primary, #1976d2);
1417
+ }
1418
+
1419
+ /* ── UX-2: 헤더 인터랙션 개선 ───────────────────────────────── */
1420
+
1421
+ /* 정렬 활성 헤더 컬럼 배경 하이라이트 */
1422
+ .og-header-cell.og-sorted {
1423
+ background: var(--og-primary-light, #e3f2fd) !important;
1424
+ }
1425
+ .og-header-cell.og-sorted:hover {
1426
+ filter: brightness(0.96);
1427
+ }
1428
+
1429
+ /* 필터 아이콘: 기본은 흐리게, 호버/활성 시 primary */
1430
+ .og-filter-icon {
1431
+ transition: opacity var(--og-transition-fast), color var(--og-transition-fast);
1432
+ }
1433
+ .og-filter-icon:hover {
1434
+ opacity: 1 !important;
1435
+ color: var(--og-primary, #1976d2) !important;
1436
+ }
1437
+ .og-filter-icon--active {
1438
+ opacity: 1 !important;
1439
+ color: var(--og-primary, #1976d2) !important;
1440
+ font-weight: bold;
1441
+ }
1442
+
1443
+ /* 리사이즈 핸들: 세로 바 힌트 + 호버 인터랙션 */
1444
+ .og-resize-handle::after {
1445
+ content: '';
1446
+ position: absolute;
1447
+ right: 1px;
1448
+ top: 25%;
1449
+ bottom: 25%;
1450
+ width: 2px;
1451
+ background: var(--og-border-color, #e0e0e0);
1452
+ border-radius: 1px;
1453
+ transition: background var(--og-transition-fast),
1454
+ top var(--og-transition-fast),
1455
+ bottom var(--og-transition-fast),
1456
+ width var(--og-transition-fast);
1457
+ }
1458
+ .og-resize-handle:hover::after {
1459
+ background: var(--og-primary, #1976d2);
1460
+ top: 12%;
1461
+ bottom: 12%;
1462
+ width: 3px;
1463
+ }
1464
+ /* resize 드래그 중 ::after 제거 (혼란 방지) */
1465
+ .og-header-cell:active .og-resize-handle::after { display: none; }
1466
+
1467
+ /* 컬럼 드롭 인디케이터 강화 */
1468
+ .og-col-drop-over {
1469
+ background: var(--og-primary-light, #e3f2fd) !important;
1470
+ border-left: 3px solid var(--og-primary, #1976d2) !important;
1471
+ box-shadow: inset 2px 0 6px rgba(25,118,210,0.12) !important;
1472
+ }
1473
+
1474
+ /* ── UX-3: 편집 셀 시각 개선 ─────────────────────────────────── */
1475
+
1476
+ /* 편집 입력 필드: 포커스 내부 링 + 드롭섀도우 */
1477
+ .og-cell.og-editing input.og-cell-input,
1478
+ .og-cell.og-editing select.og-cell-select,
1479
+ .og-cell.og-editing textarea.og-cell-textarea {
1480
+ box-shadow: inset 0 0 0 1px var(--og-input-border, #1976d2),
1481
+ 0 2px 8px rgba(25,118,210,0.10);
1482
+ }
1483
+
1484
+ /* ── UX-4: 위젯 렌더러 CSS 개선 ─────────────────────────────── */
1485
+
1486
+ /* Progress: 그라디언트 트랙 + fill 애니메이션 */
1487
+ .og-progress-track {
1488
+ background: linear-gradient(135deg, #eeeeee 0%, #e8e8e8 100%);
1489
+ }
1490
+ .og-progress-fill {
1491
+ background: linear-gradient(
1492
+ 90deg,
1493
+ var(--og-primary, #1976d2) 0%,
1494
+ color-mix(in srgb, var(--og-primary, #1976d2) 75%, white) 100%
1495
+ );
1496
+ transition: width 0.45s cubic-bezier(0.4, 0, 0.2, 1);
1497
+ }
1498
+
1499
+ /* Switch: CSS-only thumb 미세 조정 (TS inline 보완) */
1500
+ .og-switch {
1501
+ display: inline-block;
1502
+ width: 34px;
1503
+ height: 18px;
1504
+ border-radius: 9px;
1505
+ position: relative;
1506
+ transition: background 0.2s;
1507
+ cursor: pointer;
1508
+ flex-shrink: 0;
1509
+ }
1510
+
1511
+ /* ── UX-5: 그룹 행 CSS 정리 ─────────────────────────────────── */
1512
+
1513
+ /* og-group-row: 첫번째 정의(line 339)의 !important 충돌 해소
1514
+ — 그룹 변수(--og-group-*)를 명시 적용, transition 추가 */
1515
+ .og-group-row {
1516
+ background: var(--og-group-bg, #e8eaf6) !important;
1517
+ color: var(--og-group-color, #283593) !important;
1518
+ font-weight: 600;
1519
+ font-size: 13px;
1520
+ transition: background var(--og-transition-fast);
1521
+ border-bottom: 1px solid var(--og-group-border, #c5cae9) !important;
1522
+ }
1523
+ .og-group-row:hover {
1524
+ background: color-mix(in srgb, var(--og-group-bg, #e8eaf6) 80%, var(--og-primary, #1976d2)) !important;
1525
+ filter: none !important; /* 첫번째 정의의 filter:brightness 취소 */
1526
+ }
1527
+ .og-group-cell {
1528
+ color: inherit;
1529
+ }
1530
+
1531
+ /* 그룹 화살표 트랜지션 (collapsed 상태용) */
1532
+ .og-group-arrow {
1533
+ display: inline-block;
1534
+ transition: transform var(--og-transition-fast);
1535
+ }
1536
+ .og-group-row--collapsed .og-group-arrow {
1537
+ transform: rotate(-90deg);
1538
+ }
1539
+
1540
+ /* ── UX-6: 기타 컴포넌트 ─────────────────────────────────────── */
1541
+
1542
+ /* 툴팁 CSS */
1543
+ .og-tooltip {
1544
+ position: fixed;
1545
+ z-index: 99999;
1546
+ background: rgba(33, 33, 33, 0.95);
1547
+ color: #fff;
1548
+ font-size: 12px;
1549
+ font-family: var(--og-font-family);
1550
+ line-height: 1.45;
1551
+ padding: 5px 10px;
1552
+ border-radius: 5px;
1553
+ white-space: normal;
1554
+ word-break: break-word;
1555
+ pointer-events: none;
1556
+ box-shadow: 0 3px 10px rgba(0, 0, 0, 0.25);
1557
+ max-width: 300px;
1558
+ animation: og-tooltip-appear 0.12s ease-out;
1559
+ }
1560
+ @keyframes og-tooltip-appear {
1561
+ from { opacity: 0; transform: translateY(3px); }
1562
+ to { opacity: 1; transform: translateY(0); }
1563
+ }
1564
+
1565
+ /* 빈 상태 개선: 아이콘 크게, 텍스트 부드럽게 */
1566
+ .og-empty-message {
1567
+ height: 140px;
1568
+ gap: 12px;
1569
+ color: #90a4ae;
1570
+ font-size: 13.5px;
1571
+ letter-spacing: 0.025em;
1572
+ }
1573
+ .og-empty-message::before {
1574
+ font-size: 34px;
1575
+ opacity: 0.32;
1576
+ }
1577
+
1578
+ /* 고정 컬럼 그림자: 레이어드 섀도우로 자연스러운 경계 */
1579
+ .og-frozen-last {
1580
+ box-shadow: 4px 0 10px -2px rgba(0,0,0,0.12),
1581
+ 1px 0 3px -1px rgba(0,0,0,0.06) !important;
1582
+ }
1583
+
1584
+ /* 합계 푸터 바: 셀 내부 정렬 개선 */
1585
+ .og-footer-bar > div {
1586
+ display: flex;
1587
+ flex-direction: column;
1588
+ justify-content: center;
1589
+ font-size: 12px;
1590
+ }
1591
+
1592
+ .og-vue-wrapper[data-v-0a49d4fc] {
1593
+ display: block;
1594
+ box-sizing: border-box;
1595
+ }