I already have the graph details in a Javascript object so I feed them into dagre and then extract the resulting layout. I send that back into elm via a port which decodes to these types:
import Json.Decode exposing (bool, field, float, int, list, maybe, string)
import Json.Decode.Pipeline exposing (decode, hardcoded, optional, required, requiredAt)
type NodeType
= QuestionNode Int
| EndNode
| NewSectionNode String
type alias NodeLayout =
{ type_ : NodeType
, x : Float
, y : Float
}
type alias EdgeLayout =
{ x : Float
, y : Float
, label : String
, points : List { x : Float, y : Float }
}
type alias GraphLayout =
{ nodes : List NodeLayout
, edges : List EdgeLayout
, width : Float
, height : Float
, offsetX : Float
}
graphLayoutDecoder : Json.Decode.Decoder GraphLayout
graphLayoutDecoder =
let
nodeTypeDecoder =
let
decodeType type_ =
case type_ of
"node" ->
Json.Decode.map QuestionNode (field "id" int)
"new-section" ->
Json.Decode.map NewSectionNode (field "name" string)
"end-of-process" ->
Json.Decode.succeed EndNode
_ ->
Json.Decode.fail "Unknown Node Type"
in
field "type" string |> Json.Decode.andThen decodeType
nodeLayoutDecoder =
decode NodeLayout
|> required "type" nodeTypeDecoder
|> required "x" float
|> required "y" float
pointDecoder =
decode (\x y -> { x = x, y = y })
|> required "x" float
|> required "y" float
edgeLayoutDecoder =
decode EdgeLayout
|> required "x" float
|> required "y" float
|> required "label" string
|> required "points" (list pointDecoder)
in
decode GraphLayout
|> required "nodes" (list nodeLayoutDecoder)
|> required "edges" (list edgeLayoutDecoder)
|> required "width" float
|> required "height" float
|> required "offsetX" float
But I'm afraid the project is commercial and not open sourced so I can't share much more. Happy to answer questions if I can.