|
@@ -1692,21 +1692,31 @@ function WorkflowEditorInner() {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- if (showNodePicker) return
|
|
|
|
|
|
|
+ // The node picker is a sidebar, not a modal: the canvas stays usable
|
|
|
|
|
+ // beside it and it deliberately stays open after adding a node. So the
|
|
|
|
|
+ // canvas keys keep working while it is open - the only thing needing
|
|
|
|
|
+ // protection is its search box, and that is what `typing` is for.
|
|
|
|
|
+ if (typing) return
|
|
|
|
|
|
|
|
if (e.key === 'Delete') {
|
|
if (e.key === 'Delete') {
|
|
|
e.preventDefault()
|
|
e.preventDefault()
|
|
|
- // Check for multiple selected nodes first
|
|
|
|
|
- const selectedNodes = nodes.filter(n => n.selected)
|
|
|
|
|
- if (selectedNodes.length > 1) {
|
|
|
|
|
- // Delete all selected nodes without confirmation for multi-select
|
|
|
|
|
- const selectedIds = new Set(selectedNodes.map(n => n.id))
|
|
|
|
|
- setNodes((nds) => nds.filter((n) => !selectedIds.has(n.id)))
|
|
|
|
|
- setEdges((eds) => eds.filter((e) => !selectedIds.has(e.source) && !selectedIds.has(e.target)))
|
|
|
|
|
|
|
+
|
|
|
|
|
+ // One node can be "the node you mean" in two ways: clicked, which sets
|
|
|
|
|
+ // the focus, or rubber-banded and shift-clicked, which only sets
|
|
|
|
|
+ // `selected`. Reading just one of them made Delete do nothing at all
|
|
|
|
|
+ // for the other, with no hint as to why.
|
|
|
|
|
+ const targetIds = new Set(nodes.filter((n) => n.selected).map((n) => n.id))
|
|
|
|
|
+ if (focusedNodeId) targetIds.add(focusedNodeId)
|
|
|
|
|
+
|
|
|
|
|
+ if (targetIds.size > 1) {
|
|
|
|
|
+ // Several at once goes through without a confirmation: it was a
|
|
|
|
|
+ // deliberate selection, and undo puts it back.
|
|
|
|
|
+ setNodes((nds) => nds.filter((n) => !targetIds.has(n.id)))
|
|
|
|
|
+ setEdges((eds) => eds.filter((e) => !targetIds.has(e.source) && !targetIds.has(e.target)))
|
|
|
setHasChanges(true)
|
|
setHasChanges(true)
|
|
|
setFocusedNodeId(null)
|
|
setFocusedNodeId(null)
|
|
|
- } else if (focusedNodeId) {
|
|
|
|
|
- setShowDeleteConfirm(focusedNodeId)
|
|
|
|
|
|
|
+ } else if (targetIds.size === 1) {
|
|
|
|
|
+ setShowDeleteConfirm([...targetIds][0])
|
|
|
} else if (selectedEdgeId) {
|
|
} else if (selectedEdgeId) {
|
|
|
deleteEdge(selectedEdgeId)
|
|
deleteEdge(selectedEdgeId)
|
|
|
}
|
|
}
|
|
@@ -1714,6 +1724,13 @@ function WorkflowEditorInner() {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
if (e.key === 'Escape') {
|
|
if (e.key === 'Escape') {
|
|
|
|
|
+ // Closing the picker comes first, otherwise the one key everybody
|
|
|
|
|
+ // presses to dismiss a panel does nothing to it.
|
|
|
|
|
+ if (showNodePicker) {
|
|
|
|
|
+ setShowNodePicker(false)
|
|
|
|
|
+ setConnectDrop(null)
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
setFocusedNodeId(null)
|
|
setFocusedNodeId(null)
|
|
|
setSelectedEdgeId(null)
|
|
setSelectedEdgeId(null)
|
|
|
setContextMenu(null)
|
|
setContextMenu(null)
|