Skip to content

Commit e252157

Browse files
shift output and source buttons to node context menu
1 parent 3fa91b7 commit e252157

File tree

4 files changed

+106
-18
lines changed

4 files changed

+106
-18
lines changed

js/ContextMenu.css

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
@import url('./widget.css');
2+
3+
html,
4+
body {
5+
margin: 0;
6+
font-family: sans-serif;
7+
box-sizing: border-box;
8+
}
9+
10+
#app {
11+
width: 100vw;
12+
height: 100vh;
13+
}
14+
15+
.context-menu {
16+
background: white;
17+
border-style: solid;
18+
box-shadow: 10px 19px 20px rgba(0, 0, 0, 10%);
19+
position: absolute;
20+
z-index: 10;
21+
}
22+
23+
.context-menu button {
24+
border: none;
25+
display: block;
26+
padding: 0.5em;
27+
text-align: left;
28+
width: 100%;
29+
}
30+
31+
.context-menu button:hover {
32+
background: white;
33+
}

js/ContextMenu.jsx

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import React, { useCallback } from 'react';
2+
import { useReactFlow } from '@xyflow/react';
3+
4+
export default function ContextMenu({
5+
id,
6+
data,
7+
top,
8+
left,
9+
right,
10+
bottom,
11+
onOutput,
12+
onSource,
13+
...props
14+
}) {
15+
16+
return (
17+
<div
18+
style={{ position: 'absolute', top: top, left: left, zIndex: 1000 }}
19+
className="context-menu"
20+
{...props}
21+
>
22+
<p style={{ margin: '0.5em' }}>
23+
<b>node: {id}</b>
24+
</p>
25+
<button onClick={() => onOutput(data)} title="View the output(s) of this node without running it"> View Output</button>
26+
<button onClick={() => onSource(data)} title="View the source code of this node">View Source</button>
27+
</div>
28+
);
29+
}

js/CustomNode.jsx

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -46,19 +46,7 @@ export default memo(({ data, node_status }) => {
4646
model.save_changes();
4747
}
4848

49-
const outputFunction = () => {
50-
// direct output of node to output widget
51-
console.log('output: ', data.label)
52-
model.set("commands", `output: ${data.label}`);
53-
model.save_changes();
54-
}
55-
56-
const sourceFunction = () => {
57-
// show source code of node
58-
console.log('source: ', data.label)
59-
model.set("commands", `source: ${data.label}`);
60-
model.save_changes();
61-
}
49+
// outputFunction and sourceFunction lifted to widget.jsx to be used by ContextMenu.jsx
6250

6351
const resetFunction = () => {
6452
// reset state and cache of node
@@ -273,8 +261,6 @@ export default memo(({ data, node_status }) => {
273261
<button onClick={pullFunction} title="Run all connected upstream nodes and this node">Pull</button>
274262
<button onClick={pushFunction} title="Run this node and all connected downstream nodes">Push</button>
275263
<button onClick={resetFunction} title="Reset this node by clearing its cache">Reset</button>
276-
<button onClick={outputFunction} title="View the current output(s) of this node">Output</button>
277-
<button onClick={sourceFunction} title="Show the source code of this node">Source</button>
278264
</NodeToolbar>
279265
</div>
280266
);

js/widget.jsx

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
useOnSelectionChange,
1313
} from '@xyflow/react';
1414
import '@xyflow/react/dist/style.css';
15+
import { ReactFlowProvider } from '@xyflow/react';
1516

1617

1718
import TextUpdaterNode from './TextUpdaterNode.jsx';
@@ -20,6 +21,8 @@ import {getLayoutedNodes2} from './useElkLayout';
2021

2122
import './text-updater-node.css';
2223
import './widget.css';
24+
import './ContextMenu.css';
25+
import ContextMenu from './ContextMenu';
2326

2427
/**
2528
* Author: Joerg Neugebauer
@@ -79,6 +82,9 @@ const render = createRender(() => {
7982
const selectedNodes = [];
8083
const selectedEdges = [];
8184

85+
const [menu, setMenu] = useState(null);
86+
const ref = useRef(null);
87+
8288
const nodeTypes = {
8389
textUpdater: TextUpdaterNode,
8490
customNode: CustomNode,
@@ -89,6 +95,37 @@ const render = createRender(() => {
8995
setNodes(layoutedNodes);
9096
// setTimeout(() => fitView(), 0);
9197
};
98+
99+
const outputFunction = (data) => {
100+
// direct output of node to output widget
101+
console.log('output: ', data.label)
102+
model.set("commands", `output: ${data.label}`);
103+
model.save_changes();
104+
}
105+
106+
const sourceFunction = (data) => {
107+
// show source code of node
108+
console.log('source: ', data.label)
109+
model.set("commands", `source: ${data.label}`);
110+
model.save_changes();
111+
}
112+
113+
const onNodeContextMenu = useCallback(
114+
(event, node) => {
115+
// Prevent native context menu from showing
116+
event.preventDefault();
117+
118+
const wrapperRect = reactFlowWrapper.current.getBoundingClientRect();
119+
setMenu({
120+
id: node.id,
121+
top: event.clientY - wrapperRect.top, // relative to wrapper top
122+
left: event.clientX - wrapperRect.left, // relative to wrapper left
123+
data: node.data
124+
});
125+
},
126+
);
127+
128+
const onPaneClick = useCallback(() => setMenu(null), [setMenu]);
92129

93130
useEffect(() => {
94131
layoutNodes();
@@ -381,7 +418,8 @@ const render = createRender(() => {
381418
}, [model, reactFlowWrapper]);
382419

383420
return (
384-
<div style={{ position: "relative", height: "100%", width: "100%" }}>
421+
<ReactFlowProvider>
422+
<div ref={reactFlowWrapper} style={{ position: "relative", height: "100%", width: "100%" }}>
385423
<UpdateDataContext.Provider value={updateData}>
386424
<ReactFlow
387425
nodes={nodes}
@@ -393,10 +431,10 @@ const render = createRender(() => {
393431
onNodesDelete={onNodesDelete}
394432
onMoveEnd={onMoveEnd}
395433
nodeTypes={nodeTypes}
434+
onPaneClick={onPaneClick}
435+
onNodeContextMenu={onNodeContextMenu}
396436
fitView
397437
style={rfStyle}
398-
// makes sure that reactFlowWrapper wrapper contains a ref to this element
399-
ref={reactFlowWrapper}
400438
/*debugMode={true}*/
401439
>
402440
{/*
@@ -461,8 +499,10 @@ const render = createRender(() => {
461499
Reset Layout
462500
</button>
463501
</ReactFlow>
502+
{menu && <ContextMenu onOutput={outputFunction} onSource={sourceFunction} onClick={onPaneClick} {...menu} />}
464503
</UpdateDataContext.Provider>
465504
</div>
505+
</ReactFlowProvider>
466506
);
467507
});
468508

0 commit comments

Comments
 (0)