Skip to content

Commit e0d4349

Browse files
authored
Merge pull request #44 from DMU-DebugVisual/dongjun
코드 시각화 모달 추가,코드 시각화 모달 구현
2 parents 16cad61 + 7459bcf commit e0d4349

File tree

11 files changed

+3890
-30
lines changed

11 files changed

+3890
-30
lines changed

package-lock.json

Lines changed: 441 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
"@testing-library/react": "^16.3.0",
1111
"@testing-library/user-event": "^13.5.0",
1212
"axios": "^0.27.2",
13+
"d3": "^7.9.0",
1314
"react": "^19.1.0",
1415
"react-dom": "^19.1.0",
1516
"react-icons": "^5.5.0",
Lines changed: 288 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,288 @@
1+
import React from 'react';
2+
3+
// 🎨 임시 플레이스홀더 컴포넌트 (파일이 없을 경우 대비)
4+
const FallbackPlaceholder = ({ type = 'unknown', data = null, currentStep = 0, totalSteps = 0 }) => (
5+
<div style={{
6+
width: '100%',
7+
height: '100%',
8+
display: 'flex',
9+
flexDirection: 'column',
10+
alignItems: 'center',
11+
justifyContent: 'center',
12+
padding: '40px',
13+
backgroundColor: '#ffffff',
14+
border: '2px dashed #e2e8f0',
15+
borderRadius: '12px',
16+
textAlign: 'center',
17+
minHeight: '400px'
18+
}}>
19+
<div style={{ fontSize: '48px', marginBottom: '20px' }}>🚧</div>
20+
<h3 style={{ margin: '0 0 12px 0', color: '#1e293b' }}>
21+
{type} 애니메이션 개발 중
22+
</h3>
23+
<p style={{ margin: '0 0 20px 0', color: '#64748b' }}>
24+
곧 완성될 예정입니다!
25+
</p>
26+
<div style={{
27+
display: 'grid',
28+
gridTemplateColumns: 'repeat(auto-fit, minmax(120px, 1fr))',
29+
gap: '12px',
30+
width: '100%',
31+
maxWidth: '400px',
32+
marginTop: '20px'
33+
}}>
34+
<div style={{
35+
padding: '12px',
36+
backgroundColor: '#f8fafc',
37+
borderRadius: '8px',
38+
border: '1px solid #e2e8f0'
39+
}}>
40+
<div style={{ fontSize: '11px', color: '#64748b', marginBottom: '4px' }}>현재 단계</div>
41+
<div style={{ fontSize: '14px', color: '#8b5cf6', fontWeight: 'bold' }}>
42+
{currentStep + 1} / {totalSteps}
43+
</div>
44+
</div>
45+
<div style={{
46+
padding: '12px',
47+
backgroundColor: '#f8fafc',
48+
borderRadius: '8px',
49+
border: '1px solid #e2e8f0'
50+
}}>
51+
<div style={{ fontSize: '11px', color: '#64748b', marginBottom: '4px' }}>애니메이션</div>
52+
<div style={{ fontSize: '14px', color: '#8b5cf6', fontWeight: 'bold' }}>{type}</div>
53+
</div>
54+
</div>
55+
</div>
56+
);
57+
58+
// PlaceholderAnimation import 시도 (실패시 FallbackPlaceholder 사용)
59+
let PlaceholderAnimation;
60+
try {
61+
PlaceholderAnimation = require('./animations/PlaceholderAnimation').default;
62+
} catch (error) {
63+
console.warn('PlaceholderAnimation 컴포넌트를 불러올 수 없습니다. Fallback을 사용합니다.', error);
64+
PlaceholderAnimation = FallbackPlaceholder;
65+
}
66+
67+
/**
68+
* 🏭 AnimationFactory 클래스 (임시 개발 버전)
69+
* 실제 애니메이션 컴포넌트가 준비되면 교체될 예정
70+
*/
71+
export class AnimationFactory {
72+
// 📋 애니메이션 타입 → 컴포넌트 매핑 테이블 (모두 플레이스홀더)
73+
static animations = {
74+
// 🔄 재귀 애니메이션들
75+
'fibonacci-recursion': PlaceholderAnimation,
76+
'factorial-recursion': PlaceholderAnimation,
77+
'hanoi-tower': PlaceholderAnimation,
78+
'recursion-tree': PlaceholderAnimation,
79+
80+
// 🔢 정렬 애니메이션들
81+
'bubble-sort': PlaceholderAnimation,
82+
'quick-sort': PlaceholderAnimation,
83+
'merge-sort': PlaceholderAnimation,
84+
'insertion-sort': PlaceholderAnimation,
85+
'selection-sort': PlaceholderAnimation,
86+
'heap-sort': PlaceholderAnimation,
87+
88+
// 📊 자료구조 애니메이션들
89+
'array': PlaceholderAnimation,
90+
'array-manipulation': PlaceholderAnimation,
91+
'linked-list': PlaceholderAnimation,
92+
'stack': PlaceholderAnimation,
93+
'queue': PlaceholderAnimation,
94+
'tree': PlaceholderAnimation,
95+
'binary-tree': PlaceholderAnimation,
96+
'bst': PlaceholderAnimation,
97+
98+
// 🔍 탐색 애니메이션들
99+
'binary-search': PlaceholderAnimation,
100+
'linear-search': PlaceholderAnimation,
101+
'breadth-first-search': PlaceholderAnimation,
102+
'depth-first-search': PlaceholderAnimation,
103+
'bfs': PlaceholderAnimation,
104+
'dfs': PlaceholderAnimation,
105+
106+
// 📝 변수 추적 애니메이션들
107+
'variables': PlaceholderAnimation,
108+
'variable-tracking': PlaceholderAnimation,
109+
'basic-algorithm': PlaceholderAnimation,
110+
111+
// 🎯 기본 애니메이션 (fallback)
112+
'default': PlaceholderAnimation,
113+
'unknown': PlaceholderAnimation
114+
};
115+
116+
// 📚 애니메이션 카테고리별 분류
117+
static categories = {
118+
recursion: [
119+
'fibonacci-recursion', 'factorial-recursion',
120+
'hanoi-tower', 'recursion-tree'
121+
],
122+
sorting: [
123+
'bubble-sort', 'quick-sort', 'merge-sort',
124+
'insertion-sort', 'selection-sort', 'heap-sort'
125+
],
126+
dataStructures: [
127+
'array', 'array-manipulation', 'linked-list',
128+
'stack', 'queue', 'tree', 'binary-tree', 'bst'
129+
],
130+
searching: [
131+
'binary-search', 'linear-search', 'breadth-first-search',
132+
'depth-first-search', 'bfs', 'dfs'
133+
],
134+
variables: [
135+
'variables', 'variable-tracking', 'basic-algorithm'
136+
]
137+
};
138+
139+
/**
140+
* 🎨 애니메이션 컴포넌트 생성
141+
* @param {string} type - 애니메이션 타입
142+
* @param {Object} props - 컴포넌트에 전달할 props
143+
* @returns {React.Component} 생성된 애니메이션 컴포넌트
144+
*/
145+
static createAnimation(type, props = {}) {
146+
console.log(`🏭 Creating animation: ${type} (개발 중 버전)`);
147+
console.log('Props:', props);
148+
149+
// 1. 애니메이션 타입 정규화
150+
const normalizedType = this.normalizeType(type);
151+
console.log('Normalized type:', normalizedType);
152+
153+
// 2. 컴포넌트 찾기 (현재는 모두 플레이스홀더)
154+
const AnimationComponent = this.animations[normalizedType] || PlaceholderAnimation;
155+
console.log('Selected component:', AnimationComponent);
156+
157+
// 3. 컴포넌트 생성 및 반환
158+
const element = React.createElement(AnimationComponent, {
159+
key: `animation-${normalizedType}-${Date.now()}`,
160+
type: normalizedType,
161+
animationType: normalizedType,
162+
...props
163+
});
164+
165+
console.log('Created element:', element);
166+
return element;
167+
}
168+
169+
/**
170+
* 🔧 애니메이션 타입 정규화
171+
* @param {string} type - 원본 타입
172+
* @returns {string} 정규화된 타입
173+
*/
174+
static normalizeType(type) {
175+
if (!type) return 'unknown';
176+
177+
// 소문자로 변환하고 공백/언더스코어를 하이픈으로 변환
178+
return type.toLowerCase()
179+
.replace(/[\s_]+/g, '-')
180+
.replace(/[^a-z0-9-]/g, '');
181+
}
182+
183+
/**
184+
* 📋 사용 가능한 애니메이션 타입 목록
185+
* @returns {Array} 애니메이션 타입 배열
186+
*/
187+
static getAvailableTypes() {
188+
return Object.keys(this.animations).filter(type =>
189+
type !== 'unknown' && type !== 'default'
190+
);
191+
}
192+
193+
/**
194+
* 🏷️ 애니메이션 타입이 유효한지 확인
195+
* @param {string} type - 확인할 타입
196+
* @returns {boolean} 유효성 여부
197+
*/
198+
static isValidType(type) {
199+
const normalizedType = this.normalizeType(type);
200+
return normalizedType in this.animations;
201+
}
202+
203+
/**
204+
* 📂 카테고리별 애니메이션 타입 조회
205+
* @param {string} category - 카테고리명
206+
* @returns {Array} 해당 카테고리의 애니메이션 타입들
207+
*/
208+
static getTypesByCategory(category) {
209+
return this.categories[category] || [];
210+
}
211+
212+
/**
213+
* 🔍 애니메이션 타입의 카테고리 찾기
214+
* @param {string} type - 애니메이션 타입
215+
* @returns {string|null} 카테고리명
216+
*/
217+
static getCategoryByType(type) {
218+
const normalizedType = this.normalizeType(type);
219+
220+
for (const [category, types] of Object.entries(this.categories)) {
221+
if (types.includes(normalizedType)) {
222+
return category;
223+
}
224+
}
225+
return null;
226+
}
227+
228+
/**
229+
* 🎯 새로운 애니메이션 타입 등록 (실제 컴포넌트 완성 후 사용)
230+
* @param {string} type - 애니메이션 타입
231+
* @param {React.Component} component - 컴포넌트
232+
* @param {string} category - 카테고리 (선택사항)
233+
*/
234+
static registerAnimation(type, component, category = null) {
235+
const normalizedType = this.normalizeType(type);
236+
237+
// 애니메이션 등록
238+
this.animations[normalizedType] = component;
239+
240+
// 카테고리에 추가 (지정된 경우)
241+
if (category && this.categories[category]) {
242+
if (!this.categories[category].includes(normalizedType)) {
243+
this.categories[category].push(normalizedType);
244+
}
245+
}
246+
247+
console.log(`✅ Registered animation: ${normalizedType}`);
248+
}
249+
250+
/**
251+
* 📊 팩토리 상태 정보
252+
* @returns {Object} 팩토리 상태
253+
*/
254+
static getFactoryInfo() {
255+
return {
256+
version: 'development',
257+
mode: 'placeholder-only',
258+
totalAnimations: Object.keys(this.animations).length,
259+
categories: Object.keys(this.categories),
260+
availableTypes: this.getAvailableTypes(),
261+
categoriesInfo: Object.entries(this.categories).map(([name, types]) => ({
262+
name,
263+
count: types.length,
264+
types
265+
})),
266+
note: '모든 애니메이션이 개발 중입니다. 실제 컴포넌트는 곧 추가될 예정입니다.'
267+
};
268+
}
269+
}
270+
271+
// 🎨 편의 함수들
272+
export const createAnimation = (type, props) =>
273+
AnimationFactory.createAnimation(type, props);
274+
275+
export const isValidAnimationType = (type) =>
276+
AnimationFactory.isValidType(type);
277+
278+
export const getAnimationTypes = () =>
279+
AnimationFactory.getAvailableTypes();
280+
281+
export const registerAnimation = (type, component, category) =>
282+
AnimationFactory.registerAnimation(type, component, category);
283+
284+
// 기본 export
285+
export default AnimationFactory;
286+
287+
// 개발 상태 로그
288+
console.log('🏭 AnimationFactory (개발 중) 로드됨:', AnimationFactory.getFactoryInfo());

src/components/ide/IDE.css

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -726,8 +726,8 @@ body.dark-mode .visualization-button.active {
726726
position: absolute;
727727
top: 0;
728728
right: 0;
729-
bottom: 0;
730-
width: 65%; /* 화면의 65% 차지 */
729+
bottom: 20px;
730+
width: 90%; /* 화면의 65% 차지 */
731731
background-color: var(--element);
732732
border-left: 1px solid var(--border);
733733
display: flex;

0 commit comments

Comments
 (0)