1- import React , { useEffect , useMemo , useState } from "react" ;
1+ import React , { useEffect , useMemo } from "react" ;
22
3- import { Select , SelectOption , ThemeProvider } from "@gravity-ui/uikit" ;
3+ import { ThemeProvider } from "@gravity-ui/uikit" ;
4+ import { Description , Meta as StorybookMeta , Title } from "@storybook/blocks" ;
45import type { Meta , StoryFn } from "@storybook/react" ;
5- import ELK from "elkjs" ;
6-
7- import { Graph , GraphCanvas , GraphState , TBlock , TConnection , useGraph , useGraphEvent } from "../../../index" ;
6+ import ELK , { ElkNode } from "elkjs" ;
7+
8+ import {
9+ Graph ,
10+ GraphCanvas ,
11+ GraphState ,
12+ TBlock ,
13+ TConnection ,
14+ TGraphConfig ,
15+ useGraph ,
16+ useGraphEvent ,
17+ } from "../../../index" ;
818import { MultipointConnection , useElk } from "../../../plugins" ;
919import { TMultipointConnection } from "../../../plugins/elk/types" ;
1020import { useFn } from "../../../utils/hooks/useFn" ;
@@ -27,10 +37,12 @@ export enum Algorithm {
2737
2838import "@gravity-ui/uikit/styles/styles.css" ;
2939
30- const GraphApp = ( ) => {
31- const [ algorithm , setAlgorithm ] = useState ( Algorithm . Layered ) ;
32- const [ algorithms , setAlgorithms ] = useState < SelectOption [ ] > ( [ ] ) ;
40+ interface GraphAppProps {
41+ elkConfig ?: ElkNode ;
42+ graphConfig ?: TGraphConfig ;
43+ }
3344
45+ const GraphApp = ( { elkConfig, graphConfig } : GraphAppProps ) => {
3446 const elk = useMemo ( ( ) => new ELK ( ) , [ ] ) ;
3547
3648 const { graph, setEntities, start } = useGraph ( {
@@ -39,10 +51,6 @@ const GraphApp = () => {
3951 } ,
4052 } ) ;
4153
42- const { elkConfig, graphConfig } = useMemo ( ( ) => {
43- return getExampleConfig ( algorithm ) ;
44- } , [ algorithm ] ) ;
45-
4654 const { isLoading, result } = useElk ( elkConfig , elk ) ;
4755
4856 useEffect ( ( ) => {
@@ -75,18 +83,6 @@ const GraphApp = () => {
7583 graph . zoomTo ( "center" , { padding : 300 } ) ;
7684 } , [ isLoading , result ] ) ;
7785
78- useEffect ( ( ) => {
79- elk . knownLayoutAlgorithms ( ) . then ( ( knownLayoutAlgorithms ) => {
80- setAlgorithms (
81- knownLayoutAlgorithms . map ( ( knownLayoutAlgorithm ) => {
82- const { id, name } = knownLayoutAlgorithm ;
83- const algId = id . split ( "." ) . at ( - 1 ) ;
84- return { value : algId , content : name } ;
85- } )
86- ) ;
87- } ) ;
88- } , [ elk ] ) ;
89-
9086 useGraphEvent ( graph , "state-change" , ( { state } ) => {
9187 if ( state === GraphState . ATTACHED ) {
9288 start ( ) ;
@@ -99,17 +95,130 @@ const GraphApp = () => {
9995
10096 return (
10197 < ThemeProvider theme = { "light" } >
102- < Select value = { [ algorithm ] } options = { algorithms } onUpdate = { ( v ) => setAlgorithm ( v [ 0 ] as Algorithm ) } > </ Select >
103- < GraphCanvas className = "graph" graph = { graph } renderBlock = { renderBlockFn } /> ;
98+ < GraphCanvas className = "graph" graph = { graph } renderBlock = { renderBlockFn } />
10499 </ ThemeProvider >
105100 ) ;
106101} ;
107102
108- const meta : Meta = {
103+ const meta : Meta < typeof GraphApp > = {
109104 title : "Plugins/ELK" ,
110105 component : GraphApp ,
106+ tags : [ "autodocs" ] ,
107+ parameters : {
108+ docs : {
109+ page : ( ) => (
110+ < >
111+ < StorybookMeta />
112+ < Title />
113+ < Description />
114+ </ >
115+ ) ,
116+ description : {
117+ component :
118+ "The useElk hook is used to compute graph layouts using the ELK (Eclipse Layout Kernel) layout engine. It " +
119+ "provides a React-friendly way to integrate ELK.js into your application.\n\n" +
120+ "## Usage\n\n" +
121+ "```tsx\n" +
122+ "const { isLoading, result } = useElk(elkConfig, elk);\n" +
123+ "```\n\n" +
124+ "### Parameters\n\n" +
125+ "- `elkConfig`: ElkNode configuration object that describes the graph structure and layout options\n" +
126+ "- `elk`: Instance of the ELK layout engine\n\n" +
127+ "### Returns\n\n" +
128+ "- `isLoading`: Boolean indicating if the layout computation is in progress\n" +
129+ "- `result`: The computed layout result containing node positions and edge routing information\n\n" +
130+ "## Layout Algorithms\n\n" +
131+ "ELK.js supports several layout algorithms that can be specified in the elkConfig:\n\n" +
132+ "- `box`: Pack the nodes like boxes\n" +
133+ "- `layered`: Hierarchical layout, good for DAGs and trees\n" +
134+ "- `force`: Force-directed layout\n" +
135+ "- `stress`: Stress-minimizing layout\n" +
136+ "- `mrtree`: Traditional tree layout\n" +
137+ "- `radial`: Radial layout\n" +
138+ "- `random`: Random node placement\n\n" +
139+ "## Example\n\n" +
140+ "```tsx\n" +
141+ "import React from 'react';\n" +
142+ "import { GraphCanvas, useGraph, useElk } from '@gravity-ui/graph';\n" +
143+ "import ELK from 'elkjs';\n\n" +
144+ "const elkConfig = {\n" +
145+ ' id: "root",\n' +
146+ " children: [\n" +
147+ ' { id: "n1", width: 30, height: 30 },\n' +
148+ ' { id: "n2", width: 30, height: 30 }\n' +
149+ " ],\n" +
150+ " edges: [\n" +
151+ ' { id: "e1", sources: ["n1"], targets: ["n2"] }\n' +
152+ " ],\n" +
153+ " layoutOptions: {\n" +
154+ " 'algorithm': 'layered',\n" +
155+ " 'elk.direction': 'DOWN',\n" +
156+ " 'elk.spacing.nodeNode': '50'\n" +
157+ " }\n" +
158+ "};\n\n" +
159+ "function MyGraphWithElk() {\n" +
160+ " const elk = React.useMemo(() => new ELK(), []);\n" +
161+ " const { graph } = useGraph();\n\n" +
162+ " const { isLoading, result } = useElk(elkConfig, elk);\n\n" +
163+ " React.useEffect(() => {\n" +
164+ " if (!isLoading && result) {\n" +
165+ " graph.setEntities({\n" +
166+ " blocks: result.blocks,\n" +
167+ " connections: result.edges\n" +
168+ " });\n" +
169+ " }\n" +
170+ " }, [isLoading, result]);\n\n" +
171+ " return <GraphCanvas graph={graph} />;\n" +
172+ "}\n" +
173+ "```\n\n" +
174+ "## Resources\n\n" +
175+ "- [ELK.js on GitHub](https://github.com/kieler/elkjs)\n" +
176+ "- [ELK Documentation](https://eclipse.dev/elk/documentation.html)\n" +
177+ "- [ELK Algorithm Documentation](https://eclipse.dev/elk/reference/algorithms.html)" ,
178+ } ,
179+ } ,
180+ } ,
181+ argTypes : {
182+ elkConfig : {
183+ control : "object" ,
184+ description : "ELK layout configuration object" ,
185+ } ,
186+ graphConfig : {
187+ control : "object" ,
188+ description : "Graph configuration with blocks and connections" ,
189+ } ,
190+ } ,
111191} ;
112192
113193export default meta ;
114194
115- export const Default : StoryFn = ( ) => < GraphApp /> ;
195+ // Create a story for each algorithm
196+ export const Box : StoryFn < typeof GraphApp > = ( args ) => < GraphApp { ...args } /> ;
197+ Box . args = getExampleConfig ( Algorithm . Box ) ;
198+
199+ export const Layered : StoryFn < typeof GraphApp > = ( args ) => < GraphApp { ...args } /> ;
200+ Layered . args = getExampleConfig ( Algorithm . Layered ) ;
201+
202+ export const Disco : StoryFn < typeof GraphApp > = ( args ) => < GraphApp { ...args } /> ;
203+ Disco . args = getExampleConfig ( Algorithm . Disco ) ;
204+
205+ export const Radial : StoryFn < typeof GraphApp > = ( args ) => < GraphApp { ...args } /> ;
206+ Radial . args = getExampleConfig ( Algorithm . Radial ) ;
207+
208+ export const MrTree : StoryFn < typeof GraphApp > = ( args ) => < GraphApp { ...args } /> ;
209+ MrTree . args = getExampleConfig ( Algorithm . MrTree ) ;
210+
211+ export const Force : StoryFn < typeof GraphApp > = ( args ) => < GraphApp { ...args } /> ;
212+ Force . args = getExampleConfig ( Algorithm . Force ) ;
213+
214+ export const Stress : StoryFn < typeof GraphApp > = ( args ) => < GraphApp { ...args } /> ;
215+ Stress . args = getExampleConfig ( Algorithm . Stress ) ;
216+
217+ export const Random : StoryFn < typeof GraphApp > = ( args ) => < GraphApp { ...args } /> ;
218+ Random . args = getExampleConfig ( Algorithm . Random ) ;
219+
220+ export const SporeOverlap : StoryFn < typeof GraphApp > = ( args ) => < GraphApp { ...args } /> ;
221+ SporeOverlap . args = getExampleConfig ( Algorithm . SporeOverlap ) ;
222+
223+ export const SporeCompaction : StoryFn < typeof GraphApp > = ( args ) => < GraphApp { ...args } /> ;
224+ SporeCompaction . args = getExampleConfig ( Algorithm . SporeCompaction ) ;
0 commit comments