Skip to content

Commit

Permalink
Default Layout (#15)
Browse files Browse the repository at this point in the history
* Default Layout

Set and use a default layout if one or more of the nodes does not have a position.

* Update from notes in PR
  • Loading branch information
sroussey committed Jan 27, 2024
1 parent cb51661 commit c1c97e6
Show file tree
Hide file tree
Showing 3 changed files with 34 additions and 11 deletions.
27 changes: 22 additions & 5 deletions lib/NodeGraphEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
ReactFlowProps,
ReactFlowProvider,
useEdgesState,
useNodesInitialized,
useNodesState,
useReactFlow,
useStoreApi,
Expand All @@ -23,6 +24,7 @@ import {
useMemo,
JSX,
CSSProperties,
useEffect,
} from 'react'
import { defaultEdgeTypes } from './edge-types'
import { IGraphConfig } from './config'
Expand All @@ -32,15 +34,15 @@ import { ClipboardItem } from './clipboard'
import { LayoutEngine, useLayoutEngine } from './layout/layout'

type NodeGraphEditorProps = Omit<FlowProps, 'edges' | 'nodes'> & {
onSave?: (data: any) => void
onSave?: (data: any) => void,
}

export const NodeGraphEditor = forwardRef<
NodeGraphHandle,
NodeGraphEditorProps
>(
(
{ defaultNodes, defaultEdges, ...props }: NodeGraphEditorProps,
{ defaultNodes, defaultEdges, layoutEngine, ...props }: NodeGraphEditorProps,
ref,
): JSX.Element => {
const [nodes, , onNodesChange] = useNodesState(defaultNodes ?? [])
Expand Down Expand Up @@ -81,15 +83,19 @@ export const ExampleNodeGraphEditor = forwardRef<
})

type FlowProps = ReactFlowProps & {
backgroundStyles?: CSSProperties
backgroundStyles?: CSSProperties,
/**
* The default layout engine to use when nodes are provided without positions.
*/
layoutEngine?: LayoutEngine
}
export type NodeGraphHandle = {
layout: () => void
}

const Flow = forwardRef<NodeGraphHandle, FlowProps>(
(
{ defaultNodes, defaultEdges, backgroundStyles, ...props }: FlowProps,
{ backgroundStyles, layoutEngine, ...props }: FlowProps,
ref,
) => {
const nodeTypes = useNodeTypes()
Expand All @@ -98,6 +104,7 @@ const Flow = forwardRef<NodeGraphHandle, FlowProps>(
const [config] = useGraphConfig()
const { getState } = useStoreApi()
const { setNodes, setEdges } = useReactFlow()


// Handle clipboard events
useHotkeys(
Expand All @@ -110,7 +117,7 @@ const Flow = forwardRef<NodeGraphHandle, FlowProps>(
)

// Provide methods to parent components
const layout = useLayoutEngine(LayoutEngine.Dagre)
const layout = useLayoutEngine(layoutEngine ?? LayoutEngine.Dagre)
useImperativeHandle(
ref,
() => ({
Expand All @@ -119,6 +126,16 @@ const Flow = forwardRef<NodeGraphHandle, FlowProps>(
[],
)

const initialized = useNodesInitialized()
useEffect(() => {
const shouldLayout = !!getState().nodes.find(
(node) => node.position == undefined,
)
if (initialized && shouldLayout) {
layout()
}
}, [initialized])

return (
<div
style={{
Expand Down
4 changes: 2 additions & 2 deletions lib/NodeGraphEditorInputGroups.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ export const InputGroups: Story = {
{
id: '1',
type: 'bsdf',
position: { x: 350, y: 100 },
// position: { x: 350, y: 100 },
data: {
__inputGroupsExpanded: ['Specular'],
},
Expand All @@ -251,7 +251,7 @@ export const InputGroups: Story = {
position: { x: 100, y: 300 },
data: {},
},
],
] as Node[],
edges: [
{
id: 'e1',
Expand Down
14 changes: 10 additions & 4 deletions lib/layout/layout.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useCallback } from 'react'
import { computeDagreLayout } from './dagre'
import { Instance, Node, useReactFlow } from '@xyflow/react'
import { Edge, Instance, Node, useReactFlow } from '@xyflow/react'

export enum LayoutEngine {
Dagre,
Expand All @@ -20,10 +20,16 @@ function computeLayout(
getNodes: Instance.GetNodes<any>,
getEdges: Instance.GetEdges<any>,
): Node[] {
const layoutFn = getLayoutFunction(engine)
if (!layoutFn) throw new Error(`Unknown layout engine ${engine}`)
return layoutFn(getNodes(), getEdges())
}

export function getLayoutFunction(
engine: LayoutEngine | undefined,
): ((nodes: Node[], edges: Edge[]) => Node[]) | undefined {
switch (engine) {
case LayoutEngine.Dagre:
return computeDagreLayout(getNodes(), getEdges())
default:
throw new Error(`Unknown layout engine ${engine}`)
return computeDagreLayout
}
}

0 comments on commit c1c97e6

Please sign in to comment.