Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Zooming Controls to Visualizer #762

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 17 additions & 14 deletions calm-visualizer/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import Drawer from './components/drawer/Drawer';
import Navbar from './components/navbar/Navbar';
import { CALMInstantiation } from '../../shared/src/types';
import React from 'react';
import { ZoomProvider } from './components/zoom-context.provider';

function App() {
const [title, setTitle] = useState<string | undefined>(undefined);
Expand All @@ -21,20 +22,22 @@ function App() {
}

return (
<div className="h-screen flex flex-col">
<Navbar
handleUpload={handleFile}
isGraphRendered={instance ? true : false}
toggleNodeDesc={() => setNodeDescActive((isNodeDescActive) => !isNodeDescActive)}
toggleConnectionDesc={() => setConDescActive((isConDescActive) => !isConDescActive)}
/>
<Drawer
isNodeDescActive={isNodeDescActive}
isConDescActive={isConDescActive}
calmInstance={instance}
title={title}
/>
</div>
<ZoomProvider>
<div className="h-screen flex flex-col">
<Navbar
handleUpload={handleFile}
isGraphRendered={instance ? true : false}
toggleNodeDesc={() => setNodeDescActive((isNodeDescActive) => !isNodeDescActive)}
toggleConnectionDesc={() => setConDescActive((isConDescActive) => !isConDescActive)}
/>
<Drawer
isNodeDescActive={isNodeDescActive}
isConDescActive={isConDescActive}
calmInstance={instance}
title={title}
/>
</div>
</ZoomProvider>
);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import './cytoscape.css';
import { useEffect, useRef, useState } from 'react';
import { useContext, useEffect, useRef, useState } from 'react';
import cytoscape, { Core, EdgeSingular, NodeSingular } from 'cytoscape';
import nodeHtmlLabel from 'cytoscape-node-html-label';
import nodeEdgeHtmlLabel from 'cytoscape-node-edge-html-label';
import coseBilkent from 'cytoscape-cose-bilkent';
import expandCollapse from 'cytoscape-expand-collapse';
import fcose from 'cytoscape-fcose';
import Sidebar from '../sidebar/Sidebar';
import { ZoomContext } from '../zoom-context.provider';

//Make some information available on tooltip hover

Expand Down Expand Up @@ -86,11 +87,17 @@ const CytoscapeRenderer = ({
}: Props) => {
const cyRef = useRef<HTMLDivElement>(null);
const [cy, setCy] = useState<Core | null>(null);
const { zoomLevel, updateZoom } = useContext(ZoomContext);
const [selectedNode, setSelectedNode] = useState<Node['data'] | null>(null);
const [selectedEdge, setSelectedEdge] = useState<Edge['data'] | null>(null);

useEffect(() => {
if (cy) {
//Ensure cytoscape zoom and context state are synchronised
if(cy.zoom() != zoomLevel) {
updateZoom(cy.zoom());
}

(cy as any).nodeHtmlLabel([
{
query: '.node',
Expand Down Expand Up @@ -124,6 +131,8 @@ const CytoscapeRenderer = ({
setSelectedEdge(edge?.data()); // Update state with the clicked node's data
});

cy.on('zoom', () => updateZoom(cy.zoom()));

return () => {
cy.destroy();
};
Expand Down Expand Up @@ -166,11 +175,18 @@ const CytoscapeRenderer = ({
);
}, [nodes, edges]); // Re-render on cy, nodes or edges change

useEffect(() => {
//Ensure cytoscape zoom and context state are synchronised
if(cy?.zoom() !== zoomLevel) {
cy?.zoom(zoomLevel);
}
}, [zoomLevel])

return (
<div className="relative flex m-auto border">
{title && (
<div className="graph-title absolute m-5 bg-primary-content shadow-md">
<span className="text-m">Architecture: {title}</span>
<div className="graph-title absolute m-5 bg-gray-100 shadow-md">
<span className="text-m">Architecture: {title}</span>
</div>
)}
<div
Expand Down
27 changes: 25 additions & 2 deletions calm-visualizer/src/components/navbar/Navbar.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React from 'react';
import React, { useContext } from 'react';
import { ZoomContext } from '../zoom-context.provider';

interface NavbarProps {
handleUpload: (instanceFile: File) => void;
Expand All @@ -16,6 +17,23 @@ function Navbar({
const upload = (file: File) => {
handleUpload(file);
};
const { zoomLevel, updateZoom } = useContext(ZoomContext);

function zoomIn() {
//Obtain percentage as integer
const currentPercentageZoom = Math.round(zoomLevel*100);
//Add 10% to the zoom or round to upper 10% interval
const newPercentageZoom = Math.floor(currentPercentageZoom/10)*10 + 10;
updateZoom(newPercentageZoom/100);
}

function zoomOut() {
//Obtain percentage as integer
const currentPercentageZoom = Math.round(zoomLevel*100);
//Subtract 10% from the zoom or round to lower 10% interval - but not less than zero
const newPercentageZoom = Math.max(Math.ceil(currentPercentageZoom/10)*10 - 10, 0);
updateZoom(newPercentageZoom/100);
}

return (
<div className="navbar bg-base-300">
Expand Down Expand Up @@ -51,8 +69,13 @@ function Navbar({

{isGraphRendered && (
<>
<div className="divider divider-horizontal"></div>
<div className="divider divider-horizontal"></div>
<div className="toggles menu-horizontal">
<label className="label cursor-pointer">
<span className="label label-text">Zoom: {(zoomLevel*100).toFixed(0)}%</span>
<button className='ms-1 ps-2 pe-2 cursor-pointer' onClick={zoomIn}>+</button>
<button className='ms-1 ps-2 pe-2 cursor-pointer' onClick={zoomOut}>-</button>
</label>
<label className="label cursor-pointer">
<span className="label label-text">Connection Descriptions</span>
<input
Expand Down
28 changes: 28 additions & 0 deletions calm-visualizer/src/components/zoom-context.provider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import React, { createContext, useState } from 'react';

type ZoomContextProps = {
zoomLevel: number,
updateZoom: (newZoomLevel: number) => void
}

type ZoomProviderProps = { children: React.ReactNode; };

const ZoomContext = createContext<ZoomContextProps>({
zoomLevel: 1, updateZoom: () => {}
});

const ZoomProvider = ({children}: ZoomProviderProps) => {
const [zoomLevel, setZoomLevel] = useState(1);

const updateZoom = (newZoomLevel: number) => {
setZoomLevel(newZoomLevel);
};

return (
<ZoomContext.Provider value={{ zoomLevel, updateZoom }}>
{children}
</ZoomContext.Provider>
);
};

export { ZoomContext, ZoomProvider };