goldenChat base source add
This commit is contained in:
@@ -0,0 +1,229 @@
|
||||
import React from 'react';
|
||||
import type { Drawing, DrawingToolId } from '../types';
|
||||
import { useDraggablePanel } from '../hooks/useDraggablePanel';
|
||||
|
||||
interface ObjectTreeProps {
|
||||
drawings: Drawing[];
|
||||
onToggleVisible: (id: string) => void;
|
||||
onRemove: (id: string) => void;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
// 드로잉 타입 → 한글 이름
|
||||
const TOOL_LABEL: Partial<Record<DrawingToolId, string>> = {
|
||||
cursor: '커서',
|
||||
hline: '수평선',
|
||||
hline_ray: '수평 레이',
|
||||
vline: '수직선',
|
||||
cross_line: '십자선',
|
||||
info_line: '정보선',
|
||||
trendline: '추세선',
|
||||
extended_line: '연장 추세선',
|
||||
ray: '레이',
|
||||
arrow: '화살표 추세선',
|
||||
fib: '피보나치 되돌림',
|
||||
fib_ext: '피보나치 확장',
|
||||
fib_channel: '피보나치 채널',
|
||||
fibtz: '피보나치 타임존',
|
||||
fib_speed_fan: '스피드 리지스턴스 팬',
|
||||
fib_trend_time: '추세기반 피보나치 시간',
|
||||
fib_circles: '피보나치 서클',
|
||||
fib_spiral: '피보나치 스파이럴',
|
||||
fib_speed_arc: '스피드 리지스턴스 아크',
|
||||
fib_wedge: '피보나치 웻지',
|
||||
pitchfork: '앤드류스 피치포크',
|
||||
pitchfork_schiff: '쉬프 피치포크',
|
||||
pitchfork_inside: '인사이드 피치포크',
|
||||
gann_box: '갠 박스',
|
||||
gann_square: '갠 스퀘어',
|
||||
gann_fan: '갠 팬',
|
||||
xabcd: 'XABCD 패턴',
|
||||
cypher: '사이퍼 패턴',
|
||||
abcd: 'ABCD 패턴',
|
||||
head_shoulders: '헤드 앤 숄더',
|
||||
channel: '추세 채널',
|
||||
parallel_channel: '평행 채널',
|
||||
disjoint_channel: '분리 채널',
|
||||
flat_top_bottom: '플랫 탑/바텀 채널',
|
||||
rect: '사각형',
|
||||
rotated_rect: '회전 사각형',
|
||||
circle: '원',
|
||||
ellipse: '타원',
|
||||
triangle: '삼각형',
|
||||
parallelogram: '평행사변형',
|
||||
polyline: '다각형',
|
||||
arc: '호',
|
||||
text: '텍스트',
|
||||
callout: '콜아웃',
|
||||
price_note: '가격 주석',
|
||||
arrow_mark_up: '위쪽 화살표',
|
||||
arrow_mark_down: '아래쪽 화살표',
|
||||
price_label: '가격 레이블',
|
||||
pencil: '자유곡선',
|
||||
highlight: '형광펜',
|
||||
trend_angle: '추세각',
|
||||
long_position: '매수 포지션',
|
||||
short_position: '숏 포지션',
|
||||
forecast: '예측',
|
||||
bar_pattern: '봉패턴',
|
||||
ghost_feed: '고스트피드',
|
||||
projection_tool: '프로젝션',
|
||||
anchored_vwap: '앵커드 VWAP',
|
||||
fixed_volume_profile: '볼륨 프로파일',
|
||||
price_range: '가격범위',
|
||||
wedge_pattern: '세모 패턴',
|
||||
three_drives: '쓰리 드라이브 패턴',
|
||||
elliott_impulse: '엘리엇 임펄스 파동',
|
||||
elliott_correction: '엘리엇 코렉션 파동',
|
||||
elliott_triangle_wave: '엘리엇 트라이앵글 웨이브',
|
||||
elliott_double_combo: '엘리엇 다블콤보 파동',
|
||||
arrow_mark: '화살표',
|
||||
anchored_arrow: '앵커 화살표',
|
||||
arrow_mark_left: '왼화살표',
|
||||
arrow_mark_right: '오른화살표',
|
||||
fixed_text: '고정위치문자',
|
||||
note_text: '노트',
|
||||
pin_mark: '핀',
|
||||
table_tool: '테이블',
|
||||
comment_mark: '코멘트',
|
||||
guide_line: '길잡이',
|
||||
flag_mark: '플래그',
|
||||
measure: '가격 측정',
|
||||
date_range: '날짜 범위',
|
||||
date_price_range: '날짜&가격 범위',
|
||||
data_window: '데이터 창',
|
||||
zoom: '확대/축소',
|
||||
magnifier: '돋보기',
|
||||
};
|
||||
|
||||
// 아이콘 기본값
|
||||
const DefaultIcon = () => <svg width="12" height="12" viewBox="0 0 12 12" fill="none" stroke="currentColor" strokeWidth="1.2"><circle cx="6" cy="6" r="4"/></svg>;
|
||||
|
||||
// 타입별 아이콘
|
||||
const ToolIcon: Partial<Record<DrawingToolId, React.ReactNode>> = {
|
||||
cursor: <svg width="12" height="12" viewBox="0 0 12 12" fill="currentColor"><path d="M2 1 L2 9 L4 7 L6 10 L7 9.5 L5 6.5 L8 6.5 Z"/></svg>,
|
||||
hline: <svg width="12" height="12" viewBox="0 0 12 12" fill="none" stroke="currentColor" strokeWidth="1.2"><line x1="1" y1="6" x2="11" y2="6"/></svg>,
|
||||
vline: <svg width="12" height="12" viewBox="0 0 12 12" fill="none" stroke="currentColor" strokeWidth="1.2"><line x1="6" y1="1" x2="6" y2="11"/></svg>,
|
||||
trendline: <svg width="12" height="12" viewBox="0 0 12 12" fill="none" stroke="currentColor" strokeWidth="1.2"><line x1="2" y1="10" x2="10" y2="2"/></svg>,
|
||||
ray: <svg width="12" height="12" viewBox="0 0 12 12" fill="none" stroke="currentColor" strokeWidth="1.2"><line x1="2" y1="10" x2="11" y2="2"/><circle cx="2" cy="10" r="1.2" fill="currentColor"/></svg>,
|
||||
fib: <svg width="12" height="12" viewBox="0 0 12 12" fill="none" stroke="currentColor" strokeWidth="1"><line x1="1" y1="2" x2="11" y2="2"/><line x1="1" y1="5" x2="11" y2="5" strokeOpacity="0.6"/><line x1="1" y1="7" x2="11" y2="7"/><line x1="1" y1="10" x2="11" y2="10"/></svg>,
|
||||
fibtz: <svg width="12" height="12" viewBox="0 0 12 12" fill="none" stroke="currentColor" strokeWidth="1"><line x1="1" y1="1" x2="1" y2="11"/><line x1="3" y1="1" x2="3" y2="11" strokeOpacity="0.7"/><line x1="6" y1="1" x2="6" y2="11" strokeOpacity="0.5"/><line x1="9" y1="1" x2="9" y2="11" strokeOpacity="0.6"/><line x1="11" y1="1" x2="11" y2="11" strokeOpacity="0.4"/></svg>,
|
||||
rect: <svg width="12" height="12" viewBox="0 0 12 12" fill="none" stroke="currentColor" strokeWidth="1.2"><rect x="2" y="3" width="8" height="6"/></svg>,
|
||||
circle: <svg width="12" height="12" viewBox="0 0 12 12" fill="none" stroke="currentColor" strokeWidth="1.2"><circle cx="6" cy="6" r="4.5"/></svg>,
|
||||
triangle: <svg width="12" height="12" viewBox="0 0 12 12" fill="none" stroke="currentColor" strokeWidth="1.2"><path d="M6 2 L11 10 L1 10 Z"/></svg>,
|
||||
channel: <svg width="12" height="12" viewBox="0 0 12 12" fill="none" stroke="currentColor" strokeWidth="1.2"><line x1="1" y1="8" x2="11" y2="3"/><line x1="1" y1="11" x2="11" y2="6" strokeDasharray="2 2"/></svg>,
|
||||
measure: <svg width="12" height="12" viewBox="0 0 12 12" fill="none" stroke="currentColor" strokeWidth="1.2"><line x1="1" y1="6" x2="11" y2="6"/><line x1="1" y1="4" x2="1" y2="8"/><line x1="11" y1="4" x2="11" y2="8"/></svg>,
|
||||
text: <svg width="12" height="12" viewBox="0 0 12 12" fill="currentColor"><text x="2" y="10" fontSize="10" fontWeight="700" fontFamily="sans-serif">T</text></svg>,
|
||||
pencil: <svg width="12" height="12" viewBox="0 0 12 12" fill="none" stroke="currentColor" strokeWidth="1.2"><path d="M2 10 L2.5 7 L9 1.5 Q10 1 11 2 Q12 3 11 4 L4.5 10 Z"/></svg>,
|
||||
pitchfork: <svg width="12" height="12" viewBox="0 0 12 12" fill="none" stroke="currentColor" strokeWidth="1.2"><line x1="2" y1="6" x2="7" y2="5"/><line x1="7" y1="5" x2="11" y2="3"/><line x1="7" y1="5" x2="11" y2="7"/></svg>,
|
||||
};
|
||||
|
||||
const IcEye = ({ visible }: { visible: boolean }) => visible ? (
|
||||
<svg width="13" height="13" viewBox="0 0 13 13" fill="none" stroke="currentColor" strokeWidth="1.3">
|
||||
<path d="M1 6.5 Q4 2 6.5 2 Q9 2 12 6.5 Q9 11 6.5 11 Q4 11 1 6.5 Z"/>
|
||||
<circle cx="6.5" cy="6.5" r="1.8"/>
|
||||
</svg>
|
||||
) : (
|
||||
<svg width="13" height="13" viewBox="0 0 13 13" fill="none" stroke="currentColor" strokeWidth="1.3">
|
||||
<path d="M1 6.5 Q4 2 6.5 2 Q9 2 12 6.5 Q9 11 6.5 11 Q4 11 1 6.5 Z" strokeOpacity="0.3"/>
|
||||
<line x1="2" y1="2" x2="11" y2="11"/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
const IcTrash = () => (
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" stroke="currentColor" strokeWidth="1.2">
|
||||
<line x1="2" y1="4" x2="10" y2="4"/>
|
||||
<path d="M4.5 4 V2.5 Q4.5 2 6 2 Q7.5 2 7.5 2.5 V4"/>
|
||||
<path d="M3 4 L3.5 10.5 Q3.5 11 4 11 L8 11 Q8.5 11 8.5 10.5 L9 4"/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
const ObjectTree: React.FC<ObjectTreeProps> = ({ drawings, onToggleVisible, onRemove, onClose }) => {
|
||||
const {
|
||||
panelRef,
|
||||
dragging,
|
||||
onHeaderPointerDown, headerTouchStyle,
|
||||
panelStyle,
|
||||
headerCursor,
|
||||
} = useDraggablePanel({ centerOnMount: true });
|
||||
|
||||
return (
|
||||
<div className="obj-tree-overlay" onMouseDown={e => { if (e.target === e.currentTarget) onClose(); }}>
|
||||
<div
|
||||
ref={panelRef}
|
||||
className="obj-tree-panel"
|
||||
style={{
|
||||
...panelStyle,
|
||||
zIndex: 5001,
|
||||
cursor: dragging ? 'grabbing' : undefined,
|
||||
}}
|
||||
onMouseDown={e => e.stopPropagation()}
|
||||
>
|
||||
<div
|
||||
className="gc-popup-header obj-tree-header"
|
||||
onPointerDown={onHeaderPointerDown}
|
||||
style={{ cursor: headerCursor, ...headerTouchStyle }}
|
||||
>
|
||||
<span className="gc-popup-title obj-tree-title">오브젝트 트리</span>
|
||||
<button type="button" className="gc-popup-close obj-tree-close" onClick={onClose}>✕</button>
|
||||
</div>
|
||||
|
||||
{drawings.length === 0 ? (
|
||||
<div className="obj-tree-empty">드로잉이 없습니다</div>
|
||||
) : (
|
||||
<div className="obj-tree-list">
|
||||
{[...drawings].reverse().map((d, idx) => {
|
||||
const visible = d.visible !== false;
|
||||
return (
|
||||
<div
|
||||
key={d.id}
|
||||
className={`obj-tree-item${!visible ? ' hidden' : ''}`}
|
||||
>
|
||||
{/* 색상 스와치 */}
|
||||
<span
|
||||
className="obj-tree-swatch"
|
||||
style={{ background: d.color }}
|
||||
/>
|
||||
|
||||
{/* 타입 아이콘 */}
|
||||
<span className="obj-tree-icon" style={{ color: d.color }}>
|
||||
{ToolIcon[d.type as DrawingToolId] ?? <DefaultIcon />}
|
||||
</span>
|
||||
|
||||
{/* 이름 */}
|
||||
<span className="obj-tree-name">
|
||||
{TOOL_LABEL[d.type as DrawingToolId] ?? d.type}
|
||||
{d.text && <span className="obj-tree-text-preview"> "{d.text}"</span>}
|
||||
</span>
|
||||
|
||||
{/* 번호 (역순) */}
|
||||
<span className="obj-tree-num">#{drawings.length - idx}</span>
|
||||
|
||||
{/* 가시성 토글 */}
|
||||
<button
|
||||
className="obj-tree-btn"
|
||||
title={visible ? '숨기기' : '표시'}
|
||||
onClick={() => onToggleVisible(d.id)}
|
||||
>
|
||||
<IcEye visible={visible} />
|
||||
</button>
|
||||
|
||||
{/* 삭제 */}
|
||||
<button
|
||||
className="obj-tree-btn obj-tree-del"
|
||||
title="삭제"
|
||||
onClick={() => onRemove(d.id)}
|
||||
>
|
||||
<IcTrash />
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ObjectTree;
|
||||
Reference in New Issue
Block a user