selection and state management between the visualizer and tabular view (#804, #819, #823)

This commit is contained in:
Michael Quigley 2025-01-08 14:11:06 -05:00
parent 0c551c8df2
commit deb0d8ca22
No known key found for this signature in database
GPG Key ID: 9B60314A9DD20A62
4 changed files with 82 additions and 32 deletions

View File

@ -27,8 +27,6 @@ const EnvironmentNode = ({ data }) => {
s[i] = v;
});
setSparkData(s);
} else {
console.log("not found", data, environments);
}
}
}, [environments]);

View File

@ -1,13 +1,33 @@
import {Box, Paper} from "@mui/material";
import useStore from "./model/store.ts";
import {MaterialReactTable, type MRT_ColumnDef, useMaterialReactTable} from "material-react-table";
import {useMemo} from "react";
import {
getMRT_RowSelectionHandler,
MaterialReactTable,
type MRT_ColumnDef,
MRT_RowSelectionState,
useMaterialReactTable
} from "material-react-table";
import {useEffect, useMemo, useState} from "react";
import {Node} from "@xyflow/react";
const data: Node[] = [];
const TabularView = () => {
const overview = useStore((state) => state.overview);
const nodes = useStore((state) => state.nodes);
const selectedNode = useStore((state) => state.selectedNode);
const updateSelectedNode = useStore((state) => state.updateSelectedNode);
const [rowSelection, setRowSelection] = useState<MRT_RowSelectionState>({});
useEffect(() => {
if(selectedNode) {
let selection = {};
selection[selectedNode.id] = true;
setRowSelection(selection);
}
}, []);
useEffect(() => {
let sn = nodes.find(node => Object.keys(rowSelection).includes(node.id));
updateSelectedNode(sn);
}, [rowSelection]);
const columns = useMemo<MRT_ColumnDef<Node>[]>(
() => [
@ -25,11 +45,24 @@ const TabularView = () => {
const table = useMaterialReactTable({
columns: columns,
data: overview.nodes,
data: nodes,
enableRowSelection: false,
enableMultiRowSelection: false,
getRowId: r => r.id,
onRowSelectionChange: setRowSelection,
state: { rowSelection },
muiTableBodyRowProps: ({ row }) => ({
onClick: () => {
setRowSelection({[row.id]: true})
},
selected: rowSelection[row.id],
sx: {
cursor: 'pointer',
},
}),
positionToolbarAlertBanner: "bottom",
});
console.log(overview.nodes);
return (
<Box sx={{ width: "100%", mt: 2 }} height={{ xs: 400, sm: 600, md: 800 }}>
<Paper>

View File

@ -1,15 +1,15 @@
import "@xyflow/react/dist/style.css";
import "./styling/react-flow.css";
import {
applyNodeChanges,
Background,
Controls,
MiniMap,
Node,
ReactFlow,
ReactFlowProvider,
useEdgesState,
useNodesState,
useStore as xyStore
useOnViewportChange,
Viewport
} from "@xyflow/react";
import {VisualOverview} from "./model/visualizer.ts";
import {useEffect} from "react";
@ -30,16 +30,24 @@ const nodeTypes = {
const Visualizer = () => {
const overview = useStore((state) => state.overview);
const selectedNode = useStore((state) => state.selectedNode);
const updateSelectedNode = useStore((state) => state.updateSelectedNode);
const viewport = useStore((state) => state.viewport);
const updateViewport = useStore((state) => state.updateViewport);
const [nodes, setNodes, onNodesChange] = useNodesState([]);
const [edges, setEdges, onEdgesChange] = useEdgesState([]);
const transform = xyStore((store) => store.transform);
const nodes = useStore((state) => state.nodes);
const updateNodes = useStore((state) => state.updateNodes);
const edges = useStore((state) => state.edges);
const updateEdges = useStore((state) => state.updateEdges);
useEffect(() => {
updateViewport(transform);
}, [transform]);
const onNodesChange = (changes) => {
updateNodes(applyNodeChanges(changes, nodes));
}
useOnViewportChange({
onEnd: (viewport: Viewport) => {
updateViewport(viewport);
}
});
const onSelectionChange = ({ nodes }) => {
if(nodes.length > 0) {
@ -80,27 +88,30 @@ const Visualizer = () => {
useEffect(() => {
if(overview) {
let laidOut = layout(overview.nodes, overview.edges);
setNodes(laidOut.nodes);
setEdges(laidOut.edges);
let selected = laidOut.nodes.map((n) => ({
...n,
selected: selectedNode ? selectedNode.id === n.id : false,
}));
updateNodes(selected);
updateEdges(laidOut.edges);
}
}, [overview]);
const defaultViewport = {
x: viewport[0],
y: viewport[1],
zoom: viewport[2],
let fitView = false;
if(viewport.x === 0 && viewport.y === 0 && viewport.zoom === 1) {
fitView = true;
}
return (
<ReactFlow
nodeTypes={nodeTypes}
nodes={nodes}
edges={edges}
onNodesChange={onNodesChange}
onEdgesChange={onEdgesChange}
edges={edges}
onSelectionChange={onSelectionChange}
nodesDraggable={false}
defaultViewport={defaultViewport}
defaultViewport={viewport}
fitView={fitView}
>
<Background />
<Controls position="bottom-left" orientation="horizontal" showInteractive={false} />

View File

@ -1,15 +1,17 @@
import {create} from "zustand";
import {Environment} from "../api";
import {VisualOverview} from "./visualizer.ts";
import {Node} from "@xyflow/react";
import {Edge, Node, Viewport} from "@xyflow/react";
import {User} from "./user.ts";
type StoreState = {
user: User;
environments: Array<Environment>;
overview: VisualOverview;
environments: Array<Environment>;
nodes: Node[];
edges: Edge[];
selectedNode: Node;
viewport: Array<Number>;
viewport: Viewport;
};
type StoreAction = {
@ -17,6 +19,8 @@ type StoreAction = {
updateOverview: (vov: StoreState['overview']) => void,
updateEnvironments: (environments: StoreState['environments']) => void,
updateSelectedNode: (selectedNode: StoreState['selectedNode']) => void,
updateNodes: (nodes: StoreState['nodes']) => void,
updateEdges: (edges: StoreState['edges']) => void,
updateViewport: (viewport: StoreState['viewport']) => void,
};
@ -24,11 +28,15 @@ const useStore = create<StoreState & StoreAction>((set) => ({
user: null,
overview: new VisualOverview(),
environments: new Array<Environment>(),
nodes: [],
edges: [],
selectedNode: null,
viewport: [0, 0, 1.5],
viewport: {x: 0, y: 0, zoom: 1},
updateUser: (user) => set({user: user}),
updateOverview: (vov) => set({overview: vov}),
updateEnvironments: (environments) => set({environments: environments}),
updateNodes: (nodes) => set({nodes: nodes}),
updateEdges: (edges) => set({edges: edges}),
updateSelectedNode: (selectedNode) => set({selectedNode: selectedNode}),
updateViewport: (viewport) => set({viewport: viewport})
}));