Skip to content
Open
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
43 changes: 2 additions & 41 deletions packages/graph-explorer/src/core/StateProvider/edges.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,6 @@
import { atom, useSetAtom } from "jotai";
import { atomFamily, atomWithReset, RESET } from "jotai/utils";
import {
createRenderedEdgeId,
getEdgeIdFromRenderedEdgeId,
type RenderedEdgeId,
type Edge,
type EdgeId,
} from "@/core";
import { atomFamily, atomWithReset } from "jotai/utils";
import type { Edge, EdgeId } from "@/core";
import { filteredNodesSelector } from "./nodes";

export function toEdgeMap(edges: Iterable<Edge>): Map<EdgeId, Edge> {
Expand All @@ -20,40 +14,7 @@ export const edgeSelector = atomFamily((id: EdgeId) =>
);

export const edgesSelectedIdsAtom = atomWithReset(new Set<EdgeId>());

export const edgesSelectedRenderedIdsAtom = atom(
get => new Set(get(edgesSelectedIdsAtom).values().map(createRenderedEdgeId)),
(_get, set, newValue: Set<RenderedEdgeId> | typeof RESET) => {
if (newValue === RESET) {
set(edgesSelectedIdsAtom, newValue);
return;
}

set(
edgesSelectedIdsAtom,
new Set(newValue.values().map(getEdgeIdFromRenderedEdgeId))
);
}
);

export const edgesOutOfFocusIdsAtom = atomWithReset(new Set<EdgeId>());

export const edgesOutOfFocusRenderedIdsAtom = atom(
get =>
new Set(get(edgesOutOfFocusIdsAtom).values().map(createRenderedEdgeId)),
(_get, set, newValue: Set<RenderedEdgeId> | typeof RESET) => {
if (newValue === RESET) {
set(edgesOutOfFocusIdsAtom, newValue);
return;
}

set(
edgesOutOfFocusIdsAtom,
new Set(newValue.values().map(getEdgeIdFromRenderedEdgeId))
);
}
);

export const edgesFilteredIdsAtom = atomWithReset(new Set<EdgeId>());
export const edgesTypesFilteredAtom = atomWithReset(new Set<string>());

Expand Down
45 changes: 2 additions & 43 deletions packages/graph-explorer/src/core/StateProvider/nodes.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,6 @@
import { atom, useAtomValue, useSetAtom } from "jotai";
import { atomFamily, atomWithReset, RESET } from "jotai/utils";
import {
createRenderedVertexId,
getVertexIdFromRenderedVertexId,
type RenderedVertexId,
type Vertex,
type VertexId,
createVertex,
} from "@/core";
import { atomFamily, atomWithReset } from "jotai/utils";
import { type Vertex, type VertexId, createVertex } from "@/core";
import { vertexDetailsQuery } from "@/connector";
import { useQuery } from "@tanstack/react-query";

Expand All @@ -22,41 +15,7 @@ export const nodeSelector = atomFamily((id: VertexId) =>
);

export const nodesSelectedIdsAtom = atomWithReset(new Set<VertexId>());

export const nodesSelectedRenderedIdsAtom = atom(
get =>
new Set(get(nodesSelectedIdsAtom).values().map(createRenderedVertexId)),
(_get, set, newValue: Set<RenderedVertexId> | typeof RESET) => {
if (newValue === RESET) {
set(nodesSelectedIdsAtom, newValue);
return;
}

set(
nodesSelectedIdsAtom,
new Set(newValue.values().map(getVertexIdFromRenderedVertexId))
);
}
);

export const nodesOutOfFocusIdsAtom = atomWithReset(new Set<VertexId>());

export const nodesOutOfFocusRenderedIdsAtom = atom(
get =>
new Set(get(nodesOutOfFocusIdsAtom).values().map(createRenderedVertexId)),
(_get, set, newValue: Set<RenderedVertexId> | typeof RESET) => {
if (newValue === RESET) {
set(nodesOutOfFocusIdsAtom, newValue);
return;
}

set(
nodesOutOfFocusIdsAtom,
new Set(newValue.values().map(getVertexIdFromRenderedVertexId))
);
}
);

export const nodesFilteredIdsAtom = atomWithReset(new Set<VertexId>());
export const nodesTypesFilteredAtom = atomWithReset(new Set<string>());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,10 @@ import Tabular from "@/components/Tabular/Tabular";
import {
edgesFilteredIdsAtom,
edgesOutOfFocusIdsAtom,
edgesSelectedIdsAtom,
edgesTableFiltersAtom,
edgesTableSortsAtom,
useToggleFilteredEdge,
} from "@/core/StateProvider/edges";
import { nodesSelectedIdsAtom } from "@/core/StateProvider/nodes";
import { useDeepMemo } from "@/hooks";
import useTranslations from "@/hooks/useTranslations";
import {
Expand All @@ -27,6 +25,7 @@ import {
} from "@/core";
import { useAtom, useAtomValue, useSetAtom } from "jotai";
import { LABELS } from "@/utils";
import { useGraphSelection } from "@/modules/GraphViewer";

/** Creates the model for the table data */
function createEdgeForTable(
Expand Down Expand Up @@ -58,9 +57,7 @@ const EdgesTabular = forwardRef<TabularInstance<ToggleEdge>, any>(
const setEdgesOut = useSetAtom(edgesOutOfFocusIdsAtom);
const filteredEdges = useAtomValue(edgesFilteredIdsAtom);
const toggleFilteredEdge = useToggleFilteredEdge();
const setSelectedNodesIds = useSetAtom(nodesSelectedIdsAtom);
const [selectedEdgesIds, setSelectedEdgesIds] =
useAtom(edgesSelectedIdsAtom);
const { graphSelection, replaceGraphSelection } = useGraphSelection();
const [tableFilters, setTableFilters] = useAtom(edgesTableFiltersAtom);
const [tableSorts, setTableSorts] = useAtom(edgesTableSortsAtom);

Expand Down Expand Up @@ -128,20 +125,19 @@ const EdgesTabular = forwardRef<TabularInstance<ToggleEdge>, any>(

const onSelectRows = (rowIndex: string) => {
const entityId = data[Number(rowIndex)].id;
setSelectedEdgesIds(new Set([entityId]));
setSelectedNodesIds(new Set([]));
replaceGraphSelection({ edges: [entityId] });
};

const selectedRowsIds: Record<string, boolean> = useDeepMemo(() => {
const selectedRows: Record<string, boolean> = {};
Array.from(selectedEdgesIds).forEach(selectedEdgeId => {
graphSelection.edges.forEach(selectedEdgeId => {
const rowIndex = data.findIndex(edge => edge.id === selectedEdgeId);
if (rowIndex !== -1) {
selectedRows[rowIndex] = true;
}
});
return selectedRows;
}, [data, selectedEdgesIds]);
}, [data, graphSelection]);

return (
<Tabular
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,17 @@ import {
useDisplayVerticesInCanvas,
useAllNeighbors,
} from "@/core";
import { edgesSelectedIdsAtom } from "@/core/StateProvider/edges";
import {
nodesFilteredIdsAtom,
nodesOutOfFocusIdsAtom,
nodesSelectedIdsAtom,
nodesTableFiltersAtom,
nodesTableSortsAtom,
useToggleFilteredNode,
} from "@/core/StateProvider/nodes";

import { useDeepMemo, useTranslations } from "@/hooks";
import { useAtom, useAtomValue, useSetAtom } from "jotai";
import { useGraphSelection } from "@/modules/GraphViewer";

type ToggleVertex = DisplayVertex & {
__is_visible: boolean;
Expand All @@ -39,9 +38,7 @@ const NodesTabular = forwardRef<TabularInstance<ToggleVertex>, any>(
const setNodesOut = useSetAtom(nodesOutOfFocusIdsAtom);
const filteredNodes = useAtomValue(nodesFilteredIdsAtom);
const toggleFilteredNode = useToggleFilteredNode();
const [selectedNodesIds, setSelectedNodesIds] =
useAtom(nodesSelectedIdsAtom);
const setSelectedEdgesIds = useSetAtom(edgesSelectedIdsAtom);
const { graphSelection, replaceGraphSelection } = useGraphSelection();
const [tableFilters, setTableFilters] = useAtom(nodesTableFiltersAtom);
const [tableSorts, setTableSorts] = useAtom(nodesTableSortsAtom);

Expand Down Expand Up @@ -122,20 +119,20 @@ const NodesTabular = forwardRef<TabularInstance<ToggleVertex>, any>(

const onSelectRows = (rowIndex: string) => {
const entityId = data[Number(rowIndex)].id;
setSelectedNodesIds(new Set([entityId]));
setSelectedEdgesIds(new Set([]));
console.log("Changing selection from NodesTabular");

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we need this?

replaceGraphSelection({ vertices: [entityId] });
};

const selectedRowsIds: Record<string, boolean> = useDeepMemo(() => {
const selectedRows: Record<string, boolean> = {};
Array.from(selectedNodesIds).forEach(selectedNodeId => {
graphSelection.vertices.forEach(selectedNodeId => {
const rowIndex = data.findIndex(node => node.id === selectedNodeId);
if (rowIndex !== -1) {
selectedRows[rowIndex] = true;
}
});
return selectedRows;
}, [data, selectedNodesIds]);
}, [data, graphSelection]);

return (
<Tabular
Expand Down
68 changes: 35 additions & 33 deletions packages/graph-explorer/src/modules/GraphViewer/GraphViewer.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { type MouseEvent, useRef, useState } from "react";
import {
createRenderedEdgeId,
createRenderedVertexId,
getEdgeIdFromRenderedEdgeId,
getVertexIdFromRenderedVertexId,
type RenderedEdgeId,
type RenderedVertex,
Expand All @@ -23,22 +26,15 @@ import {
import Graph from "@/components/Graph";
import type { GraphRef } from "@/components/Graph/Graph";
import type { ElementEventCallback } from "@/components/Graph/hooks/useAddClickEvents";
import {
edgesOutOfFocusRenderedIdsAtom,
edgesSelectedRenderedIdsAtom,
} from "@/core/StateProvider/edges";
import {
nodesOutOfFocusRenderedIdsAtom,
nodesSelectedRenderedIdsAtom,
} from "@/core/StateProvider/nodes";
import { edgesOutOfFocusIdsAtom } from "@/core/StateProvider/edges";
import { nodesOutOfFocusIdsAtom } from "@/core/StateProvider/nodes";
import { useClearGraph, useExpandNode } from "@/hooks";
import ContextMenu from "./internalComponents/ContextMenu";
import useContextMenu from "./useContextMenu";
import useGraphGlobalActions from "./useGraphGlobalActions";
import useGraphStyles from "./useGraphStyles";
import useNodeBadges from "./useNodeBadges";
import type { SelectedElements } from "@/components/Graph/Graph.model";
import { useAutoOpenDetailsSidebar } from "./useAutoOpenDetailsSidebar";
import { ImportGraphButton } from "./ImportGraphButton";
import { ExportGraphButton } from "./ExportGraphButton";
import {
Expand All @@ -50,9 +46,10 @@ import {
ZoomInIcon,
ZoomOutIcon,
} from "lucide-react";
import { useAtom, useAtomValue } from "jotai";
import { useAtomValue } from "jotai";
import { useDefaultNeighborExpansionLimit } from "@/hooks/useExpandNode";
import { graphLayoutSelectionAtom, SelectLayout } from "./SelectLayout";
import { useGraphSelection } from "./useGraphSelection";

// Prevent open context menu on Windows
function onContextMenu(e: MouseEvent<HTMLDivElement>) {
Expand All @@ -63,31 +60,36 @@ function onContextMenu(e: MouseEvent<HTMLDivElement>) {
export default function GraphViewer() {
const graphRef = useRef<GraphRef | null>(null);

const [nodesSelectedIds, setNodesSelectedIds] = useAtom(
nodesSelectedRenderedIdsAtom
);
const { graphSelection, replaceGraphSelection } = useGraphSelection();
const selectedVertices = graphSelection.vertices.map(createRenderedVertexId);
const selectedEdges = graphSelection.edges.map(createRenderedEdgeId);

const [edgesSelectedIds, setEdgesSelectedIds] = useAtom(
edgesSelectedRenderedIdsAtom
);
const nodesOutIds = useAtomValue(nodesOutOfFocusRenderedIdsAtom);
const edgesOutIds = useAtomValue(edgesOutOfFocusRenderedIdsAtom);
const nodesOutIds = useAtomValue(nodesOutOfFocusIdsAtom);
const edgesOutIds = useAtomValue(edgesOutOfFocusIdsAtom);

const autoOpenDetails = useAutoOpenDetailsSidebar();
// Map the ids to rendered IDs for compatibility with Cytoscape
const nodesOutRenderedIds = new Set(
nodesOutIds.values().map(createRenderedVertexId)
);
const edgesOutRenderedIds = new Set(
edgesOutIds.values().map(createRenderedEdgeId)
);

const onSelectedElementIdsChange = ({
nodeIds,
edgeIds,
}: SelectedElements) => {
setNodesSelectedIds(nodeIds as Set<RenderedVertexId>);
setEdgesSelectedIds(edgeIds as Set<RenderedEdgeId>);

if (
(nodeIds.size === 1 && edgeIds.size === 0) ||
(nodeIds.size === 0 && edgeIds.size === 1)
) {
autoOpenDetails();
}
console.log("Changing selection from onSelectedElementIdsChange");
// Map the rendered ids to the original ids and change selection
replaceGraphSelection({
vertices: (nodeIds as Set<RenderedVertexId>)
.values()
.map(getVertexIdFromRenderedVertexId),
edges: (edgeIds as Set<RenderedEdgeId>)
.values()
.map(getEdgeIdFromRenderedEdgeId),
disableSideEffects: true,
});
};

const [legendOpen, setLegendOpen] = useState(false);
Expand Down Expand Up @@ -194,11 +196,11 @@ export default function GraphViewer() {
nodes={nodes}
edges={edges}
badgesEnabled={false}
getNodeBadges={getNodeBadges(nodesOutIds)}
selectedNodesIds={nodesSelectedIds}
selectedEdgesIds={edgesSelectedIds}
outOfFocusNodesIds={nodesOutIds}
outOfFocusEdgesIds={edgesOutIds}
getNodeBadges={getNodeBadges(nodesOutRenderedIds)}
selectedNodesIds={selectedVertices}
selectedEdgesIds={selectedEdges}
outOfFocusNodesIds={nodesOutRenderedIds}
outOfFocusEdgesIds={edgesOutRenderedIds}
onSelectedElementIdsChange={onSelectedElementIdsChange}
onNodeDoubleClick={onNodeDoubleClick}
onNodeRightClick={onNodeRightClick}
Expand Down
1 change: 1 addition & 0 deletions packages/graph-explorer/src/modules/GraphViewer/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export { default } from "./GraphViewer";
export * from "./useGraphSelection";
Loading
Loading