feat: [rd] 기획디자인 연결선 삭제 + 스페이스바 패닝 기능 추가

- 연결선 클릭 선택 → Delete/Backspace로 삭제
- 우클릭 컨텍스트 메뉴에 '연결선 삭제' 항목 추가
- 스페이스바 누른 채 마우스 드래그로 캔버스 이동 (Figma/FigJam 방식)
- 패닝 중 커서 grab/grabbing 변경
This commit is contained in:
김보곤
2026-03-07 22:28:40 +09:00
parent b0a70481e8
commit 7b9ce99541

View File

@@ -504,7 +504,7 @@
} }
</style> </style>
<div class="pc-wrap" id="planningCanvas" x-data="planningCanvas()" x-init="init()" @keydown.window="handleKeyDown($event)"> <div class="pc-wrap" id="planningCanvas" x-data="planningCanvas()" x-init="init()" @keydown.window="handleKeyDown($event)" @keyup.window="handleKeyUp($event)">
{{-- ===== Top Toolbar ===== --}} {{-- ===== Top Toolbar ===== --}}
<div class="pc-toolbar"> <div class="pc-toolbar">
@@ -1054,7 +1054,8 @@
<div class="pc-cm-sep"></div> <div class="pc-cm-sep"></div>
<div class="pc-cm-item" @click="addNodeAtMouse(paletteItems.planning[0]); hideContextMenu();">빠른 노드 추가</div> <div class="pc-cm-item" @click="addNodeAtMouse(paletteItems.planning[0]); hideContextMenu();">빠른 노드 추가</div>
<div class="pc-cm-sep"></div> <div class="pc-cm-sep"></div>
<div class="pc-cm-item danger" @click="deleteSelectedNode(); hideContextMenu();">삭제</div> <div class="pc-cm-item danger" x-show="selectedConnection" @click="deleteSelectedConnection(); hideContextMenu();">연결선 삭제</div>
<div class="pc-cm-item danger" x-show="selectedNode" @click="deleteSelectedNode(); hideContextMenu();">노드 삭제</div>
</div> </div>
</div> </div>
@@ -1117,6 +1118,7 @@ function planningCanvas() {
panning: false, panning: false,
panStartX: 0, panStartX: 0,
panStartY: 0, panStartY: 0,
spaceHeld: false,
// Connection Drawing // Connection Drawing
drawingConnection: false, drawingConnection: false,
@@ -1509,11 +1511,22 @@ function planningCanvas() {
// ===== Canvas Events ===== // ===== Canvas Events =====
onCanvasMouseDown(e) { onCanvasMouseDown(e) {
if (e.target.closest('.pc-node') || e.target.closest('.pc-port')) return; if (e.target.closest('.pc-node') || e.target.closest('.pc-port')) {
if (this.tool === 'pan' || e.button === 1) { // 스페이스바 누른 채 노드 위에서도 패닝 가능
if (this.spaceHeld) {
this.panning = true; this.panning = true;
this.panStartX = e.clientX - this.panX * this.zoom; this.panStartX = e.clientX - this.panX * this.zoom;
this.panStartY = e.clientY - this.panY * this.zoom; this.panStartY = e.clientY - this.panY * this.zoom;
document.getElementById('canvasWrap')?.style.setProperty('cursor', 'grabbing');
e.preventDefault();
}
return;
}
if (this.tool === 'pan' || this.spaceHeld || e.button === 1) {
this.panning = true;
this.panStartX = e.clientX - this.panX * this.zoom;
this.panStartY = e.clientY - this.panY * this.zoom;
document.getElementById('canvasWrap')?.style.setProperty('cursor', 'grabbing');
e.preventDefault(); e.preventDefault();
} else { } else {
this.selectedNode = null; this.selectedNode = null;
@@ -1551,7 +1564,12 @@ function planningCanvas() {
this.pushHistory(); this.pushHistory();
this.autoSave(); this.autoSave();
} }
if (this.panning) { this.panning = false; } if (this.panning) {
this.panning = false;
const wrap = document.getElementById('canvasWrap');
if (this.spaceHeld) wrap?.style.setProperty('cursor', 'grab');
else wrap?.style.removeProperty('cursor');
}
if (this.drawingConnection) { if (this.drawingConnection) {
// Check if dropped on a node port // Check if dropped on a node port
const target = e.target.closest('.pc-node'); const target = e.target.closest('.pc-node');
@@ -1699,11 +1717,22 @@ function planningCanvas() {
// ===== Keyboard Shortcuts ===== // ===== Keyboard Shortcuts =====
handleKeyDown(e) { handleKeyDown(e) {
if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA' || e.target.isContentEditable) return; if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA' || e.target.isContentEditable) return;
// 스페이스바: 패닝 모드 (누르고 있는 동안)
if (e.key === ' ' || e.code === 'Space') {
e.preventDefault();
if (!this.spaceHeld) {
this.spaceHeld = true;
this._toolBeforeSpace = this.tool;
this.tool = 'pan';
document.getElementById('canvasWrap')?.style.setProperty('cursor', 'grab');
}
return;
}
if (e.key === 'Delete' || e.key === 'Backspace') { if (e.key === 'Delete' || e.key === 'Backspace') {
if (this.selectedConnection) { if (this.selectedConnection) {
this.connections = this.connections.filter(c => c.id !== this.selectedConnection.id); this.deleteSelectedConnection();
this.selectedConnection = null;
this.pushHistory(); this.autoSave();
} else { } else {
this.deleteSelectedNode(); this.deleteSelectedNode();
} }
@@ -1726,6 +1755,26 @@ function planningCanvas() {
} }
}, },
handleKeyUp(e) {
if (e.key === ' ' || e.code === 'Space') {
if (this.spaceHeld) {
this.spaceHeld = false;
this.panning = false;
this.tool = this._toolBeforeSpace || 'select';
document.getElementById('canvasWrap')?.style.removeProperty('cursor');
}
}
},
// ===== Connection Delete =====
deleteSelectedConnection() {
if (!this.selectedConnection) return;
this.connections = this.connections.filter(c => c.id !== this.selectedConnection.id);
this.selectedConnection = null;
this.pushHistory();
this.autoSave();
},
// ===== Context Menu ===== // ===== Context Menu =====
showContextMenu(e) { showContextMenu(e) {
this.contextMenuPos = { x: e.clientX, y: e.clientY }; this.contextMenuPos = { x: e.clientX, y: e.clientY };