Skip to content

Commit

Permalink
add Edge edit
Browse files Browse the repository at this point in the history
  • Loading branch information
aBgAmeuR committed Sep 13, 2023
1 parent 2605271 commit 7b838ac
Show file tree
Hide file tree
Showing 5 changed files with 201 additions and 49 deletions.
92 changes: 76 additions & 16 deletions components/Editor/Edges/FloatingEdge.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,27 @@
import { EdgeProps, getStraightPath, useStore, getBezierPath, getSmoothStepPath } from 'reactflow';
import { EdgeProps, getStraightPath, useStore, getBezierPath, getSmoothStepPath, BaseEdge, EdgeLabelRenderer } from 'reactflow';
import { useCallback } from 'react';
import { getEdgeParams } from './utils';

function FloatingEdge({ id, source, target, markerEnd, style, data }: EdgeProps) {
function EdgeLabel({ transform, label }: { transform: string; label: string }) {
return (
<div
style={{
position: 'absolute',
background: 'transparent',
padding: 10,
color: '#ff5050',
fontSize: 12,
fontWeight: 700,
transform,
}}
className="nodrag nopan"
>
{label}
</div>
);
}

function FloatingEdge({ id, source, target, markerEnd, markerStart, style, data }: EdgeProps) {
const sourceNode = useStore(useCallback((store) => store.nodeInternals.get(source), [source]));
const targetNode = useStore(useCallback((store) => store.nodeInternals.get(target), [target]));

Expand All @@ -12,9 +31,9 @@ function FloatingEdge({ id, source, target, markerEnd, style, data }: EdgeProps)

const { sx, sy, tx, ty, sourcePos, targetPos } = getEdgeParams(sourceNode, targetNode);

let edgePath = '';
let edgePath = '', labelX = 0, labelY = 0, sourceX = sx, sourceY = sy, targetX = tx, targetY = ty;
if (data?.path === 'bezier') {
[edgePath] = getBezierPath({
[edgePath, labelX, labelY] = getBezierPath({
sourceX: sx,
sourceY: sy,
sourcePosition: sourcePos,
Expand All @@ -23,27 +42,68 @@ function FloatingEdge({ id, source, target, markerEnd, style, data }: EdgeProps)
targetY: ty,
});
} else {
[edgePath] = getStraightPath({
[edgePath, labelX, labelY] = getStraightPath({
sourceX: sx,
sourceY: sy,
targetX: tx,
targetY: ty,
});
}

const getTranslateTarget = (sourceX: number, sourceY: number, targetX: number, targetY: number) => {
if (targetX > sourceX && targetY > sourceY) {
// if ((targetX - sourceX) < 200)
// return "translate(0%, -100%)"
return "translate(-100%, 0%)"
} else if (targetX < sourceX && targetY > sourceY) {
return "translate(0%, -100%)"
} else if (targetX > sourceX && targetY < sourceY) {
return "translate(-100%, 0%)"
} else return ""
};

const getTranslateSource = (sourceX: number, sourceY: number, targetX: number, targetY: number) => {
if (targetX < sourceX && targetY > sourceY) {
return "translate(-100%, 0%)"
} else if (targetX < sourceX && targetY < sourceY) {
if ((sourceY - targetY) < 100)
return "translate(-100%, 0%)"
return "translate(-100%, -100%)"
} else if (targetX > sourceX && targetY < sourceY) {
return "translate(0%, -100%)"
} else return ""
};

return (
<path
id={id}
className="react-flow__edge-path"
d={edgePath}
markerEnd={markerEnd}
style={style}
/>
<>
<BaseEdge id={id} path={edgePath} markerEnd={markerEnd} markerStart={markerStart} style={style} />
<EdgeLabelRenderer>
<div
style={{
position: 'absolute',
transform: `translate(-50%, -50%) translate(${labelX}px,${labelY}px)`,
fontSize: 14,
fontWeight: 700,
}}
className="nodrag nopan"
>
{data.label}
</div>
{data.startLabel && (
<EdgeLabel
transform={`${getTranslateSource(sourceX, sourceY, targetX, targetY)} translate(${sourceX}px,${sourceY}px)`}
label={data.startLabel}
/>
)}
{data.endLabel && (
<EdgeLabel
transform={`${getTranslateTarget(sourceX, sourceY, targetX, targetY)} translate(${targetX}px,${targetY}px)`}
label={data.endLabel}
/>
)}
</EdgeLabelRenderer>
</>
);
}

export default FloatingEdge;
function getStepPath(arg0: { sourceX: number; sourceY: number; targetX: number; targetY: number; }): [string] {
throw new Error('Function not implemented.');
}

26 changes: 16 additions & 10 deletions components/Editor/Editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const Data: initialDataState = {
{
id: 'n1',
type: 'Class',
position: { x: 250, y: 10 },
position: { x: 150, y: 80 },
data: {
name: 'Test',
type: 'Class',
Expand Down Expand Up @@ -83,7 +83,7 @@ const Data: initialDataState = {
{
id: 'n3',
type: 'Class',
position: { x: 750, y: 150 },
position: { x: 500, y: 250 },
data: {
name: 'Test 3',
type: 'interface',
Expand Down Expand Up @@ -111,22 +111,28 @@ const Data: initialDataState = {
source: 'n1',
target: 'n2',
type: 'floating',
markerEnd: { type: MarkerType.Arrow },
markerEnd: { type: MarkerType.Arrow, strokeWidth: 2 },
data: {
type: 'association',
path: 'beziera'
}
label: 'test2',
path: 'beziera',
startLabel: '1',
},
style: { strokeWidth: 2 },
},
{
id: '1to3',
source: 'n1',
target: 'n3',
type: 'floating',
markerEnd: { type: MarkerType.Arrow },
markerStart: { type: MarkerType.ArrowClosed, strokeWidth: 2 },
markerEnd: { type: MarkerType.Arrow, strokeWidth: 2 },
data: {
type: 'association',
path: 'beziera'
}
label: 'test',
path: 'bezier',
startLabel: '2',
endLabel: '1.n',
},
style: { strokeWidth: 2 },
}
]
};
Expand Down
24 changes: 19 additions & 5 deletions components/Editor/Nodes/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import {
import { initialDataState } from "@/types/ClassDiagram"

import Class from "./Class"
import { log } from "console"
import { get } from "http"

export type NodesState = {
nodes: Node[]
Expand All @@ -29,7 +31,7 @@ export type NodesState = {
onConnect: OnConnect
addEdgeMode: boolean
setAddEdgeMode: (state: boolean) => void
editEdge: (id: string, newData: any) => void
editEdge: (id: string, label: string, markerStart: string, markerEnd: string, path: string, startLabel: string, endLabel: string) => void
deleteEdge: (id: string) => void
}

Expand Down Expand Up @@ -137,16 +139,28 @@ export namespace Nodes {
[shiftPressed]
)

const editEdge = useCallback((id: string, newData: any) => {
const editEdge = useCallback((id: string, label: string, markerStart: string, markerEnd: string, path: string, startLabel: string, endLabel: string) => {
const getMarkerType = (marker: string) => {
if (marker === "arrow") return { type: MarkerType.Arrow, strokeWidth: 2 }
else if (marker === "arrowclosed") return { type: MarkerType.ArrowClosed, strokeWidth: 2 }
else return undefined
}

setEdges((edges) => {
return edges.map((edge) => {
if (edge.id === id) {
return {
...edge,
label: label,
markerStart: getMarkerType(markerStart),
markerEnd: getMarkerType(markerEnd),
data: {
...edge.data,
...newData,
},
label: label,
path: path,
startLabel: startLabel,
endLabel: endLabel,
}
}
}
return edge
Expand All @@ -160,7 +174,7 @@ export namespace Nodes {
},
[edges]
)

useEffect(() => {
const resNodes = nodes.map((node) => {
if (node.data.addEdgeMode !== addEdgeMode) {
Expand Down
97 changes: 86 additions & 11 deletions components/Editor/OptionBar/Edge.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,98 @@
import { BackgroundVariant, Edge } from "reactflow"
import { Select } from "@/components/ui/select"
import { Button } from "@/components/ui/button";
import { Select } from "@/components/ui/select";
import { useEffect, useState } from "react";
import { Edge } from "reactflow"

interface Props {
edge: Edge,
editEdge: (id: string, newData: any) => void;
editEdge: (id: string, label: string, markerStart: string, markerEnd: string, path: string, startLabel: string, endLabel: string) => void
deleteEdge: (id: string) => void;
}

export function EdgeOptions({ edge, editEdge, deleteEdge }: Props) {
const [label, setLabel] = useState<string>(edge.data?.label);
const [markerStart, setMarkerStart] = useState<string>(edge.markerStart?.type || "");
const [markerEnd, setMarkerEnd] = useState<string>(edge.markerEnd?.type || "");
const [path, setPath] = useState<string>(edge.data?.path);
const [startLabel, setStartLabel] = useState<string>(edge.data?.startLabel);
const [endLabel, setEndLabel] = useState<string>(edge.data?.endLabel);

useEffect(() => {
setLabel(edge.data?.label);
setMarkerStart(edge.markerStart?.type || "");
setMarkerEnd(edge.markerEnd?.type || "");
setPath(edge.data?.path);
setStartLabel(edge.data?.startLabel);
setEndLabel(edge.data?.endLabel);
}, [edge.data, edge.label, edge.markerEnd, edge.markerEnd?.type, edge.markerStart, edge.markerStart?.type]);

useEffect(() => {
editEdge(edge.id, label, markerStart, markerEnd, path, startLabel, endLabel);
}, [edge.id, editEdge, endLabel, label, markerEnd, markerStart, path, startLabel]);

return (
<div className="flex flex-row items-center justify-between">
<h2>Edge</h2>
<div>
<button
className="box-border flex h-[28px] w-[28px] items-center justify-center rounded bg-neutral-100 p-1 text-xl font-normal text-neutral-800 hover:bg-red-500 dark:bg-neutral-800 dark:text-neutral-400 dark:hover:bg-red-500"
onClick={e => deleteEdge(edge.id)}
><p className="=text-[20px]"><svg fill="#000000" height="12px" width="12px" version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 490 490"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"> <polygon points="456.851,0 245,212.564 33.149,0 0.708,32.337 212.669,245.004 0.708,457.678 33.149,490 245,277.443 456.851,490 489.292,457.678 277.331,245.004 489.292,32.337 "></polygon> </g></svg></p></button>
<div>
<div className="flex flex-row items-center justify-between ">
<h2 className="text-xl font-semibold">Edge</h2>
<div>
<button
className="box-border flex h-[28px] w-[28px] items-center justify-center rounded bg-neutral-100 p-1 text-xl font-normal text-neutral-800 hover:bg-red-500 dark:bg-neutral-800 dark:text-neutral-400 dark:hover:bg-red-500"
onClick={e => deleteEdge(edge.id)}
><p className="=text-[20px]"><svg fill="#000000" height="12px" width="12px" version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 490 490"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"> <polygon points="456.851,0 245,212.564 33.149,0 0.708,32.337 212.669,245.004 0.708,457.678 33.149,490 245,277.443 456.851,490 489.292,457.678 277.331,245.004 489.292,32.337 "></polygon> </g></svg></p></button>
</div>
</div>
<div className="mt-4 flex flex-col gap-1">
<div className="flex flex-row items-center gap-2">
<p>Shape :</p>
<Select variant="outline" size="sm" value={path} onChange={e => setPath(e.target.value)}>
<option value="line">Line</option>
<option value="bezier">Bezier</option>
</Select>
</div>
<div className="flex flex-row items-center gap-2">
<p>Label :</p>
<input
className="w-[150px] rounded bg-transparent p-1 text-left outline-none duration-100 hover:bg-slate-50 focus:bg-slate-50 focus:outline-none dark:text-white dark:hover:bg-neutral-900 dark:focus:bg-neutral-900"
placeholder="Nom"
value={label}
onChange={(e) => setLabel(e.target.value)}
/>
</div>
<h2 className="mt-2 text-xl font-semibold">Start</h2>
<div className="flex flex-row items-center gap-2">
<p>Arrow :</p>
<Select variant="outline" size="sm" value={markerStart} onChange={e => setMarkerStart(e.target.value)}>
<option value="arrow">Arrow</option>
<option value="arrowclosed">ArrowClosed</option>
<option value="">None</option>
</Select>
</div>
<div className="flex flex-row items-center gap-2">
<p>Label :</p>
<input
className="w-[80px] rounded bg-transparent p-1 text-left outline-none duration-100 hover:bg-slate-50 focus:bg-slate-50 focus:outline-none dark:text-white dark:hover:bg-neutral-900 dark:focus:bg-neutral-900"
placeholder="Nom"
value={startLabel}
onChange={(e) => setStartLabel(e.target.value)}
/>
</div>
<h2 className="mt-2 text-xl font-semibold">Target</h2>
<div className="flex flex-row items-center gap-2">
<p>Arrow :</p>
<Select variant="outline" size="sm" value={markerEnd} onChange={e => setMarkerEnd(e.target.value)}>
<option value="arrow">Arrow</option>
<option value="arrowclosed">ArrowClosed</option>
<option value="">None</option>
</Select>
</div>
<div className="flex flex-row items-center gap-2">
<p>Label :</p>
<input
className="w-[80px] rounded bg-transparent p-1 text-left outline-none duration-100 hover:bg-slate-50 focus:bg-slate-50 focus:outline-none dark:text-white dark:hover:bg-neutral-900 dark:focus:bg-neutral-900"
placeholder="Nom"
value={endLabel}
onChange={(e) => setEndLabel(e.target.value)}
/>
</div>
</div>
</div>
)
Expand Down
11 changes: 4 additions & 7 deletions components/Editor/OptionBar/index.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { BackgroundVariant, Edge, Node, useOnSelectionChange } from "reactflow";
import { BackgroundVariant, Edge, MarkerType, Node, useOnSelectionChange } from "reactflow";
import { ClassOptions } from "./Class";
import { DefaultOptions } from "./Default";
import { EdgeOptions } from "./Edge";
import { useEffect, useState } from "react";

interface OptionBarProps {
editNode: (id: string, newData: any) => void;
editEdge: (id: string, newData: any) => void;
editEdge: (id: string, label: string, markerStart: string, markerEnd: string, path: string, startLabel: string, endLabel: string) => void
setBgVariant: (variant: BackgroundVariant) => void;
deleteEdge: (id: string) => void;
}
Expand All @@ -15,19 +15,16 @@ export function OptionBar({ editNode, setBgVariant, editEdge, deleteEdge }: Opti
const [entitySelected, setEntitySelected] = useState<Node | Edge | null>(null);

useOnSelectionChange({
onChange: (e) => {
onChange: (e) => {
const node = e.nodes[0];
const edge = e.edges[0];
if (entitySelected?.type === "floating" && !node) return;
if (node) setEntitySelected(node);
else if (edge) setEntitySelected(edge);
else setEntitySelected(null);
}
});

useEffect(() => {
console.log(entitySelected);
}, [entitySelected]);

function getOptions() {
if (!entitySelected) return <DefaultOptions setBgVariant={setBgVariant} />;
if (entitySelected.type === "Class")
Expand Down

0 comments on commit 7b838ac

Please sign in to comment.