feat: Add editor UI toggle functionality

- 캔버스 편집 UI 표시/숨김 기능을 추가했습니다.
- 에디터 툴바에 토글 버튼을 추가하여 UI 표시 상태를 제어할 수 있습니다.
- 에디터 UI가 숨겨졌을 때 캔버스에 마우스 이벤트가 전달되지 않도록 수정했습니다.
This commit is contained in:
BaekRyang 2025-11-05 11:52:33 +09:00
parent 0c3c0b606e
commit fed9dc7606
11 changed files with 299 additions and 99 deletions

6
.idea/AICommit.xml generated Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="com.github.blarc.ai.commits.intellij.plugin.settings.ProjectSettings">
<option name="splitButtonActionSelectedLLMClientId" value="2f900cba-1f90-431b-acb2-e4e6ac66b31e" />
</component>
</project>

30
dist/index.css vendored
View File

@ -11,6 +11,36 @@
min-height: 100vh; min-height: 100vh;
padding: 20px; padding: 20px;
} }
.editor-toolbar {
max-width: 1600px;
margin: 0 auto 16px;
display: flex;
justify-content: flex-end;
gap: 12px;
}
.editor-toggle-btn {
padding: 10px 20px;
background: #383838;
color: #e0e0e0;
border: 2px solid #555;
border-radius: 6px;
font-size: 14px;
font-weight: 500;
cursor: pointer;
transition: all 0.2s;
display: flex;
align-items: center;
gap: 8px;
}
.editor-toggle-btn:hover {
background: #404040;
border-color: #00aaff;
}
.editor-toggle-btn.active {
background: #2d5a7a;
border-color: #00aaff;
color: #fff;
}
.editor-main { .editor-main {
display: flex; display: flex;
gap: 20px; gap: 20px;

2
dist/index.css.map vendored

File diff suppressed because one or more lines are too long

49
dist/index.js vendored
View File

@ -655,7 +655,8 @@ var EditorCanvas = ({
draggingPointIndex, draggingPointIndex,
onStartDragging, onStartDragging,
onStopDragging, onStopDragging,
style: customStyle style: customStyle,
showEditor = true
}) => { }) => {
const containerRef = (0, import_react4.useRef)(null); const containerRef = (0, import_react4.useRef)(null);
const [canvasSize, setCanvasSize] = (0, import_react4.useState)({ width: 0, height: 0 }); const [canvasSize, setCanvasSize] = (0, import_react4.useState)({ width: 0, height: 0 });
@ -704,7 +705,7 @@ var EditorCanvas = ({
); );
const handleCanvasMouseDown = (0, import_react4.useCallback)( const handleCanvasMouseDown = (0, import_react4.useCallback)(
(e) => { (e) => {
if (!selectedArea || !containerRef.current) return; if (!showEditor || !selectedArea || !containerRef.current) return;
const rect = containerRef.current.getBoundingClientRect(); const rect = containerRef.current.getBoundingClientRect();
const x = (e.clientX - rect.left) / rect.width; const x = (e.clientX - rect.left) / rect.width;
const y = (e.clientY - rect.top) / rect.height; const y = (e.clientY - rect.top) / rect.height;
@ -715,11 +716,11 @@ var EditorCanvas = ({
e.preventDefault(); e.preventDefault();
} }
}, },
[selectedArea, isPointInPolygon] [showEditor, selectedArea, isPointInPolygon]
); );
const handleMouseMove = (0, import_react4.useCallback)( const handleMouseMove = (0, import_react4.useCallback)(
(e) => { (e) => {
if (!selectedArea || !containerRef.current) return; if (!showEditor || !selectedArea || !containerRef.current) return;
const rect = containerRef.current.getBoundingClientRect(); const rect = containerRef.current.getBoundingClientRect();
const x = (e.clientX - rect.left) / rect.width; const x = (e.clientX - rect.left) / rect.width;
const y = (e.clientY - rect.top) / rect.height; const y = (e.clientY - rect.top) / rect.height;
@ -738,7 +739,7 @@ var EditorCanvas = ({
setDragStartPos({ x, y }); setDragStartPos({ x, y });
} }
}, },
[draggingPointIndex, isDraggingArea, dragStartPos, selectedArea, onUpdatePoint, onUpdateArea] [showEditor, draggingPointIndex, isDraggingArea, dragStartPos, selectedArea, onUpdatePoint, onUpdateArea]
); );
const handleMouseUp = (0, import_react4.useCallback)(() => { const handleMouseUp = (0, import_react4.useCallback)(() => {
if (draggingPointIndex !== null) { if (draggingPointIndex !== null) {
@ -829,12 +830,19 @@ var EditorCanvas = ({
{ {
ref: containerRef, ref: containerRef,
className: "editor-canvas", className: "editor-canvas",
style: { width, height, position: "relative", cursor: getCursorStyle() }, style: {
onMouseDown: handleCanvasMouseDown, width,
onMouseMove: handleMouseMove, height,
position: "relative",
cursor: showEditor ? getCursorStyle() : "default",
pointerEvents: showEditor ? "auto" : "none"
// 에디터 숨김 시 포인터 이벤트 비활성화
},
onMouseDown: showEditor ? handleCanvasMouseDown : void 0,
onMouseMove: showEditor ? handleMouseMove : void 0,
children: [ children: [
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ImageDistortion, { imageSrc, areas }), /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ImageDistortion, { imageSrc, areas }),
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)( showEditor && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
"svg", "svg",
{ {
style: { style: {
@ -863,7 +871,7 @@ var EditorCanvas = ({
}) })
} }
), ),
selectedArea && canvasSize.width > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( showEditor && selectedArea && canvasSize.width > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
"canvas", "canvas",
{ {
style: { style: {
@ -887,7 +895,7 @@ var EditorCanvas = ({
} }
} }
), ),
selectedArea && selectedArea.basePoints.map((point, index) => { showEditor && selectedArea && selectedArea.basePoints.map((point, index) => {
const handleStyle = editorStyle.pointHandle || {}; const handleStyle = editorStyle.pointHandle || {};
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
"div", "div",
@ -1149,6 +1157,7 @@ var DistortionEditor = ({
stopDragging, stopDragging,
getSelectedArea getSelectedArea
} = useDistortionEditor(initialAreas); } = useDistortionEditor(initialAreas);
const [showEditor, setShowEditor] = (0, import_react5.useState)(true);
(0, import_react5.useEffect)(() => { (0, import_react5.useEffect)(() => {
onAreasChange?.(state.areas); onAreasChange?.(state.areas);
}, [state.areas, onAreasChange]); }, [state.areas, onAreasChange]);
@ -1182,7 +1191,17 @@ var DistortionEditor = ({
} }
}; };
const selectedArea = getSelectedArea(); const selectedArea = getSelectedArea();
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "distortion-editor", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "editor-main", children: [ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "distortion-editor", children: [
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "editor-toolbar", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
"button",
{
className: `editor-toggle-btn ${showEditor ? "active" : ""}`,
onClick: () => setShowEditor(!showEditor),
title: showEditor ? "\uC5D0\uB514\uD130 \uC228\uAE30\uAE30 (\uC65C\uACE1 \uD6A8\uACFC\uB9CC \uBCF4\uAE30)" : "\uC5D0\uB514\uD130 \uD45C\uC2DC",
children: showEditor ? "\u{1F441}\uFE0F \uC5D0\uB514\uD130 \uC228\uAE30\uAE30" : "\u270F\uFE0F \uC5D0\uB514\uD130 \uD45C\uC2DC"
}
) }),
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "editor-main", children: [
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "editor-canvas-container", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)( /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "editor-canvas-container", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
EditorCanvas, EditorCanvas,
{ {
@ -1196,7 +1215,8 @@ var DistortionEditor = ({
draggingPointIndex: state.draggingPointIndex, draggingPointIndex: state.draggingPointIndex,
onStartDragging: startDragging, onStartDragging: startDragging,
onStopDragging: stopDragging, onStopDragging: stopDragging,
style: canvasStyle style: canvasStyle,
showEditor
} }
) }), ) }),
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "editor-sidebar", children: [ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "editor-sidebar", children: [
@ -1212,7 +1232,8 @@ var DistortionEditor = ({
), ),
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ParameterPanel, { area: selectedArea, onUpdateArea: handleUpdateArea }) /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ParameterPanel, { area: selectedArea, onUpdateArea: handleUpdateArea })
] }) ] })
] }) }); ] })
] });
}; };
// Annotate the CommonJS export names for ESM import in node: // Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = { 0 && (module.exports = {

