feat: Add area selection functionality

- EditorCanvas 컴포넌트에 영역 선택 콜백(onSelectArea) 추가
- 비선택 영역 클릭 시 해당 영역을 선택하는 기능 구현
- 드래그 시작 조건에서 showEditor만 확인하도록 수정 (selectedArea 불필요)
- package.json 버전 1.2.0에서 1.2.1로 업데이트
This commit is contained in:
BaekRyang 2025-11-26 13:53:15 +09:00
parent 317c7c5c92
commit 6b6c8d8fd0
8 changed files with 60 additions and 15 deletions

2
dist/index.d.mts vendored
View File

@ -332,6 +332,8 @@ interface EditorCanvasProps {
style?: EditorCanvasStyle;
/** 에디터 UI 표시 여부 (기본값: true) */
showEditor?: boolean;
/** 영역 선택 콜백 (비선택 영역 클릭 시) */
onSelectArea?: (areaId: string) => void;
}
declare const EditorCanvas: React$1.FC<EditorCanvasProps>;

2
dist/index.d.ts vendored
View File

@ -332,6 +332,8 @@ interface EditorCanvasProps {
style?: EditorCanvasStyle;
/** 에디터 UI 표시 여부 (기본값: true) */
showEditor?: boolean;
/** 영역 선택 콜백 (비선택 영역 클릭 시) */
onSelectArea?: (areaId: string) => void;
}
declare const EditorCanvas: React$1.FC<EditorCanvasProps>;

20
dist/index.js vendored
View File

