229 lines
7.9 KiB
TypeScript
229 lines
7.9 KiB
TypeScript
import React, { useState } from 'react';
|
|
import type { Drawing, DrawingStyle } from '../types';
|
|
import { useDraggablePanel } from '../hooks/useDraggablePanel';
|
|
|
|
const PRESET_COLORS = [
|
|
'#FF6D00','#FF5722','#EF5350','#E91E63','#9C27B0',
|
|
'#673AB7','#3F51B5','#2196F3','#03A9F4','#00BCD4',
|
|
'#009688','#4CAF50','#8BC34A','#CDDC39','#FFC107',
|
|
'#FF9800','#795548','#9E9E9E','#607D8B','#FFFFFF',
|
|
];
|
|
|
|
const LINE_WIDTHS = [1, 2, 3, 4] as const;
|
|
const LINE_STYLES: { value: DrawingStyle; label: string }[] = [
|
|
{ value: 'solid', label: '실선' },
|
|
{ value: 'dashed', label: '파선' },
|
|
{ value: 'dotted', label: '점선' },
|
|
];
|
|
|
|
interface Props {
|
|
drawing: Drawing;
|
|
onSave: (updated: Drawing) => void;
|
|
onCancel: () => void;
|
|
}
|
|
|
|
const DrawingSettingsModal: React.FC<Props> = ({ drawing, onSave, onCancel }) => {
|
|
const [color, setColor] = useState(drawing.color);
|
|
const [lineWidth, setLineWidth] = useState(drawing.lineWidth);
|
|
const [style, setStyle] = useState<DrawingStyle>(drawing.style);
|
|
const [text, setText] = useState(drawing.text ?? '');
|
|
|
|
const handleSave = () => {
|
|
onSave({
|
|
...drawing,
|
|
color,
|
|
lineWidth,
|
|
style,
|
|
...(drawing.type === 'text' ? { text: text.trim() || drawing.text } : {}),
|
|
});
|
|
};
|
|
|
|
const {
|
|
panelRef,
|
|
dragging,
|
|
onHeaderPointerDown, headerTouchStyle,
|
|
panelStyle,
|
|
headerCursor,
|
|
} = useDraggablePanel({ centerOnMount: true });
|
|
|
|
return (
|
|
<div
|
|
className="dsm-overlay"
|
|
style={{ zIndex: 1200 }}
|
|
onMouseDown={e => { if (e.target === e.currentTarget) onCancel(); }}
|
|
>
|
|
<div
|
|
ref={panelRef}
|
|
className="dsm-dialog"
|
|
style={{
|
|
...panelStyle,
|
|
minWidth: 300,
|
|
cursor: dragging ? 'grabbing' : undefined,
|
|
}}
|
|
onMouseDown={e => e.stopPropagation()}
|
|
>
|
|
<div
|
|
className="gc-popup-header"
|
|
onPointerDown={onHeaderPointerDown}
|
|
style={{ cursor: headerCursor, ...headerTouchStyle }}
|
|
>
|
|
<span className="gc-popup-title">드로잉 설정</span>
|
|
<button type="button" className="gc-popup-close" onClick={onCancel} aria-label="닫기">✕</button>
|
|
</div>
|
|
<div className="dsm-body">
|
|
|
|
{/* 색상 */}
|
|
<div style={{ marginBottom: 16 }}>
|
|
<div style={{ fontSize: 11, color: 'var(--text-muted, #6e7191)', marginBottom: 8 }}>색상</div>
|
|
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 5 }}>
|
|
{PRESET_COLORS.map(c => (
|
|
<button
|
|
key={c}
|
|
onClick={() => setColor(c)}
|
|
style={{
|
|
width: 20,
|
|
height: 20,
|
|
borderRadius: '50%',
|
|
background: c,
|
|
border: color === c ? '2px solid #fff' : '2px solid transparent',
|
|
cursor: 'pointer',
|
|
padding: 0,
|
|
flexShrink: 0,
|
|
}}
|
|
/>
|
|
))}
|
|
{/* 커스텀 컬러 피커 */}
|
|
<input
|
|
type="color"
|
|
value={color}
|
|
onChange={e => setColor(e.target.value)}
|
|
title="커스텀 색상"
|
|
style={{
|
|
width: 20,
|
|
height: 20,
|
|
borderRadius: '50%',
|
|
border: 'none',
|
|
padding: 0,
|
|
cursor: 'pointer',
|
|
background: 'none',
|
|
}}
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
{/* 선 굵기 (pencil·text·measure 제외) */}
|
|
{drawing.type !== 'text' && drawing.type !== 'measure' && (
|
|
<div style={{ marginBottom: 16 }}>
|
|
<div style={{ fontSize: 11, color: 'var(--text-muted, #6e7191)', marginBottom: 8 }}>선 굵기</div>
|
|
<div style={{ display: 'flex', gap: 6 }}>
|
|
{LINE_WIDTHS.map(w => (
|
|
<button
|
|
key={w}
|
|
onClick={() => setLineWidth(w)}
|
|
style={{
|
|
width: 34,
|
|
height: 28,
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
background: lineWidth === w ? 'var(--accent-bg, rgba(122,162,247,0.15))' : 'transparent',
|
|
border: lineWidth === w ? '1px solid var(--accent, #7aa2f7)' : '1px solid var(--border, rgba(122,162,247,0.2))',
|
|
borderRadius: 4,
|
|
cursor: 'pointer',
|
|
color: 'inherit',
|
|
}}
|
|
>
|
|
<div style={{ width: 20, height: w, background: 'currentColor', borderRadius: 1 }} />
|
|
</button>
|
|
))}
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{/* 선 스타일 (hline, vline, trendline, ray, channel 계열) */}
|
|
{!['text', 'measure', 'pencil', 'fib', 'rect'].includes(drawing.type) && (
|
|
<div style={{ marginBottom: 16 }}>
|
|
<div style={{ fontSize: 11, color: 'var(--text-muted, #6e7191)', marginBottom: 8 }}>선 스타일</div>
|
|
<div style={{ display: 'flex', gap: 6 }}>
|
|
{LINE_STYLES.map(({ value, label }) => (
|
|
<button
|
|
key={value}
|
|
onClick={() => setStyle(value)}
|
|
style={{
|
|
flex: 1,
|
|
height: 28,
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
background: style === value ? 'var(--accent-bg, rgba(122,162,247,0.15))' : 'transparent',
|
|
border: style === value ? '1px solid var(--accent, #7aa2f7)' : '1px solid var(--border, rgba(122,162,247,0.2))',
|
|
borderRadius: 4,
|
|
cursor: 'pointer',
|
|
color: 'inherit',
|
|
fontSize: 12,
|
|
}}
|
|
>
|
|
{label}
|
|
</button>
|
|
))}
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{/* 텍스트 (text 타입만) */}
|
|
{drawing.type === 'text' && (
|
|
<div style={{ marginBottom: 16 }}>
|
|
<div style={{ fontSize: 11, color: 'var(--text-muted, #6e7191)', marginBottom: 8 }}>텍스트</div>
|
|
<input
|
|
value={text}
|
|
onChange={e => setText(e.target.value)}
|
|
style={{
|
|
width: '100%',
|
|
boxSizing: 'border-box',
|
|
background: 'rgba(255,255,255,0.05)',
|
|
border: '1px solid var(--border, rgba(122,162,247,0.25))',
|
|
borderRadius: 4,
|
|
padding: '6px 8px',
|
|
color: 'inherit',
|
|
fontSize: 13,
|
|
}}
|
|
/>
|
|
</div>
|
|
)}
|
|
|
|
{/* 버튼 */}
|
|
<div className="dsm-footer">
|
|
<button
|
|
onClick={onCancel}
|
|
style={{
|
|
padding: '6px 16px',
|
|
background: 'transparent',
|
|
border: '1px solid var(--border, rgba(122,162,247,0.25))',
|
|
borderRadius: 5,
|
|
cursor: 'pointer',
|
|
color: 'inherit',
|
|
fontSize: 13,
|
|
}}
|
|
>취소</button>
|
|
<button
|
|
onClick={handleSave}
|
|
style={{
|
|
padding: '6px 16px',
|
|
background: 'var(--accent, #7aa2f7)',
|
|
border: 'none',
|
|
borderRadius: 5,
|
|
cursor: 'pointer',
|
|
color: '#1a1b26',
|
|
fontWeight: 600,
|
|
fontSize: 13,
|
|
}}
|
|
>확인</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default DrawingSettingsModal;
|