mirror of
https://github.com/20kaushik02/spotify-manager-web.git
synced 2025-12-06 08:04:06 +00:00
abt to start link creation/removal
This commit is contained in:
parent
090ba0b085
commit
f471c666e7
@ -11,6 +11,7 @@
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
|
gap: var(--mb-3);
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
width: 10vw;
|
width: 10vw;
|
||||||
padding: var(--mb-3);
|
padding: var(--mb-3);
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import React, { useCallback, useContext, useEffect, useState } from "react";
|
|||||||
import {
|
import {
|
||||||
ReactFlow,
|
ReactFlow,
|
||||||
Controls,
|
Controls,
|
||||||
|
MiniMap,
|
||||||
Background,
|
Background,
|
||||||
addEdge,
|
addEdge,
|
||||||
applyNodeChanges,
|
applyNodeChanges,
|
||||||
@ -18,6 +19,8 @@ import {
|
|||||||
type OnNodesChange,
|
type OnNodesChange,
|
||||||
type OnEdgesChange,
|
type OnEdgesChange,
|
||||||
type OnConnect,
|
type OnConnect,
|
||||||
|
type OnDelete,
|
||||||
|
type OnBeforeDelete,
|
||||||
} from "@xyflow/react";
|
} from "@xyflow/react";
|
||||||
import Dagre, { type GraphLabel } from "@dagrejs/dagre";
|
import Dagre, { type GraphLabel } from "@dagrejs/dagre";
|
||||||
|
|
||||||
@ -26,6 +29,7 @@ import styles from "./Graph.module.css";
|
|||||||
|
|
||||||
import { IoIosGitNetwork } from "react-icons/io";
|
import { IoIosGitNetwork } from "react-icons/io";
|
||||||
import { WiCloudRefresh } from "react-icons/wi";
|
import { WiCloudRefresh } from "react-icons/wi";
|
||||||
|
import { MdOutlineLock, MdOutlineLockOpen } from "react-icons/md";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
showErrorToastNotification,
|
showErrorToastNotification,
|
||||||
@ -37,8 +41,8 @@ import { apiFetchGraph } from "../../api/operations";
|
|||||||
import { RefreshAuthContext } from "../../App";
|
import { RefreshAuthContext } from "../../App";
|
||||||
import Button from "../../components/Button";
|
import Button from "../../components/Button";
|
||||||
|
|
||||||
const initialNodes: any[] = [];
|
const initialNodes: Node[] = [];
|
||||||
const initialEdges: any[] = [];
|
const initialEdges: Edge[] = [];
|
||||||
|
|
||||||
const nodeOffsets = {
|
const nodeOffsets = {
|
||||||
connected: {
|
connected: {
|
||||||
@ -63,6 +67,18 @@ const nodeOffsets = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
interface Interactive {
|
||||||
|
ndDrag: boolean;
|
||||||
|
ndConn: boolean;
|
||||||
|
elsSel: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const initialInteractive: Interactive = {
|
||||||
|
ndDrag: true,
|
||||||
|
ndConn: true,
|
||||||
|
elsSel: true,
|
||||||
|
};
|
||||||
|
|
||||||
const edgeOptions: DefaultEdgeOptions = {
|
const edgeOptions: DefaultEdgeOptions = {
|
||||||
animated: true,
|
animated: true,
|
||||||
style: {
|
style: {
|
||||||
@ -72,8 +88,9 @@ const edgeOptions: DefaultEdgeOptions = {
|
|||||||
markerEnd: {
|
markerEnd: {
|
||||||
type: MarkerType.ArrowClosed,
|
type: MarkerType.ArrowClosed,
|
||||||
color: "white",
|
color: "white",
|
||||||
width: 40,
|
width: 16,
|
||||||
height: 40,
|
height: 16,
|
||||||
|
orient: "auto",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -84,11 +101,31 @@ const Graph = () => {
|
|||||||
const flowInstance = useReactFlow();
|
const flowInstance = useReactFlow();
|
||||||
const [playlistNodes, setPlaylistNodes] = useState<Node[]>(initialNodes);
|
const [playlistNodes, setPlaylistNodes] = useState<Node[]>(initialNodes);
|
||||||
const [linkEdges, setLinkEdges] = useState<Edge[]>(initialEdges);
|
const [linkEdges, setLinkEdges] = useState<Edge[]>(initialEdges);
|
||||||
|
const [interactive, setInteractive] =
|
||||||
|
useState<Interactive>(initialInteractive);
|
||||||
|
|
||||||
const onFlowInit = (_instance: ReactFlowInstance) => {
|
const onFlowInit = (_instance: ReactFlowInstance) => {
|
||||||
console.debug("flow loaded");
|
console.debug("flow loaded");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const onFlowBeforeDelete: OnBeforeDelete = useCallback(
|
||||||
|
async ({ nodes, edges }) => {
|
||||||
|
// can't delete playlists
|
||||||
|
if (nodes.length > 0) {
|
||||||
|
showErrorToastNotification("Can't delete playlists!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return { nodes, edges };
|
||||||
|
},
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
|
||||||
|
const onFlowAfterDelete: OnDelete = useCallback(({ nodes, edges }) => {
|
||||||
|
console.debug("deleted edges");
|
||||||
|
console.debug(edges);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// base event handling
|
||||||
const onNodesChange: OnNodesChange = useCallback(
|
const onNodesChange: OnNodesChange = useCallback(
|
||||||
(changes) => setPlaylistNodes((nds) => applyNodeChanges(changes, nds)),
|
(changes) => setPlaylistNodes((nds) => applyNodeChanges(changes, nds)),
|
||||||
[setPlaylistNodes]
|
[setPlaylistNodes]
|
||||||
@ -121,9 +158,9 @@ const Graph = () => {
|
|||||||
g.setDefaultEdgeLabel(() => ({}));
|
g.setDefaultEdgeLabel(() => ({}));
|
||||||
g.setGraph({
|
g.setGraph({
|
||||||
rankdir: options.direction,
|
rankdir: options.direction,
|
||||||
nodesep: 200,
|
nodesep: 100,
|
||||||
edgesep: 200,
|
edgesep: 100,
|
||||||
ranksep: 200,
|
ranksep: 100,
|
||||||
});
|
});
|
||||||
|
|
||||||
edges.forEach((edge) => g.setEdge(edge.source, edge.target));
|
edges.forEach((edge) => g.setEdge(edge.source, edge.target));
|
||||||
@ -235,7 +272,7 @@ const Graph = () => {
|
|||||||
setLinkEdges(
|
setLinkEdges(
|
||||||
resp.data.links?.map((link, idx) => {
|
resp.data.links?.map((link, idx) => {
|
||||||
return {
|
return {
|
||||||
id: `${idx}`,
|
id: `${link.from}->${link.to}`,
|
||||||
source: link.from,
|
source: link.from,
|
||||||
target: link.to,
|
target: link.to,
|
||||||
};
|
};
|
||||||
@ -270,27 +307,46 @@ const Graph = () => {
|
|||||||
// onRefresh();
|
// onRefresh();
|
||||||
}, [fetchGraph]);
|
}, [fetchGraph]);
|
||||||
|
|
||||||
|
const toggleInteractive = () => {
|
||||||
|
setInteractive({
|
||||||
|
ndDrag: !interactive.ndDrag,
|
||||||
|
ndConn: !interactive.ndConn,
|
||||||
|
elsSel: !interactive.elsSel,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const isInteractive = () => {
|
||||||
|
return interactive.ndDrag && interactive.ndConn && interactive.elsSel;
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.graph_wrapper}>
|
<div className={styles.graph_wrapper}>
|
||||||
<ReactFlow
|
<ReactFlow
|
||||||
nodes={playlistNodes}
|
nodes={playlistNodes}
|
||||||
edges={linkEdges}
|
edges={linkEdges}
|
||||||
defaultEdgeOptions={edgeOptions}
|
defaultEdgeOptions={edgeOptions}
|
||||||
connectionLineType={ConnectionLineType.SmoothStep}
|
|
||||||
fitView
|
|
||||||
proOptions={proOptions}
|
proOptions={proOptions}
|
||||||
|
connectionLineType={ConnectionLineType.SmoothStep}
|
||||||
|
connectOnClick={false}
|
||||||
|
fitView
|
||||||
colorMode={"light"}
|
colorMode={"light"}
|
||||||
|
edgesReconnectable={false}
|
||||||
|
nodesFocusable={false}
|
||||||
|
nodesDraggable={interactive.ndDrag}
|
||||||
|
nodesConnectable={interactive.ndConn}
|
||||||
|
elementsSelectable={interactive.elsSel}
|
||||||
|
deleteKeyCode={["Delete", "Backspace"]}
|
||||||
|
multiSelectionKeyCode={null}
|
||||||
onInit={onFlowInit}
|
onInit={onFlowInit}
|
||||||
|
onBeforeDelete={onFlowBeforeDelete}
|
||||||
|
onDelete={onFlowAfterDelete}
|
||||||
onNodesChange={onNodesChange}
|
onNodesChange={onNodesChange}
|
||||||
onEdgesChange={onEdgesChange}
|
onEdgesChange={onEdgesChange}
|
||||||
onConnect={onConnect}
|
onConnect={onConnect}
|
||||||
>
|
>
|
||||||
<Controls />
|
<Controls onInteractiveChange={toggleInteractive} />
|
||||||
|
<MiniMap pannable zoomable />
|
||||||
<Background variant={BackgroundVariant.Dots} gap={36} size={3} />
|
<Background variant={BackgroundVariant.Dots} gap={36} size={3} />
|
||||||
{/* <Panel position="top-right"> */}
|
|
||||||
{/* <button onClick={() => arrangeLayout('TB')}>Arrange vertically</button> */}
|
|
||||||
{/* <button onClick={() => arrangeLayout('LR')}>Arrange horizontally</button> */}
|
|
||||||
{/* </Panel> */}
|
|
||||||
</ReactFlow>
|
</ReactFlow>
|
||||||
<div className={styles.operations_wrapper}>
|
<div className={styles.operations_wrapper}>
|
||||||
<Button onClickMethod={onRefresh}>
|
<Button onClickMethod={onRefresh}>
|
||||||
@ -301,6 +357,14 @@ const Graph = () => {
|
|||||||
<IoIosGitNetwork size={36} />
|
<IoIosGitNetwork size={36} />
|
||||||
Arrange
|
Arrange
|
||||||
</Button>
|
</Button>
|
||||||
|
<Button onClickMethod={toggleInteractive}>
|
||||||
|
{isInteractive() ? (
|
||||||
|
<MdOutlineLock size={36} />
|
||||||
|
) : (
|
||||||
|
<MdOutlineLockOpen size={36} />
|
||||||
|
)}
|
||||||
|
{isInteractive() ? "Lock" : "Unlock"}
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user