refactor context menu II
This commit is contained in:
@@ -1,14 +1,13 @@
|
||||
import { createContext, useContext, useEffect, useRef, useState, type MouseEventHandler } from "react";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import Viz from 'viz.js';
|
||||
import { Module, render } from 'viz.js/full.render.js';
|
||||
import * as d3 from 'd3';
|
||||
import { graphToDot } from "../Graphviz";
|
||||
import { Dropdown, type MenuProps } from "antd";
|
||||
import { Modal } from "antd";
|
||||
import { Input } from "antd";
|
||||
import { type MenuProps } from "antd";
|
||||
import type { BreadcrumbItemType } from "antd/es/breadcrumb/Breadcrumb";
|
||||
import { cloneDeep } from "lodash";
|
||||
import NodeContextMenu from "./NodeContextMenu";
|
||||
import NodeRenameModal from "./NodeRenameModal";
|
||||
|
||||
export class GraphModel {
|
||||
nodes: NodeModel[] = [];
|
||||
@@ -39,6 +38,19 @@ export interface GraphLevel extends BreadcrumbItemType {
|
||||
|
||||
}
|
||||
|
||||
export interface NodeContext {
|
||||
nodeId: string;
|
||||
nodeName?: string;
|
||||
coords: { x: number, y: number };
|
||||
graph: GraphModel;
|
||||
setGraph: React.Dispatch<React.SetStateAction<GraphModel>>
|
||||
}
|
||||
|
||||
export interface OpenNodeContext {
|
||||
opened: boolean;
|
||||
nodeContext: NodeContext | undefined;
|
||||
}
|
||||
|
||||
const viz = new Viz({ Module, render });
|
||||
|
||||
const items: MenuProps['items'] = [
|
||||
@@ -71,6 +83,8 @@ export default function Graph({ setGraphPath }) {
|
||||
const [nodeName, setSelectedNodeName] = useState('');
|
||||
const [graphId, selectGraphId] = useState('main');
|
||||
const [graphsPath, setGraphsPath] = useState([createPathSegment('main', 'Main')])
|
||||
const [nodeContext, openNodeContext] = useState<null | NodeContext>(null)
|
||||
const [nodeContextMenuOpened, openNodeContextMenu] = useState<OpenNodeContext>({ nodeContext: undefined, opened: false })
|
||||
|
||||
useEffect(() => {
|
||||
console.info(graphsPath);
|
||||
@@ -82,6 +96,12 @@ export default function Graph({ setGraphPath }) {
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [graph]);
|
||||
|
||||
useEffect(() => {
|
||||
if (nodeContext) {
|
||||
openContextMenu(true);
|
||||
}
|
||||
}, [nodeContext]);
|
||||
|
||||
async function renderGraph() {
|
||||
const dot = graphToDot(graph);
|
||||
|
||||
@@ -108,25 +128,24 @@ export default function Graph({ setGraphPath }) {
|
||||
})
|
||||
.on('contextmenu', function (event) {
|
||||
const id = d3.select(this).attr('id');
|
||||
const name = d3.select(this).attr('label');
|
||||
handleOpenContextMenuClick(event, id, name);
|
||||
const node = graph.nodes.find(n => n.id === id);
|
||||
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
console.info(node.label);
|
||||
openNodeContext({
|
||||
nodeId: id,
|
||||
nodeName: node.label,
|
||||
coords: { x: event.clientX, y: event.clientY },
|
||||
setGraph: setGraph,
|
||||
graph: graph
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
function contextMenuOpenChange(open: boolean) {
|
||||
if (!open) {
|
||||
openContextMenu(false)
|
||||
}
|
||||
}
|
||||
|
||||
function handleOpenContextMenuClick(event: React.MouseEvent<HTMLDivElement, MouseEvent>, id: string, name: string) {
|
||||
event.preventDefault();
|
||||
selectNodeId(id);
|
||||
setSelectedNodeName(name)
|
||||
setCoords({ x: event.clientX, y: event.clientY });
|
||||
openContextMenu(true);
|
||||
}
|
||||
|
||||
function createChildNode(parentId: string) {
|
||||
const id = crypto.randomUUID();
|
||||
setGraph(prev => ({ ...prev, nodes: [...prev.nodes, { id, label: 'New node' }], edges: [...prev.edges, { from: parentId, to: id }] }));
|
||||
@@ -225,26 +244,17 @@ export default function Graph({ setGraphPath }) {
|
||||
<div ref={containerRef} className="w-full h-full bg-white rounded shadow" style={{ minHeight: '600px', overflow: 'auto' }}>
|
||||
|
||||
</div>
|
||||
<NodeContextMenu coords={coords} openContextMenu={openContextMenu} contextMenuOpened={contextMenuOpened}>
|
||||
|
||||
<NodeContextMenu
|
||||
nodeContext={nodeContext!}
|
||||
contextMenuOpened={contextMenuOpened}
|
||||
openContextMenu={openContextMenu}
|
||||
openRenameModal={openRenameModal}>
|
||||
</NodeContextMenu>
|
||||
{/* <Dropdown menu={{ items, onClick: onMenuClick }} trigger={['contextMenu']} open={contextMenuOpened} onOpenChange={contextMenuOpenChange} getPopupContainer={() => document.body}
|
||||
// 👇 Key part: manually position the dropdown
|
||||
overlayStyle={{
|
||||
position: "absolute",
|
||||
left: coords.x,
|
||||
top: coords.y,
|
||||
}}>
|
||||
|
||||
</Dropdown> */}
|
||||
<Modal
|
||||
title="Rename"
|
||||
open={renameModalOpened}
|
||||
onOk={() => renameNode()}
|
||||
onCancel={() => openRenameModal(false)}
|
||||
>
|
||||
<Input value={nodeName} onChange={(e) => setSelectedNodeName(e.target.value)} />
|
||||
</Modal>
|
||||
<NodeRenameModal
|
||||
nodeContext={nodeContext!}
|
||||
renameModalOpened={renameModalOpened}
|
||||
openRenameModal={openRenameModal}>
|
||||
</NodeRenameModal>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user