Skip to content
Merged
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
3 changes: 3 additions & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
[#956](https://github.com/aws/graph-explorer/pull/956),
[#957](https://github.com/aws/graph-explorer/pull/957),
[#974](https://github.com/aws/graph-explorer/pull/974))
- **Updated** graph layout options to include better descriptions and new
directions for hierarchical and subway
([#973](https://github.com/aws/graph-explorer/pull/973))

### Other changes

Expand Down
10 changes: 1 addition & 9 deletions packages/graph-explorer/src/components/Graph/Graph.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,15 +54,7 @@ export type Selection = {
onSelectedEdgesIdsChange?(edgesIds: Array<string> | Set<string>): void;
};

export type LayoutName =
| "CONCENTRIC"
| "DAGRE_HORIZONTAL"
| "DAGRE_VERTICAL"
| "F_COSE"
| "D3"
| "KLAY"
| "SUBWAY_HORIZONTAL"
| "SUBWAY_VERTICAL";
export type { LayoutName } from "./helpers/layoutConfig";

export type GraphNode = {
data: {
Expand Down
3 changes: 2 additions & 1 deletion packages/graph-explorer/src/components/Graph/Graph.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import type {
CytoscapeType,
GraphEdge,
GraphNode,
LayoutName,
Selection,
} from "./Graph.model";
import { runLayout } from "./helpers/layout";
Expand Down Expand Up @@ -69,7 +70,7 @@ export interface GraphProps<
};
useAnimation?: boolean;
// internal state of the graph
layout?: string;
layout?: LayoutName;
additionalLayoutsConfig?: { [key: string]: Partial<cytoscape.LayoutOptions> };
loading?: boolean;
onLayoutUpdated?: (cy: CytoscapeType, layout: string) => any;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { CytoscapeType } from "../Graph.model";
import { availableLayoutsConfig } from "./layoutConfig";
import { availableLayoutsConfig, LayoutName } from "./layoutConfig";

type ExpandedCytoscapeLayoutOptions = {
fixedNodeConstraint?: {
Expand All @@ -9,7 +9,7 @@ type ExpandedCytoscapeLayoutOptions = {
};
export const runLayout = (
cyReference: CytoscapeType,
layoutName: string,
layoutName: LayoutName,
additionalLayoutsConfig: {
[layoutName: string]: Partial<
cytoscape.LayoutOptions & ExpandedCytoscapeLayoutOptions
Expand All @@ -34,7 +34,7 @@ export const runLayout = (
position: node.position(),
}));
}
const layout = cyReference.layout(_layout);
const layout = cyReference.layout(_layout as cytoscape.LayoutOptions);
layout.run();
return;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export const concentricLayout = {
}, // transform a given node position. Useful for changing flow direction in discrete layouts
};

export const dagreLayoutHorizontal = {
export const dagreLayoutTopToBottom = {
name: "dagre",

// dagre algo options, uses default value on undefined
Expand Down Expand Up @@ -85,12 +85,22 @@ export const dagreLayoutHorizontal = {
// stop: function () {}, // on layoutstop
};

export const dagreLayoutVertical = {
...dagreLayoutHorizontal,
export const dagreLayoutBottomToTop = {
...dagreLayoutTopToBottom,
rankDir: "BT",
};

export const dagreLayoutLeftToRight = {
...dagreLayoutTopToBottom,
rankDir: "LR",
};

export const subwayLayoutHorizontal = {
export const dagreLayoutRightToLeft = {
...dagreLayoutTopToBottom,
rankDir: "RL",
};

export const subwayLayoutTopToBottom = {
name: "dagre",

// dagre algo options, uses default value on undefined
Expand Down Expand Up @@ -125,11 +135,21 @@ export const subwayLayoutHorizontal = {
// stop: function () {}, // on layoutstop
};

export const subwayLayoutVertical = {
...subwayLayoutHorizontal,
export const subwayLayoutLeftToRight = {
...subwayLayoutTopToBottom,
rankDir: "LR",
};

export const subwayLayoutRightToLeft = {
...subwayLayoutTopToBottom,
rankDir: "RL",
};

export const subwayLayoutBottomToTop = {
...subwayLayoutTopToBottom,
rankDir: "BT",
};

const d3force = {
name: "d3-force",
animate: "end",
Expand Down Expand Up @@ -228,11 +248,11 @@ const cose = {
initialEnergyOnIncremental: 1,
};

const klayLayout = {
const klayLayoutLeftToRight = {
name: "klay",
nodeDimensionsIncludeLabels: false, // Boolean which changes whether label dimensions are included when calculating node dimensions
fit: true, // Whether to fit
padding: 20, // Padding on fit
padding: 30, // Padding on fit
animate: true, // Whether to transition the node positions
animateFilter: function () {
return true;
Expand Down Expand Up @@ -293,13 +313,28 @@ const klayLayout = {
}, // Edges with a non-nil value are skipped when greedy edge cycle breaking is enabled
};

export const availableLayoutsConfig: Record<string, any> = {
const klayLayoutTopToBottom = {
...klayLayoutLeftToRight,
klay: {
...klayLayoutLeftToRight.klay,
direction: "DOWN",
},
};

export const availableLayoutsConfig = {
CONCENTRIC: concentricLayout,
DAGRE_HORIZONTAL: dagreLayoutHorizontal,
DAGRE_VERTICAL: dagreLayoutVertical,
DAGRE_TB: dagreLayoutTopToBottom,
DAGRE_BT: dagreLayoutBottomToTop,
DAGRE_LR: dagreLayoutLeftToRight,
DAGRE_RL: dagreLayoutRightToLeft,
F_COSE: cose,
D3: d3force,
KLAY: klayLayout,
SUBWAY_HORIZONTAL: subwayLayoutHorizontal,
SUBWAY_VERTICAL: subwayLayoutVertical,
KLAY_LR: klayLayoutLeftToRight,
KLAY_TB: klayLayoutTopToBottom,
SUBWAY_TB: subwayLayoutTopToBottom,
SUBWAY_BT: subwayLayoutBottomToTop,
SUBWAY_LR: subwayLayoutLeftToRight,
SUBWAY_RL: subwayLayoutRightToLeft,
};

export type LayoutName = keyof typeof availableLayoutsConfig;
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import cytoscape from "cytoscape";
import { useEffect, useRef } from "react";
import type { CytoscapeType } from "../Graph.model";
import type { CytoscapeType, LayoutName } from "../Graph.model";
import { runLayout } from "../helpers/layout";

interface UseUpdateLayout {
cy?: CytoscapeType;
layout?: string;
layout?: LayoutName;
useAnimation?: boolean;
additionalLayoutsConfig?: {
[layoutName: string]: Partial<cytoscape.LayoutOptions>;
Expand Down
5 changes: 4 additions & 1 deletion packages/graph-explorer/src/components/Select.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,10 @@ const SelectLabel = React.forwardRef<
>(({ className, ...props }, ref) => (
<SelectPrimitive.Label
ref={ref}
className={cn("px-3 py-1.5 text-base font-bold", className)}
className={cn(
"text-text-secondary font-base px-3 py-1.5 text-sm",
className
)}
{...props}
/>
));
Expand Down
48 changes: 3 additions & 45 deletions packages/graph-explorer/src/modules/GraphViewer/GraphViewer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import {
import Graph from "@/components/Graph";
import { GraphRef } from "@/components/Graph/Graph";
import { ElementEventCallback } from "@/components/Graph/hooks/useAddClickEvents";
import SelectField from "@/components/SelectField";
import {
edgesOutOfFocusRenderedIdsAtom,
edgesSelectedRenderedIdsAtom,
Expand Down Expand Up @@ -52,41 +51,7 @@ import {
} from "lucide-react";
import { useAtom, useAtomValue } from "jotai";
import { useDefaultNeighborExpansionLimit } from "@/hooks/useExpandNode";

const LAYOUT_OPTIONS = [
{
label: "Force Directed (F0Cose)",
value: "F_COSE",
},
{
label: "Force Directed (D3)",
value: "D3",
},
{
label: "Hierarchical - Vertical",
value: "DAGRE_VERTICAL",
},
{
label: "Hierarchical - Horizontal",
value: "DAGRE_HORIZONTAL",
},
{
label: "Subway - Vertical",
value: "SUBWAY_VERTICAL",
},
{
label: "Subway - Horizontal",
value: "SUBWAY_HORIZONTAL",
},
{
label: "Klay",
value: "KLAY",
},
{
label: "Concentric",
value: "CONCENTRIC",
},
];
import { graphLayoutSelectionAtom, SelectLayout } from "./SelectLayout";

// Prevent open context menu on Windows
function onContextMenu(e: MouseEvent<HTMLDivElement>) {
Expand Down Expand Up @@ -159,7 +124,7 @@ export default function GraphViewer() {
});
};

const [layout, setLayout] = useState("F_COSE");
const layout = useAtomValue(graphLayoutSelectionAtom);
const onClearGraph = useClearGraph();

const nodes = useRenderedVertices();
Expand All @@ -171,14 +136,7 @@ export default function GraphViewer() {
<PanelHeader>
<PanelTitle>Graph View</PanelTitle>
<PanelHeaderActions>
<SelectField
className="min-w-auto max-w-64"
label="Layout"
labelPlacement="inner"
options={LAYOUT_OPTIONS}
value={layout}
onValueChange={setLayout}
/>
<SelectLayout className="min-w-auto max-w-64" />
<IconButton
tooltipText="Re-run Layout"
icon={<RefreshCwIcon />}
Expand Down
66 changes: 66 additions & 0 deletions packages/graph-explorer/src/modules/GraphViewer/SelectLayout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import {
Select,
SelectContent,
SelectGroup,
SelectItem,
SelectLabel,
SelectSeparator,
SelectTrigger,
SelectValue,
} from "@/components";
import { LayoutName } from "@/components/Graph/helpers/layoutConfig";
import { cn } from "@/utils";
import { atom, useAtom } from "jotai";
import { ComponentPropsWithRef } from "react";

export const graphLayoutSelectionAtom = atom<LayoutName>("F_COSE");

export function SelectLayout({
className,
...selectTriggerProps
}: ComponentPropsWithRef<typeof SelectTrigger>) {
const [value, setValue] = useAtom(graphLayoutSelectionAtom);
return (
<Select
value={value}
onValueChange={value => setValue(value as LayoutName)}
>
<SelectTrigger
className={cn("h-11 py-1", className)}
{...selectTriggerProps}
>
<div className="flex flex-col items-start justify-center gap-0">
<div className="text-text-secondary text-xs leading-none">Layout</div>
<SelectValue placeholder="Select a layout" />
</div>
</SelectTrigger>
<SelectContent>
<SelectItem value="F_COSE">Force Directed (F0Cose)</SelectItem>
<SelectItem value="D3">Force Directed (D3)</SelectItem>
<SelectItem value="CONCENTRIC">Concentric</SelectItem>
<SelectSeparator />
<SelectGroup>
<SelectLabel>Klay</SelectLabel>
<SelectItem value="KLAY_LR">Left to Right</SelectItem>
<SelectItem value="KLAY_TB">Top to Bottom</SelectItem>
</SelectGroup>
<SelectSeparator />
<SelectGroup>
<SelectLabel>Hierarchical</SelectLabel>
<SelectItem value="DAGRE_LR">Left to Right</SelectItem>
<SelectItem value="DAGRE_RL">Right to Left</SelectItem>
<SelectItem value="DAGRE_TB">Top to Bottom</SelectItem>
<SelectItem value="DAGRE_BT">Bottom to Top</SelectItem>
</SelectGroup>
<SelectSeparator />
<SelectGroup>
<SelectLabel>Subway</SelectLabel>
<SelectItem value="SUBWAY_LR">Left to Right</SelectItem>
<SelectItem value="SUBWAY_RL">Right to Left</SelectItem>
<SelectItem value="SUBWAY_TB">Top to Bottom</SelectItem>
<SelectItem value="SUBWAY_BT">Bottom to Top</SelectItem>
</SelectGroup>
</SelectContent>
</Select>
);
}