@ -1316,7 +1316,8 @@ var EditorCanvas = ({
onStartDragging,
onStopDragging,
style: customStyle,
showEditor = true
showEditor = true,
onSelectArea
}) => {
const containerRef = (0, import_react6.useRef)(null);
const [canvasSize, setCanvasSize] = (0, import_react6.useState)({ width: 0, height: 0 });
@ -1365,7 +1366,7 @@ var EditorCanvas = ({
);
const handleCanvasDown = (0, import_react6.useCallback)(
(e) => {
if (!showEditor || !selectedArea || !containerRef.current) return;
if (!showEditor || !containerRef.current) return;
const rect = containerRef.current.getBoundingClientRect();
let clientX, clientY;
if ("touches" in e) {
@ -1379,13 +1380,24 @@ var EditorCanvas = ({
const x = (clientX - rect.left) / rect.width;
const y = (clientY - rect.top) / rect.height;
const clickPoint = { x, y };
if (isPointInPolygon2(clickPoint, selectedArea.basePoints)) {
if (selectedArea && isPointInPolygon2(clickPoint, selectedArea.basePoints)) {
setIsDraggingArea(true);
setDragStartPos(clickPoint);
e.preventDefault();
return;
}
if (onSelectArea) {
for (let i = areas.length - 1; i >= 0; i--) {
const area = areas[i];
if (area.id !== selectedAreaId && isPointInPolygon2(clickPoint, area.basePoints)) {
onSelectArea(area.id);
e.preventDefault();
return;
}
}
}
},
[showEditor, selectedArea, isPointInPolygon2]
[showEditor, selectedArea, selectedAreaId, areas, isPointInPolygon2, onSelectArea]
);
const handleMove = (0, import_react6.useCallback)(
(e) => {

2
dist/index.js.map vendored

File diff suppressed because one or more lines are too long

20
dist/index.mjs vendored
View File

@ -1256,7 +1256,8 @@ var EditorCanvas = ({
onStartDragging,
onStopDragging,
style: customStyle,
showEditor = true
showEditor = true,
onSelectArea
}) => {
const containerRef = useRef5(null);
const [canvasSize, setCanvasSize] = useState4({ width: 0, height: 0 });
@ -1305,7 +1306,7 @@ var EditorCanvas = ({
);
const handleCanvasDown = useCallback5(
(e) => {
if (!showEditor || !selectedArea || !containerRef.current) return;
if (!showEditor || !containerRef.current) return;
const rect = containerRef.current.getBoundingClientRect();
let clientX, clientY;
if ("touches" in e) {
@ -1319,13 +1320,24 @@ var EditorCanvas = ({
const x = (clientX - rect.left) / rect.width;
const y = (clientY - rect.top) / rect.height;
const clickPoint = { x, y };
if (isPointInPolygon2(clickPoint, selectedArea.basePoints)) {
if (selectedArea && isPointInPolygon2(clickPoint, selectedArea.basePoints)) {
setIsDraggingArea(true);
setDragStartPos(clickPoint);
e.preventDefault();
return;
}
if (onSelectArea) {
for (let i = areas.length - 1; i >= 0; i--) {
const area = areas[i];
if (area.id !== selectedAreaId && isPointInPolygon2(clickPoint, area.basePoints)) {
onSelectArea(area.id);
e.preventDefault();
return;
}
}
}
},
[showEditor, selectedArea, isPointInPolygon2]
[showEditor, selectedArea, selectedAreaId, areas, isPointInPolygon2, onSelectArea]
);
const handleMove = useCallback5(
(e) => {

2
dist/index.mjs.map vendored

File diff suppressed because one or more lines are too long

View File

@ -1,6 +1,6 @@
{
"name": "@baekryang/responsive-image-canvas",
"version": "1.2.0",
"version": "1.2.1",
"publishConfig": {
"registry": "https://git.bnovalab.com/api/packages/baekryang/npm/"
},

View File

@ -19,6 +19,8 @@ export interface EditorCanvasProps {
style?: EditorCanvasStyle;
/** 에디터 UI 표시 여부 (기본값: true) */
showEditor?: boolean;
/** 영역 선택 콜백 (비선택 영역 클릭 시) */
onSelectArea?: (areaId: string) => void;
}
export const EditorCanvas: React.FC<EditorCanvasProps> = ({
@ -34,6 +36,7 @@ export const EditorCanvas: React.FC<EditorCanvasProps> = ({
onStopDragging,
style: customStyle,
showEditor = true,
onSelectArea,
}) => {
const containerRef = useRef<HTMLDivElement>(null);
const [canvasSize, setCanvasSize] = useState({width: 0, height: 0});
@ -97,7 +100,7 @@ export const EditorCanvas: React.FC<EditorCanvasProps> = ({
const handleCanvasDown = useCallback(
(e: React.MouseEvent | React.TouchEvent) => {
// 에디터가 숨겨진 상태면 동작하지 않음
if (!showEditor || !selectedArea || !containerRef.current) return;
if (!showEditor || !containerRef.current) return;
const rect = containerRef.current.getBoundingClientRect();
@ -116,14 +119,28 @@ export const EditorCanvas: React.FC<EditorCanvasProps> = ({
const y = (clientY - rect.top) / rect.height;
const clickPoint = { x, y };
// 사각형 내부를 클릭했는지 확인
if (isPointInPolygon(clickPoint, selectedArea.basePoints)) {
// 선택된 영역 내부를 클릭했는지 확인 (드래그 시작)
if (selectedArea && isPointInPolygon(clickPoint, selectedArea.basePoints)) {
setIsDraggingArea(true);
setDragStartPos(clickPoint);
e.preventDefault();
return;
}
// 비선택 영역 클릭 시 해당 영역 선택
if (onSelectArea) {
// 역순으로 검사 (위에 그려진 영역 우선)
for (let i = areas.length - 1; i >= 0; i--) {
const area = areas[i];
if (area.id !== selectedAreaId && isPointInPolygon(clickPoint, area.basePoints)) {
onSelectArea(area.id);
e.preventDefault();
return;
}
}
}
},
[showEditor, selectedArea, isPointInPolygon]
[showEditor, selectedArea, selectedAreaId, areas, isPointInPolygon, onSelectArea]
);
// 이동 (마우스/터치 공통)