2
dist/index.js.map vendored

File diff suppressed because one or more lines are too long

51
dist/index.mjs vendored
View File

@ -459,7 +459,7 @@ var ImageDistortion = ({
}; };
// src/editor/DistortionEditor.tsx // src/editor/DistortionEditor.tsx
import { useEffect as useEffect4 } from "react"; import { useEffect as useEffect4, useState as useState4 } from "react";
// src/editor/hooks/useDistortionEditor.ts // src/editor/hooks/useDistortionEditor.ts
import { useState as useState2, useCallback as useCallback2 } from "react"; import { useState as useState2, useCallback as useCallback2 } from "react";
@ -609,7 +609,8 @@ var EditorCanvas = ({
draggingPointIndex, draggingPointIndex,
onStartDragging, onStartDragging,
onStopDragging, onStopDragging,
style: customStyle style: customStyle,
showEditor = true
}) => { }) => {
const containerRef = useRef3(null); const containerRef = useRef3(null);
const [canvasSize, setCanvasSize] = useState3({ width: 0, height: 0 }); const [canvasSize, setCanvasSize] = useState3({ width: 0, height: 0 });
@ -658,7 +659,7 @@ var EditorCanvas = ({
); );
const handleCanvasMouseDown = useCallback3( const handleCanvasMouseDown = useCallback3(
(e) => { (e) => {
if (!selectedArea || !containerRef.current) return; if (!showEditor || !selectedArea || !containerRef.current) return;
const rect = containerRef.current.getBoundingClientRect(); const rect = containerRef.current.getBoundingClientRect();
const x = (e.clientX - rect.left) / rect.width; const x = (e.clientX - rect.left) / rect.width;
const y = (e.clientY - rect.top) / rect.height; const y = (e.clientY - rect.top) / rect.height;
@ -669,11 +670,11 @@ var EditorCanvas = ({
e.preventDefault(); e.preventDefault();
} }
}, },
[selectedArea, isPointInPolygon] [showEditor, selectedArea, isPointInPolygon]
); );
const handleMouseMove = useCallback3( const handleMouseMove = useCallback3(
(e) => { (e) => {
if (!selectedArea || !containerRef.current) return; if (!showEditor || !selectedArea || !containerRef.current) return;
const rect = containerRef.current.getBoundingClientRect(); const rect = containerRef.current.getBoundingClientRect();
const x = (e.clientX - rect.left) / rect.width; const x = (e.clientX - rect.left) / rect.width;
const y = (e.clientY - rect.top) / rect.height; const y = (e.clientY - rect.top) / rect.height;
@ -692,7 +693,7 @@ var EditorCanvas = ({
setDragStartPos({ x, y }); setDragStartPos({ x, y });
} }
}, },
[draggingPointIndex, isDraggingArea, dragStartPos, selectedArea, onUpdatePoint, onUpdateArea] [showEditor, draggingPointIndex, isDraggingArea, dragStartPos, selectedArea, onUpdatePoint, onUpdateArea]
); );
const handleMouseUp = useCallback3(() => { const handleMouseUp = useCallback3(() => {
if (draggingPointIndex !== null) { if (draggingPointIndex !== null) {
@ -783,12 +784,19 @@ var EditorCanvas = ({
{ {
ref: containerRef, ref: containerRef,
className: "editor-canvas", className: "editor-canvas",
style: { width, height, position: "relative", cursor: getCursorStyle() }, style: {
onMouseDown: handleCanvasMouseDown, width,
onMouseMove: handleMouseMove, height,
position: "relative",
cursor: showEditor ? getCursorStyle() : "default",
pointerEvents: showEditor ? "auto" : "none"
// 에디터 숨김 시 포인터 이벤트 비활성화
},
onMouseDown: showEditor ? handleCanvasMouseDown : void 0,
onMouseMove: showEditor ? handleMouseMove : void 0,
children: [ children: [
/* @__PURE__ */ jsx2(ImageDistortion, { imageSrc, areas }), /* @__PURE__ */ jsx2(ImageDistortion, { imageSrc, areas }),
/* @__PURE__ */ jsx2( showEditor && /* @__PURE__ */ jsx2(
"svg", "svg",
{ {
style: { style: {
@ -817,7 +825,7 @@ var EditorCanvas = ({
}) })
} }
), ),
selectedArea && canvasSize.width > 0 && /* @__PURE__ */ jsx2( showEditor && selectedArea && canvasSize.width > 0 && /* @__PURE__ */ jsx2(
"canvas", "canvas",
{ {
style: { style: {
@ -841,7 +849,7 @@ var EditorCanvas = ({
} }
} }
), ),
selectedArea && selectedArea.basePoints.map((point, index) => { showEditor && selectedArea && selectedArea.basePoints.map((point, index) => {
const handleStyle = editorStyle.pointHandle || {}; const handleStyle = editorStyle.pointHandle || {};
return /* @__PURE__ */ jsx2( return /* @__PURE__ */ jsx2(
"div", "div",
@ -1103,6 +1111,7 @@ var DistortionEditor = ({
stopDragging, stopDragging,
getSelectedArea getSelectedArea
} = useDistortionEditor(initialAreas); } = useDistortionEditor(initialAreas);
const [showEditor, setShowEditor] = useState4(true);
useEffect4(() => { useEffect4(() => {
onAreasChange?.(state.areas); onAreasChange?.(state.areas);
}, [state.areas, onAreasChange]); }, [state.areas, onAreasChange]);
@ -1136,7 +1145,17 @@ var DistortionEditor = ({
} }
}; };
const selectedArea = getSelectedArea(); const selectedArea = getSelectedArea();
return /* @__PURE__ */ jsx5("div", { className: "distortion-editor", children: /* @__PURE__ */ jsxs4("div", { className: "editor-main", children: [ return /* @__PURE__ */ jsxs4("div", { className: "distortion-editor", children: [
/* @__PURE__ */ jsx5("div", { className: "editor-toolbar", children: /* @__PURE__ */ jsx5(
"button",
{
className: `editor-toggle-btn ${showEditor ? "active" : ""}`,
onClick: () => setShowEditor(!showEditor),
title: showEditor ? "\uC5D0\uB514\uD130 \uC228\uAE30\uAE30 (\uC65C\uACE1 \uD6A8\uACFC\uB9CC \uBCF4\uAE30)" : "\uC5D0\uB514\uD130 \uD45C\uC2DC",
children: showEditor ? "\u{1F441}\uFE0F \uC5D0\uB514\uD130 \uC228\uAE30\uAE30" : "\u270F\uFE0F \uC5D0\uB514\uD130 \uD45C\uC2DC"
}
) }),
/* @__PURE__ */ jsxs4("div", { className: "editor-main", children: [
/* @__PURE__ */ jsx5("div", { className: "editor-canvas-container", children: /* @__PURE__ */ jsx5( /* @__PURE__ */ jsx5("div", { className: "editor-canvas-container", children: /* @__PURE__ */ jsx5(
EditorCanvas, EditorCanvas,
{ {
@ -1150,7 +1169,8 @@ var DistortionEditor = ({
draggingPointIndex: state.draggingPointIndex, draggingPointIndex: state.draggingPointIndex,
onStartDragging: startDragging, onStartDragging: startDragging,
onStopDragging: stopDragging, onStopDragging: stopDragging,
style: canvasStyle style: canvasStyle,
showEditor
} }
) }), ) }),
/* @__PURE__ */ jsxs4("div", { className: "editor-sidebar", children: [ /* @__PURE__ */ jsxs4("div", { className: "editor-sidebar", children: [
@ -1166,7 +1186,8 @@ var DistortionEditor = ({
), ),
/* @__PURE__ */ jsx5(ParameterPanel, { area: selectedArea, onUpdateArea: handleUpdateArea }) /* @__PURE__ */ jsx5(ParameterPanel, { area: selectedArea, onUpdateArea: handleUpdateArea })
] }) ] })
] }) }); ] })
] });
}; };
export { export {
ANIMATION_CONFIG, ANIMATION_CONFIG,

2
dist/index.mjs.map vendored

File diff suppressed because one or more lines are too long

View File

@ -1,4 +1,4 @@
import React, { useEffect } from 'react'; import React, { useEffect, useState } from 'react';
import { DistortionArea } from '../types/area'; import { DistortionArea } from '../types/area';
import { DistortionEditorProps } from './types'; import { DistortionEditorProps } from './types';
import { useDistortionEditor } from './hooks/useDistortionEditor'; import { useDistortionEditor } from './hooks/useDistortionEditor';
@ -28,6 +28,9 @@ export const DistortionEditor: React.FC<DistortionEditorProps> = ({
getSelectedArea, getSelectedArea,
} = useDistortionEditor(initialAreas); } = useDistortionEditor(initialAreas);
// 에디터 모드 토글 상태
const [showEditor, setShowEditor] = useState(true);
// 영역 변경 시 콜백 호출 // 영역 변경 시 콜백 호출
useEffect(() => { useEffect(() => {
onAreasChange?.(state.areas); onAreasChange?.(state.areas);
@ -72,6 +75,17 @@ export const DistortionEditor: React.FC<DistortionEditorProps> = ({
return ( return (
<div className="distortion-editor"> <div className="distortion-editor">
{/* 에디터 모드 토글 버튼 */}
<div className="editor-toolbar">
<button
className={`editor-toggle-btn ${showEditor ? 'active' : ''}`}
onClick={() => setShowEditor(!showEditor)}
title={showEditor ? '에디터 숨기기 (왜곡 효과만 보기)' : '에디터 표시'}
>
{showEditor ? '👁️ 에디터 숨기기' : '✏️ 에디터 표시'}
</button>
</div>
<div className="editor-main"> <div className="editor-main">
{/* 왼쪽: 캔버스 */} {/* 왼쪽: 캔버스 */}
<div className="editor-canvas-container"> <div className="editor-canvas-container">
@ -87,6 +101,7 @@ export const DistortionEditor: React.FC<DistortionEditorProps> = ({
onStartDragging={startDragging} onStartDragging={startDragging}
onStopDragging={stopDragging} onStopDragging={stopDragging}
style={canvasStyle} style={canvasStyle}
showEditor={showEditor}
/> />
</div> </div>

View File

@ -17,6 +17,8 @@ interface EditorCanvasProps {
onStopDragging: () => void; onStopDragging: () => void;
/** 에디터 캔버스 스타일 커스터마이징 */ /** 에디터 캔버스 스타일 커스터마이징 */
style?: EditorCanvasStyle; style?: EditorCanvasStyle;
/** 에디터 UI 표시 여부 (기본값: true) */
showEditor?: boolean;
} }
export const EditorCanvas: React.FC<EditorCanvasProps> = ({ export const EditorCanvas: React.FC<EditorCanvasProps> = ({
@ -31,6 +33,7 @@ export const EditorCanvas: React.FC<EditorCanvasProps> = ({
onStartDragging, onStartDragging,
onStopDragging, onStopDragging,
style: customStyle, style: customStyle,
showEditor = true,
}) => { }) => {
const containerRef = useRef<HTMLDivElement>(null); const containerRef = useRef<HTMLDivElement>(null);
const [canvasSize, setCanvasSize] = useState({width: 0, height: 0}); const [canvasSize, setCanvasSize] = useState({width: 0, height: 0});
@ -93,7 +96,8 @@ export const EditorCanvas: React.FC<EditorCanvasProps> = ({
// 캔버스 클릭 (사각형 내부 클릭 감지) // 캔버스 클릭 (사각형 내부 클릭 감지)
const handleCanvasMouseDown = useCallback( const handleCanvasMouseDown = useCallback(
(e: React.MouseEvent) => { (e: React.MouseEvent) => {
if (!selectedArea || !containerRef.current) return; // 에디터가 숨겨진 상태면 동작하지 않음
if (!showEditor || !selectedArea || !containerRef.current) return;
const rect = containerRef.current.getBoundingClientRect(); const rect = containerRef.current.getBoundingClientRect();
const x = (e.clientX - rect.left) / rect.width; const x = (e.clientX - rect.left) / rect.width;
@ -107,12 +111,13 @@ export const EditorCanvas: React.FC<EditorCanvasProps> = ({
e.preventDefault(); e.preventDefault();
} }
}, },
[selectedArea, isPointInPolygon] [showEditor, selectedArea, isPointInPolygon]
); );
const handleMouseMove = useCallback( const handleMouseMove = useCallback(
(e: React.MouseEvent) => { (e: React.MouseEvent) => {
if (!selectedArea || !containerRef.current) return; // 에디터가 숨겨진 상태면 동작하지 않음
if (!showEditor || !selectedArea || !containerRef.current) return;
const rect = containerRef.current.getBoundingClientRect(); const rect = containerRef.current.getBoundingClientRect();
const x = (e.clientX - rect.left) / rect.width; const x = (e.clientX - rect.left) / rect.width;
@ -139,7 +144,7 @@ export const EditorCanvas: React.FC<EditorCanvasProps> = ({
setDragStartPos({ x, y }); // 다음 프레임을 위해 시작 위치 업데이트 setDragStartPos({ x, y }); // 다음 프레임을 위해 시작 위치 업데이트
} }
}, },
[draggingPointIndex, isDraggingArea, dragStartPos, selectedArea, onUpdatePoint, onUpdateArea] [showEditor, draggingPointIndex, isDraggingArea, dragStartPos, selectedArea, onUpdatePoint, onUpdateArea]
); );
const handleMouseUp = useCallback(() => { const handleMouseUp = useCallback(() => {
@ -270,14 +275,21 @@ export const EditorCanvas: React.FC<EditorCanvasProps> = ({
<div <div
ref={containerRef} ref={containerRef}
className="editor-canvas" className="editor-canvas"
style={{width, height, position: 'relative', cursor: getCursorStyle()}} style={{
onMouseDown={handleCanvasMouseDown} width,
onMouseMove={handleMouseMove} height,
position: 'relative',
cursor: showEditor ? getCursorStyle() : 'default',
pointerEvents: showEditor ? 'auto' : 'none', // 에디터 숨김 시 포인터 이벤트 비활성화
}}
onMouseDown={showEditor ? handleCanvasMouseDown : undefined}
onMouseMove={showEditor ? handleMouseMove : undefined}
> >
{/* ImageDistortion 컴포넌트 */} {/* ImageDistortion 컴포넌트 */}
<ImageDistortion imageSrc={imageSrc} areas={areas}/> <ImageDistortion imageSrc={imageSrc} areas={areas}/>
{/* 오버레이 SVG */} {/* 오버레이 SVG - 에디터 모드일 때만 표시 */}
{showEditor && (
<svg <svg
style={{ style={{
position: 'absolute', position: 'absolute',
@ -310,9 +322,10 @@ export const EditorCanvas: React.FC<EditorCanvasProps> = ({
); );
})} })}
</svg> </svg>
)}
{/* 선택된 영역의 타원 가이드 (Canvas로 그리기) */} {/* 선택된 영역의 타원 가이드 (Canvas로 그리기) - 에디터 모드일 때만 표시 */}
{selectedArea && canvasSize.width > 0 && ( {showEditor && selectedArea && canvasSize.width > 0 && (
<canvas <canvas
style={{ style={{
position: 'absolute', position: 'absolute',
@ -336,8 +349,8 @@ export const EditorCanvas: React.FC<EditorCanvasProps> = ({
/> />
)} )}
{/* 선택된 영역의 포인트 핸들 */} {/* 선택된 영역의 포인트 핸들 - 에디터 모드일 때만 표시 */}
{selectedArea && {showEditor && selectedArea &&
selectedArea.basePoints.map((point, index) => { selectedArea.basePoints.map((point, index) => {
const handleStyle = editorStyle.pointHandle || {}; const handleStyle = editorStyle.pointHandle || {};
return ( return (

59
src/editor/constants.ts Normal file
View File

@ -0,0 +1,59 @@
import { EditorCanvasStyle } from './types';
/**
*
*/
export const DEFAULT_EDITOR_CANVAS_STYLE: EditorCanvasStyle = {
// 3단계 원 스타일 (외부 -> 내부)
circleLevels: [
{
radius: 0.5,
opacity: 0.3,
lineWidth: 2,
color: 'rgba(255, 200, 0, 1)',
dashPattern: [8, 4],
},
{
radius: 0.33,
opacity: 0.6,
lineWidth: 2.5,
color: 'rgba(255, 200, 0, 1)',
dashPattern: [8, 4],
},
{
radius: 0.167,
opacity: 0.9,
lineWidth: 3,
color: 'rgba(255, 200, 0, 1)',
dashPattern: [8, 4],
},
],
// 원 내부 채우기
circleFillColor: 'rgba(255, 200, 0, 0.08)',
// 중심점
centerPoint: {
radius: 5,
fillColor: 'rgba(255, 200, 0, 1)',
strokeColor: 'rgba(255, 255, 255, 0.8)',
strokeWidth: 2,
},
// 포인트 핸들
pointHandle: {
size: 16,
fillColor: '#00aaff',
strokeColor: 'white',
strokeWidth: 2,
labelColor: '#00aaff',
labelFontSize: 11,
},
// 영역 외곽선
areaOutline: {
selectedColor: '#00aaff',
unselectedColor: '#888',
selectedWidth: 2,
unselectedWidth: 1,
unselectedDashPattern: [5, 5],
selectedFillColor: 'rgba(0, 170, 255, 0.08)', // 선택된 영역 배경 (연한 파란색)
unselectedFillColor: 'rgba(136, 136, 136, 0.03)', // 선택 안된 영역 배경 (연한 회색)
},
};

View File

@ -7,6 +7,41 @@
padding: 20px; padding: 20px;
} }
/* 에디터 툴바 */
.editor-toolbar {
max-width: 1600px;
margin: 0 auto 16px;
display: flex;
justify-content: flex-end;
gap: 12px;
}
.editor-toggle-btn {
padding: 10px 20px;
background: #383838;
color: #e0e0e0;
border: 2px solid #555;
border-radius: 6px;
font-size: 14px;
font-weight: 500;
cursor: pointer;
transition: all 0.2s;
display: flex;
align-items: center;
gap: 8px;
}
.editor-toggle-btn:hover {
background: #404040;
border-color: #00aaff;
}
.editor-toggle-btn.active {
background: #2d5a7a;
border-color: #00aaff;
color: #fff;
}
.editor-main { .editor-main {
display: flex; display: flex;
gap: 20px; gap: 20px;