Skip to content

Commit

Permalink
Merge pull request #159 from FalkorDB/styling-graph-page
Browse files Browse the repository at this point in the history
fix #157 Styling graph page
  • Loading branch information
AviAvni authored Apr 10, 2024
2 parents cce46dd + 68d0b6e commit 858a7f1
Show file tree
Hide file tree
Showing 7 changed files with 103 additions and 73 deletions.
10 changes: 8 additions & 2 deletions app/graph/DataPanel.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";

interface Props {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
object: any;
}

const excludedProperties = new Set([
"category",
"color",
Expand All @@ -9,7 +14,8 @@ const excludedProperties = new Set([
"source"
]);

export default function DataPanel({ node }: { node: Node }) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export default function DataPanel({ object }: Props) {
const rowClass = "dark:hover:bg-slate-700 hover:bg-gray-400 border-y-[1px] border-y-gray-700"

return (
Expand All @@ -22,7 +28,7 @@ export default function DataPanel({ node }: { node: Node }) {
</TableHeader>
<TableBody>
{
Object.entries(node).filter((row) => !excludedProperties.has(row[0])).map((row, index) => (
Object.entries(object).filter((row) => !excludedProperties.has(row[0])).map((row, index) => (
// eslint-disable-next-line react/no-array-index-key
<TableRow className={rowClass} key={index}>
{
Expand Down
44 changes: 30 additions & 14 deletions app/graph/GraphView.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import CytoscapeComponent from "react-cytoscapejs";
import { toast } from "@/components/ui/use-toast";
import cytoscape, { ElementDefinition, EventObject, NodeDataDefinition } from "cytoscape";
import { useRef, useState, useImperativeHandle, forwardRef } from "react";
import { useRef, useState, useImperativeHandle, forwardRef, useEffect } from "react";
import { signOut } from "next-auth/react";
import fcose from 'cytoscape-fcose';
import { ResizableHandle, ResizablePanel, ResizablePanelGroup } from "@/components/ui/resizable";
import { ImperativePanelHandle } from "react-resizable-panels";
import { ChevronLeft, ChevronRight } from "lucide-react"
import Labels from "./labels";
import Toolbar from "./toolbar";
import { Category, Graph } from "./model";
Expand Down Expand Up @@ -95,12 +96,20 @@ interface GraphViewProps {

const GraphView = forwardRef(({ graph, darkmode }: GraphViewProps, ref) => {

const [selectedNode, setSelectedObject] = useState(null);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const [selectedObject, setSelectedObject] = useState<any | null>(null);
const [isOpen, setIsOpen] = useState<boolean>(false);

// A reference to the chart container to allowing zooming and editing
const chartRef = useRef<cytoscape.Core | null>(null)
const dataPanel = useRef<ImperativePanelHandle>(null)

useEffect(() => {
if (isOpen) {
dataPanel.current?.expand()
} else dataPanel.current?.collapse()
}, [isOpen])

useImperativeHandle(ref, () => ({
expand: (elements: ElementDefinition[]) => {
const chart = chartRef.current
Expand Down Expand Up @@ -168,15 +177,15 @@ const GraphView = forwardRef(({ graph, darkmode }: GraphViewProps, ref) => {
const handleTap = (evt: EventObject) => {
const object = evt.target.json().data;
setSelectedObject(object);
dataPanel.current?.expand();
setIsOpen(true);
}

return (
<ResizablePanelGroup direction="horizontal">
<ResizablePanel className="h-full flex flex-col">
<div className="grid grid-cols-6">
<Toolbar className="col-start-1 justify-start" chartRef={chartRef} />
<Labels className="col-end-7 justify-end" categories={graph.Categories} onClick={onCategoryClick} />
<ResizablePanel defaultSize={selectedObject ? 80 : 100} className="h-full flex flex-col">
<div className="flex flex-row justify-between">
<Toolbar className="" chartRef={chartRef} />
<Labels className="pr-16" categories={graph.Categories} onClick={onCategoryClick} />
</div>
<CytoscapeComponent
cy={(cy) => {
Expand All @@ -190,7 +199,7 @@ const GraphView = forwardRef(({ graph, darkmode }: GraphViewProps, ref) => {

// Listen to the click event on nodes for showing node properties
cy.on('tap', 'node', handleTap);

// Listen to the click event on edges for showing edge properties
cy.on('tap', 'edge', handleTap);
}}
Expand All @@ -200,13 +209,20 @@ const GraphView = forwardRef(({ graph, darkmode }: GraphViewProps, ref) => {
className="w-full grow"
/>
</ResizablePanel>
<ResizableHandle withHandle />
<ResizablePanel ref={dataPanel} maxSize={50} minSize={17} collapsible defaultSize={selectedNode ? 20 : 0} className="bg-gray-100 dark:bg-gray-800">
{selectedNode && <DataPanel node={selectedNode} />}
</ResizablePanel>
<ResizableHandle />
{
selectedObject &&
<button type="button" onClick={() => setIsOpen(prev => !prev)} className="fixed right-5 top-[50%]">
{isOpen ? <ChevronRight /> : <ChevronLeft />}
</button>
}
{
isOpen &&
<ResizablePanel id="panel" ref={dataPanel} maxSize={50} minSize={20} collapsible defaultSize={selectedObject ? 20 : 0} className="bg-gray-100 dark:bg-gray-800">
{selectedObject && <DataPanel object={selectedObject} />}
</ResizablePanel>
}
</ResizablePanelGroup>


)
});

Expand Down
9 changes: 3 additions & 6 deletions app/graph/metadataview.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
export default function MetaDataView({ metadata }: { metadata: string[] }) {
return (
<div>
{
// eslint-disable-next-line react/no-array-index-key
metadata.map((val, index) => <p key={index}>{val}</p>)
}
</div>
<p>
{metadata.join(", ")}
</p>
)
}
42 changes: 23 additions & 19 deletions app/graph/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,8 @@ export default function Page() {
useEffect(() => {
if (showGraph) {
setValue("graph")
}else if(showData) {
} else if (showData) {
setValue("data")
}else {
setValue("metadata")
}
}, [showData, showGraph])
const graphView = useRef<GraphViewRef>(null)
Expand Down Expand Up @@ -101,25 +99,31 @@ export default function Page() {
onQueryUpdate={(state) => { queryState.current = state }}
onDeleteGraph={() => setGraph(Graph.empty())}
/>
<div className="flex flex-col grow border border-gray-300 rounded-lg p-2 overflow-auto">
<div className="h-1 grow border flex flex-col gap-2 border-gray-300 rounded-lg p-2">
{
graph.Id &&
<Tabs value={value} className="grow flex flex-col justify-center items-center">
<TabsList className="border w-fit">
<TabsTrigger value="metadata" onClick={() => setValue("metadata")}>Metadata</TabsTrigger>
{showData && <TabsTrigger value="data" onClick={() => setValue("data")}>Data</TabsTrigger>}
{showGraph && <TabsTrigger value="graph" onClick={() => setValue("graph")}>Graph</TabsTrigger>}
</TabsList>
<TabsContent value="metadata" className="grow w-full">
<>
<Tabs value={value} className="h-1 grow flex flex-row">
<div className="w-20 flex flex-row items-center">
{
(showData || showGraph) &&
<TabsList className="h-fit flex flex-col p-0">
{showGraph && <TabsTrigger className="w-full" value="graph" onClick={() => setValue("graph")}>Graph</TabsTrigger>}
{showData && <TabsTrigger className="w-full" value="table" onClick={() => setValue("table")}>Table</TabsTrigger>}
</TabsList>
}
</div>
<TabsContent value="table" className="w-1 grow overflow-auto">
<TableView graph={graph} />
</TabsContent>
<TabsContent value="graph" className="w-1 grow">
<GraphView ref={graphView} graph={graph} darkmode={darkmode} />
</TabsContent>
</Tabs>
<div className="border rounded-md border-gray-300 p-2">
<MetaDataView metadata={metaData} />
</TabsContent>
<TabsContent value="data" className="grow w-full flex-[1_1_0] overflow-auto">
<TableView graph={graph} />
</TabsContent>
<TabsContent value="graph" className="grow w-full">
<GraphView ref={graphView} graph={graph} darkmode={darkmode} />
</TabsContent>
</Tabs>
</div>
</>
}
</div>
</div>
Expand Down
66 changes: 36 additions & 30 deletions app/graph/toolbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/comp
import { CircleDot, ZoomIn, ZoomOut } from "lucide-react";
import { cn } from "@/lib/utils"

export default function Toolbar({chartRef, className=""}: {
export default function Toolbar({ chartRef, className = "" }: {
chartRef: React.RefObject<cytoscape.Core>, className: string
}) {

Expand All @@ -13,7 +13,7 @@ export default function Toolbar({chartRef, className=""}: {
}
}

const handleCenterClick= () => {
const handleCenterClick = () => {
const chart = chartRef.current
if (chart) {
chart.fit()
Expand All @@ -22,33 +22,39 @@ export default function Toolbar({chartRef, className=""}: {
}

return (
<div className={cn("flex flex-row gap-x-1", className)}>
<TooltipProvider>
<Tooltip>
<TooltipTrigger className="text-gray-600 dark:text-gray-400 rounded-lg border border-gray-300 p-2" onClick={() => handleZoomClick(1.1)}>
<ZoomIn />
</TooltipTrigger>
<TooltipContent>
<p>Zoom In</p>
</TooltipContent>
</Tooltip>
<Tooltip>
<TooltipTrigger className="text-gray-600 dark:text-gray-400 rounded-lg border border-gray-300 p-2" onClick={() => handleZoomClick(0.9)}>
<ZoomOut />
</TooltipTrigger>
<TooltipContent>
<p>Zoom Out</p>
</TooltipContent>
</Tooltip>
<Tooltip>
<TooltipTrigger className="text-gray-600 dark:text-gray-400 rounded-lg border border-gray-300 p-2" onClick={handleCenterClick}>
<CircleDot />
</TooltipTrigger>
<TooltipContent>
<p>Center</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
</div>
<TooltipProvider>
<ul className={cn("flex flex-row gap-2", className)}>
<li>
<Tooltip>
<TooltipTrigger className="text-gray-600 dark:text-gray-400 rounded-lg border border-gray-300 p-2" onClick={() => handleZoomClick(1.1)}>
<ZoomIn />
</TooltipTrigger>
<TooltipContent>
<p>Zoom In</p>
</TooltipContent>
</Tooltip>
</li>
<li>
<Tooltip>
<TooltipTrigger className="text-gray-600 dark:text-gray-400 rounded-lg border border-gray-300 p-2" onClick={() => handleZoomClick(0.9)}>
<ZoomOut />
</TooltipTrigger>
<TooltipContent>
<p>Zoom Out</p>
</TooltipContent>
</Tooltip>
</li>
<li>
<Tooltip>
<TooltipTrigger className="text-gray-600 dark:text-gray-400 rounded-lg border border-gray-300 p-2" onClick={handleCenterClick}>
<CircleDot />
</TooltipTrigger>
<TooltipContent>
<p>Center</p>
</TooltipContent>
</Tooltip>
</li>
</ul>
</TooltipProvider>
)
}
3 changes: 2 additions & 1 deletion app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import './globals.css'
import type { Metadata } from 'next'
import { Inter } from 'next/font/google'
import { Toaster } from '@/components/ui/toaster'
import { cn } from '@/lib/utils'
import NextAuthProvider from './providers'

const inter = Inter({ subsets: ['latin'] })
Expand All @@ -20,7 +21,7 @@ export default function RootLayout({
// caused by mismatched client/server content caused by next-themes
return (
<html lang="en" suppressHydrationWarning>
<body className={inter.className}>
<body className={cn("h-screen w-screen", inter.className)}>
<NextAuthProvider>{children}</NextAuthProvider>
<Toaster />
</body>
Expand Down
2 changes: 1 addition & 1 deletion app/providers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export default function NextAuthProvider({ children }: { children: React.ReactNo
return (
<SessionProvider>
<ThemeProvider attribute="class" enableSystem>
<ResizablePanelGroup direction="horizontal" className='min-h-screen'>
<ResizablePanelGroup direction="horizontal" className='h-screen'>
<ResizablePanel
ref={navPanel}
maxSize={panelSize}
Expand Down

0 comments on commit 858a7f1

Please sign in to comment.