mirror of
https://github.com/20kaushik02/spotify-manager-web.git
synced 2025-12-06 12:44:06 +00:00
styling, edge selection
This commit is contained in:
parent
f54d8ccee6
commit
c9938aca35
@ -4,6 +4,7 @@
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: space-around;
|
||||
gap: var(--mb-1);
|
||||
height: 100vh;
|
||||
width: 10vw;
|
||||
position: sticky;
|
||||
|
||||
@ -9,7 +9,7 @@ const Navbar = () => {
|
||||
const auth = useContext(AuthContext);
|
||||
|
||||
return (
|
||||
<nav className={styles.navbar_wrapper}>
|
||||
<nav className={`${styles.navbar_wrapper} custom_scrollbar`}>
|
||||
<StyledNavLink path="/" text="About" />
|
||||
<StyledNavLink path="/graph" text="Graph" />
|
||||
{auth === true ? (
|
||||
|
||||
@ -1,10 +1,11 @@
|
||||
a {
|
||||
padding: var(--mb-3);
|
||||
padding: var(--mb-3) var(--mb-1);
|
||||
border-radius: 2%;
|
||||
width: 100%;
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
color: var(--text);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.active_link {
|
||||
|
||||
@ -89,3 +89,20 @@ code {
|
||||
font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
|
||||
monospace;
|
||||
}
|
||||
|
||||
/* Custom scrollbar */
|
||||
.custom_scrollbar::-webkit-scrollbar-track {
|
||||
border-radius: 1rem;
|
||||
-webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
|
||||
background-color: var(--text);
|
||||
}
|
||||
|
||||
.custom_scrollbar::-webkit-scrollbar {
|
||||
width: 10px;
|
||||
}
|
||||
|
||||
.custom_scrollbar::-webkit-scrollbar-thumb {
|
||||
border-radius: 1rem;
|
||||
-webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
|
||||
background-color: var(--bg);
|
||||
}
|
||||
|
||||
@ -14,6 +14,11 @@
|
||||
gap: var(--mb-1);
|
||||
height: 100vh;
|
||||
width: 10vw;
|
||||
position: sticky;
|
||||
position: -webkit-sticky;
|
||||
top: 0;
|
||||
right: 0;
|
||||
overflow: auto;
|
||||
padding: var(--mb-3);
|
||||
}
|
||||
|
||||
|
||||
@ -7,10 +7,10 @@ import {
|
||||
addEdge,
|
||||
applyNodeChanges,
|
||||
applyEdgeChanges,
|
||||
useOnSelectionChange,
|
||||
useReactFlow,
|
||||
MarkerType,
|
||||
BackgroundVariant,
|
||||
ConnectionLineType,
|
||||
type DefaultEdgeOptions,
|
||||
type ProOptions,
|
||||
type ReactFlowInstance,
|
||||
@ -18,8 +18,8 @@ import {
|
||||
type Edge,
|
||||
type OnNodesChange,
|
||||
type OnEdgesChange,
|
||||
type OnSelectionChangeFunc,
|
||||
type OnConnect,
|
||||
type OnDelete,
|
||||
type OnBeforeDelete,
|
||||
} from "@xyflow/react";
|
||||
import Dagre from "@dagrejs/dagre";
|
||||
@ -91,7 +91,7 @@ const initialInteractive: Interactive = {
|
||||
};
|
||||
|
||||
const edgeOptions: DefaultEdgeOptions = {
|
||||
animated: true,
|
||||
animated: false,
|
||||
style: {
|
||||
stroke: "white",
|
||||
strokeWidth: 2,
|
||||
@ -105,6 +105,21 @@ const edgeOptions: DefaultEdgeOptions = {
|
||||
},
|
||||
};
|
||||
|
||||
const selectedEdgeOptions: DefaultEdgeOptions = {
|
||||
animated: true,
|
||||
style: {
|
||||
stroke: "red",
|
||||
strokeWidth: 2,
|
||||
},
|
||||
markerEnd: {
|
||||
type: MarkerType.ArrowClosed,
|
||||
color: "red",
|
||||
width: 16,
|
||||
height: 16,
|
||||
orient: "auto",
|
||||
},
|
||||
};
|
||||
|
||||
const proOptions: ProOptions = { hideAttribution: true };
|
||||
|
||||
const Graph = () => {
|
||||
@ -119,39 +134,6 @@ const Graph = () => {
|
||||
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(
|
||||
async ({ nodes, edges }) => {
|
||||
console.debug(
|
||||
`deleted connection: ${edges[0].source} -> ${edges[0].target}`
|
||||
);
|
||||
// call API to delete link
|
||||
const spotifyPlaylistLinkPrefix = "https://open.spotify.com/playlist/";
|
||||
const resp = await APIWrapper({
|
||||
apiFn: apiDeleteLink,
|
||||
data: {
|
||||
from: spotifyPlaylistLinkPrefix + edges[0].source,
|
||||
to: spotifyPlaylistLinkPrefix + edges[0].target,
|
||||
},
|
||||
refreshAuth,
|
||||
});
|
||||
if (resp?.status === 200)
|
||||
showSuccessToastNotification(resp?.data.message);
|
||||
},
|
||||
[refreshAuth]
|
||||
);
|
||||
|
||||
// base event handling
|
||||
const onNodesChange: OnNodesChange = useCallback(
|
||||
(changes) => setPlaylistNodes((nds) => applyNodeChanges(changes, nds)),
|
||||
@ -162,7 +144,25 @@ const Graph = () => {
|
||||
[setLinkEdges]
|
||||
);
|
||||
|
||||
const onConnect: OnConnect = useCallback(
|
||||
const onFlowSelectionChange: OnSelectionChangeFunc = useCallback(
|
||||
({ nodes, edges }) => {
|
||||
const newSelectedID = edges[0]?.id ?? "";
|
||||
setLinkEdges((eds) =>
|
||||
eds.map((ed) =>
|
||||
ed.id === newSelectedID
|
||||
? { ...ed, ...selectedEdgeOptions }
|
||||
: { ...ed, ...edgeOptions }
|
||||
)
|
||||
);
|
||||
},
|
||||
[setLinkEdges]
|
||||
);
|
||||
useOnSelectionChange({
|
||||
onChange: onFlowSelectionChange,
|
||||
});
|
||||
|
||||
// new edge
|
||||
const onFlowConnect: OnConnect = useCallback(
|
||||
async (connection) => {
|
||||
console.debug(
|
||||
`new connection: ${connection.source} -> ${connection.target}`
|
||||
@ -185,6 +185,36 @@ const Graph = () => {
|
||||
[setLinkEdges, refreshAuth]
|
||||
);
|
||||
|
||||
// remove edge
|
||||
const onFlowBeforeDelete: OnBeforeDelete = useCallback(
|
||||
async ({ nodes, edges }) => {
|
||||
// can't delete playlists
|
||||
if (nodes.length > 0) {
|
||||
showErrorToastNotification("Can't delete playlists!");
|
||||
return false;
|
||||
}
|
||||
console.debug(
|
||||
`deleted connection: ${edges[0].source} -> ${edges[0].target}`
|
||||
);
|
||||
// call API to delete link
|
||||
const spotifyPlaylistLinkPrefix = "https://open.spotify.com/playlist/";
|
||||
const resp = await APIWrapper({
|
||||
apiFn: apiDeleteLink,
|
||||
data: {
|
||||
from: spotifyPlaylistLinkPrefix + edges[0].source,
|
||||
to: spotifyPlaylistLinkPrefix + edges[0].target,
|
||||
},
|
||||
refreshAuth,
|
||||
});
|
||||
if (resp?.status === 200) {
|
||||
showSuccessToastNotification(resp?.data.message);
|
||||
return { nodes, edges };
|
||||
}
|
||||
return false;
|
||||
},
|
||||
[refreshAuth]
|
||||
);
|
||||
|
||||
type getLayoutedElementsOpts = {
|
||||
direction: rankdirType;
|
||||
};
|
||||
@ -335,8 +365,6 @@ const Graph = () => {
|
||||
|
||||
useEffect(() => {
|
||||
fetchGraph();
|
||||
// TODO: how to invoke async and sync fns in order correctly inside useEffect?
|
||||
// refreshGraph();
|
||||
}, [fetchGraph]);
|
||||
|
||||
const disableInteractive = () => {
|
||||
@ -369,10 +397,9 @@ const Graph = () => {
|
||||
edges={linkEdges}
|
||||
defaultEdgeOptions={edgeOptions}
|
||||
proOptions={proOptions}
|
||||
connectionLineType={ConnectionLineType.SmoothStep}
|
||||
connectOnClick={false}
|
||||
fitView
|
||||
colorMode={"light"}
|
||||
elevateEdgesOnSelect
|
||||
edgesReconnectable={false}
|
||||
nodesFocusable={false}
|
||||
nodesDraggable={interactive.ndDrag}
|
||||
@ -382,16 +409,15 @@ const Graph = () => {
|
||||
multiSelectionKeyCode={null}
|
||||
onInit={onFlowInit}
|
||||
onBeforeDelete={onFlowBeforeDelete}
|
||||
onDelete={onFlowAfterDelete}
|
||||
onNodesChange={onNodesChange}
|
||||
onEdgesChange={onEdgesChange}
|
||||
onConnect={onConnect}
|
||||
onConnect={onFlowConnect}
|
||||
>
|
||||
<Controls onInteractiveChange={toggleInteractive} />
|
||||
<MiniMap pannable zoomable />
|
||||
<Background variant={BackgroundVariant.Dots} gap={36} size={3} />
|
||||
</ReactFlow>
|
||||
<div className={styles.operations_wrapper}>
|
||||
<div className={`${styles.operations_wrapper} custom_scrollbar`}>
|
||||
<Button onClickMethod={refreshGraph}>
|
||||
<WiCloudRefresh size={36} />
|
||||
Refresh Graph
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user