Skip to content

Commit

Permalink
feat: added custom handle
Browse files Browse the repository at this point in the history
  • Loading branch information
sumbatx15 committed May 13, 2023
1 parent 89f0ec7 commit 98fa052
Show file tree
Hide file tree
Showing 3 changed files with 129 additions and 120 deletions.
1 change: 0 additions & 1 deletion src/App.css
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
left: 50%;
top: 50%;
touch-action: none;
background: white;
border-radius: 50%;
}
.handle.left {
Expand Down
244 changes: 126 additions & 118 deletions src/components/Diagram/handle.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,138 +12,146 @@ interface HandleProps extends React.HTMLAttributes<HTMLDivElement> {
id: string;
type: "target" | "source";
placement: Placement;
children?: React.ReactNode;
ghostSize?: number;
}

export const Handle: FC<HandleProps> = memo(({ id, type, placement }) => {
const ref = useRef<HTMLDivElement>(null);
const { diagramId } = useDiagramContext();
const { nodeId } = useNodeContext();
const useDiagram = useGetDiagramStore();
export const Handle: FC<HandleProps> = memo(
({ id, type, placement, children, ghostSize, ...props }) => {
const ref = useRef<HTMLDivElement>(null);
const { diagramId } = useDiagramContext();
const { nodeId } = useNodeContext();
const useDiagram = useGetDiagramStore();

useLayoutEffect(() => {
useDiagram.getState().setHandlePlacement(nodeId, id, placement);
}, [placement]);
useLayoutEffect(() => {
useDiagram.getState().setHandlePlacement(nodeId, id, placement);
}, [placement]);

useLayoutEffect(() => {
ref.current && resizeObserver.observe(ref.current);
return () => {
ref.current && resizeObserver.unobserve(ref.current);
useDiagram.getState().clearHandleDimensions(nodeId, id);
};
}, []);
useLayoutEffect(() => {
ref.current && resizeObserver.observe(ref.current);
return () => {
ref.current && resizeObserver.unobserve(ref.current);
useDiagram.getState().clearHandleDimensions(nodeId, id);
};
}, []);

useGesture(
{
onDrag: ({ xy, event, first, canceled, pinching, tap }) => {
if (canceled || pinching) return;
const state = useDiagram.getState();
if (first) {
const handleCenter = state.getHandleCenter(nodeId, id);
useGesture(
{
onDrag: ({ xy, event, first, canceled, pinching, tap }) => {
if (canceled || pinching) return;
const state = useDiagram.getState();
if (first) {
const handleCenter = state.getHandleCenter(nodeId, id);

state.updateDraggedEdgePosition({
start: handleCenter,
end: handleCenter,
});
state.updateDraggedEdgePosition({
start: handleCenter,
end: handleCenter,
});

state.setDraggedEdgeVisible(true);
state.setDraggedEdge({
handleId: id,
nodeId,
handleType: type,
});
}
state.setDraggedEdgeVisible(true);
state.setDraggedEdge({
handleId: id,
nodeId,
handleType: type,
});
}

console.log("state.viewport:", state.viewport);
state.updateDraggedEdgePosition({
end: getInDiagramPosition({ x: xy[0], y: xy[1] }, state.viewport),
});
state.updateDraggedEdgePosition({
end: getInDiagramPosition({ x: xy[0], y: xy[1] }, state.viewport),
});

const element = document.elementFromPoint(...xy) as HTMLElement | null;
if (
element &&
element.dataset.elementType === "handle" &&
element.dataset.type !== type &&
element.dataset.id !== id
) {
const handleCenter = state.getHandleCenter(
element.dataset.nodeId as string,
element.dataset.id as string
);
const element = document.elementFromPoint(
...xy
) as HTMLElement | null;
if (
element &&
element.dataset.elementType === "handle" &&
element.dataset.type !== type &&
element.dataset.id !== id
) {
const handleCenter = state.getHandleCenter(
element.dataset.nodeId as string,
element.dataset.id as string
);

useDiagram.getState().updateDraggedEdgePosition({
end: handleCenter,
});
}
},
onDragEnd: ({ xy }) => {
const element = document.elementFromPoint(...xy);
useDiagram.getState().updateDraggedEdgePosition({
end: handleCenter,
});
}
},
onDragEnd: ({ xy }) => {
const element = document.elementFromPoint(...xy);

const [from, to] = [ref.current, element] as HTMLElement[];
if (
from &&
to &&
to.dataset.elementType === "handle" &&
from.dataset.nodeId !== to.dataset.nodeId &&
from.dataset.id !== to.dataset.id &&
to.dataset.type !== type
) {
const [source, target] = type === "source" ? [from, to] : [to, from];
const onConnection = useDiagram.getState().onConnection;
onConnection
? onConnection({
source: source.dataset.nodeId as string,
target: target.dataset.nodeId as string,
sourceHandle: source.dataset.id as string,
targetHandle: target.dataset.id as string,
})
: useDiagram.getState().addEdge(
createEdge({
const [from, to] = [ref.current, element] as HTMLElement[];
if (
from &&
to &&
to.dataset.elementType === "handle" &&
from.dataset.nodeId !== to.dataset.nodeId &&
from.dataset.id !== to.dataset.id &&
to.dataset.type !== type
) {
const [source, target] =
type === "source" ? [from, to] : [to, from];
const onConnection = useDiagram.getState().onConnection;
onConnection
? onConnection({
source: source.dataset.nodeId as string,
target: target.dataset.nodeId as string,
sourceHandle: source.dataset.id as string,
targetHandle: target.dataset.id as string,
data: "",
animated: true,
})
);
}
useDiagram.getState().clearDraggedEdge();
},
},
{
target: ref,
eventOptions: {
passive: false,
: useDiagram.getState().addEdge(
createEdge({
source: source.dataset.nodeId as string,
target: target.dataset.nodeId as string,
sourceHandle: source.dataset.id as string,
targetHandle: target.dataset.id as string,
data: "",
animated: true,
})
);
}
useDiagram.getState().clearDraggedEdge();
},
},
}
);
const draggedEdge = useDraggedEdge();
if (draggedEdge?.handleType === type && draggedEdge?.nodeId !== nodeId)
return null;
return (
<div
ref={ref}
className={`handle circle ${placement} `}
data-diagram-id={diagramId}
data-element-type="handle"
data-id={id}
data-node-id={nodeId}
data-type={type}
>
{/* {draggedEdge && (
<Circle
data-diagram-id={diagramId}
data-element-type="handle"
data-id={id}
data-node-id={nodeId}
data-type={type}
opacity={0}
className="handle-ghost"
position="absolute"
size="42px"
border="1px dashed gray"
/>
)} */}
</div>
);
});
{
target: ref,
eventOptions: {
passive: false,
},
}
);
const draggedEdge = useDraggedEdge();
if (draggedEdge?.handleType === type && draggedEdge?.nodeId !== nodeId)
return null;
return (
<div
ref={ref}
className={`handle ${children ? "" : "circle"} ${placement} `}
data-diagram-id={diagramId}
data-element-type="handle"
data-id={id}
data-node-id={nodeId}
data-type={type}
{...props}
>
<div style={{ pointerEvents: "none" }}>{children}</div>
{draggedEdge && (
<div
data-diagram-id={diagramId}
data-element-type="handle"
data-id={id}
data-node-id={nodeId}
data-type={type}
style={{
width: ghostSize,
height: ghostSize,
}}
className="handle handle-ghost"
/>
)}
</div>
);
}
);
4 changes: 3 additions & 1 deletion src/components/Node/DefaultNode.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ export const DefaultNode: NodeFC = memo(({ id }) => {
<p /* onClick={toggle} */>
{(data as Partial<{ label: string }>)?.label || `Default: ${id}`}
</p>
<Handle id="in" type="target" placement="left" />
<Handle id="in" type="target" placement="left">
<div style={{ width: "20px", height: "20px", background: "red" }}></div>
</Handle>
<Handle id="out" type="source" placement="right" />
</div>
);
Expand Down

0 comments on commit 98fa052

Please sign in to comment.