Enhance ProcessFlowCanvas with Selection Features and Help Guide
- Added support for node selection changes and multiple node selection events, improving user interaction within the process flow. - Introduced a collapsible help guide to assist users with selection and action instructions, enhancing usability. - Updated styles for selection boxes and multi-selected nodes to improve visual feedback during interactions. - Refactored event handling for node and edge clicks to streamline the selection process and improve error logging.
This commit is contained in:
parent
5261bf601e
commit
80038e00a3
@ -89,6 +89,8 @@ const emit = defineEmits([
|
||||
"edgesChange",
|
||||
"nodeSelected",
|
||||
"edgeSelected",
|
||||
"selectionChange",
|
||||
"nodesSelection",
|
||||
]);
|
||||
|
||||
// Get the flow instance
|
||||
@ -121,8 +123,6 @@ const {
|
||||
markerEnd: "url(#arrow)",
|
||||
},
|
||||
deleteKeyCode: "Delete",
|
||||
selectionKeyCode: "Shift",
|
||||
multiSelectionKeyCode: "Control",
|
||||
connectionMode: "strict",
|
||||
edgeUpdaterRadius: 12,
|
||||
edgesUpdatable: true,
|
||||
@ -130,6 +130,10 @@ const {
|
||||
isValidConnection: (connection) => {
|
||||
return true;
|
||||
},
|
||||
selectNodesOnDrag: true,
|
||||
panOnDrag: [1, 2],
|
||||
panOnScroll: false,
|
||||
zoomOnScroll: true,
|
||||
});
|
||||
|
||||
// Define custom edge types - use markRaw to prevent reactivity issues
|
||||
@ -158,18 +162,32 @@ const flowOptions = ref({
|
||||
nodesDraggable: true,
|
||||
nodesConnectable: true,
|
||||
elementsSelectable: true,
|
||||
selectNodesOnDrag: false,
|
||||
panOnDrag: [0, 2],
|
||||
selectNodesOnDrag: true,
|
||||
panOnDrag: [1, 2],
|
||||
panOnScroll: false,
|
||||
zoomOnScroll: true,
|
||||
zoomOnPinch: true,
|
||||
zoomOnDoubleClick: false,
|
||||
connectOnClick: false,
|
||||
selectionMode: "partial",
|
||||
selectionKeyCode: true,
|
||||
multiSelectionKeyCode: true,
|
||||
selectionOnDrag: true,
|
||||
selectionBoxMode: "Default",
|
||||
preventScrolling: true,
|
||||
});
|
||||
|
||||
// Use shallowRef for selected node to avoid unnecessary reactivity
|
||||
const selectedNode = shallowRef(null);
|
||||
|
||||
// Help guide state
|
||||
const showHelpGuide = ref(false);
|
||||
|
||||
// Toggle help guide
|
||||
const toggleHelpGuide = () => {
|
||||
showHelpGuide.value = !showHelpGuide.value;
|
||||
};
|
||||
|
||||
// State management for preventing recursive updates
|
||||
const isUpdatingNodes = ref(false);
|
||||
const isUpdatingEdges = ref(false);
|
||||
@ -201,10 +219,23 @@ const onNodeClick = ({ node }) => {
|
||||
selectedNode.value = nodeData;
|
||||
emit("nodeSelected", nodeData);
|
||||
} catch (error) {
|
||||
console.error("Error processing node data:", error);
|
||||
console.error("Error processing node click:", error);
|
||||
}
|
||||
};
|
||||
|
||||
// Handle selection change
|
||||
const onSelectionChange = ({ nodes, edges }) => {
|
||||
console.log('Selection changed:', { nodes: nodes.length, edges: edges.length });
|
||||
// You can add custom logic here for handling multiple selections
|
||||
};
|
||||
|
||||
// Handle multiple node selection
|
||||
const onNodesSelection = ({ nodes }) => {
|
||||
console.log('Multiple nodes selected:', nodes.map(n => n.id));
|
||||
// Emit event for multiple node selection
|
||||
emit("nodeSelected", nodes);
|
||||
};
|
||||
|
||||
// Handle edge click
|
||||
const onEdgeClick = (event, edge) => {
|
||||
// Handle different parameter formats Vue Flow might send
|
||||
@ -1424,6 +1455,8 @@ function fromObject(flowObject) {
|
||||
@nodeDoubleClick="onNodeDelete"
|
||||
@edgeDoubleClick="onEdgeDelete"
|
||||
@keyup.delete="onDeleteKeyPress"
|
||||
@selectionChange="onSelectionChange"
|
||||
@nodesSelection="onNodesSelection"
|
||||
>
|
||||
<!-- Global SVG definitions for arrows -->
|
||||
<svg style="position: absolute; top: 0; left: 0; width: 0; height: 0">
|
||||
@ -1476,18 +1509,49 @@ function fromObject(flowObject) {
|
||||
<template #edge-label="{ label }">
|
||||
<div class="edge-label">{{ label }}</div>
|
||||
</template>
|
||||
<Panel position="top-right" class="node-controls">
|
||||
<div class="p-2 bg-white rounded shadow-sm text-sm">
|
||||
<div class="mb-1 font-medium">Controls:</div>
|
||||
<div>• Delete: Remove selected</div>
|
||||
<div>• Shift: Select nodes</div>
|
||||
<div>• Drag between nodes to connect</div>
|
||||
<div>• Double-click to remove</div>
|
||||
<div class="mt-2 mb-1 font-medium">Edge Features:</div>
|
||||
<div>• Arrows show flow direction</div>
|
||||
<div>• Select edge for controls</div>
|
||||
<div>• Blue dot = drag to reposition</div>
|
||||
<div>• Reset button restores path</div>
|
||||
|
||||
<!-- Collapsible Help Guide -->
|
||||
<Panel position="top-right" class="help-guide">
|
||||
<div class="help-guide-content">
|
||||
<button
|
||||
@click="toggleHelpGuide"
|
||||
class="help-toggle-btn"
|
||||
:class="{ 'expanded': showHelpGuide }"
|
||||
>
|
||||
<Icon name="material-symbols:help-outline" class="w-4 h-4" />
|
||||
<span v-if="showHelpGuide">Hide Help</span>
|
||||
<span v-else>Show Help</span>
|
||||
</button>
|
||||
|
||||
<div v-if="showHelpGuide" class="help-content">
|
||||
<div class="help-section">
|
||||
<h4 class="help-title">Selection</h4>
|
||||
<ul class="help-list">
|
||||
<li>• Click: Select single node</li>
|
||||
<li>• Drag: Select multiple nodes</li>
|
||||
<li>• Ctrl+Click: Add to selection</li>
|
||||
<li>• Drag selected nodes together</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="help-section">
|
||||
<h4 class="help-title">Actions</h4>
|
||||
<ul class="help-list">
|
||||
<li>• Delete: Remove selected</li>
|
||||
<li>• Double-click: Remove node/edge</li>
|
||||
<li>• Drag between nodes: Connect</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="help-section">
|
||||
<h4 class="help-title">Edges</h4>
|
||||
<ul class="help-list">
|
||||
<li>• Select edge for controls</li>
|
||||
<li>• Blue dot: Reposition</li>
|
||||
<li>• Reset: Restore path</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Panel>
|
||||
</VueFlow>
|
||||
@ -1691,12 +1755,7 @@ function fromObject(flowObject) {
|
||||
}
|
||||
|
||||
.node-controls {
|
||||
margin: 10px;
|
||||
color: #666;
|
||||
font-size: 12px;
|
||||
background: white;
|
||||
border-radius: 4px;
|
||||
pointer-events: all;
|
||||
display: none;
|
||||
}
|
||||
|
||||
:deep(.vue-flow__handle) {
|
||||
@ -1758,4 +1817,126 @@ function fromObject(flowObject) {
|
||||
0 0 30px rgba(96, 165, 250, 0.6);
|
||||
}
|
||||
}
|
||||
|
||||
/* Selection box styles */
|
||||
:deep(.vue-flow__selection) {
|
||||
background: rgba(59, 130, 246, 0.1);
|
||||
border: 2px solid #3b82f6;
|
||||
border-radius: 4px;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
:deep(.vue-flow__selection-rect) {
|
||||
fill: rgba(59, 130, 246, 0.1);
|
||||
stroke: #3b82f6;
|
||||
stroke-width: 2;
|
||||
stroke-dasharray: 5, 5;
|
||||
}
|
||||
|
||||
/* Multi-selected nodes styling */
|
||||
:deep(.vue-flow__node.selected) {
|
||||
box-shadow: 0 0 0 2px #3b82f6;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
:deep(.vue-flow__node.selected .custom-node) {
|
||||
box-shadow: 0 0 0 2px #3b82f6, 0 2px 5px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
/* Selection box animation */
|
||||
@keyframes selectionPulse {
|
||||
0%, 100% {
|
||||
opacity: 0.3;
|
||||
}
|
||||
50% {
|
||||
opacity: 0.6;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.vue-flow__selection) {
|
||||
animation: selectionPulse 2s ease-in-out infinite;
|
||||
}
|
||||
|
||||
/* Help Guide Styles */
|
||||
.help-guide {
|
||||
pointer-events: all;
|
||||
}
|
||||
|
||||
.help-guide-content {
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||
overflow: hidden;
|
||||
max-width: 280px;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.help-toggle-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 8px 12px;
|
||||
background: #3b82f6;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.help-toggle-btn:hover {
|
||||
background: #2563eb;
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.help-toggle-btn.expanded {
|
||||
background: #dc2626;
|
||||
}
|
||||
|
||||
.help-toggle-btn.expanded:hover {
|
||||
background: #b91c1c;
|
||||
}
|
||||
|
||||
.help-content {
|
||||
padding: 12px;
|
||||
background: white;
|
||||
border-top: 1px solid #e5e7eb;
|
||||
}
|
||||
|
||||
.help-section {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.help-section:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.help-title {
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
color: #374151;
|
||||
margin-bottom: 6px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.help-list {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.help-list li {
|
||||
font-size: 11px;
|
||||
color: #6b7280;
|
||||
margin-bottom: 3px;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.help-list li:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
</style>
|
||||
|
Loading…
x
Reference in New Issue
Block a user