LiveFlow.State (LiveFlow v0.3.2)

Copy Markdown View Source

Main state container for LiveFlow.

The state holds all nodes, edges, viewport information, and selection state. It provides CRUD operations for nodes and edges, as well as selection management.

Fields

  • :nodes - Map of node ID to LiveFlow.Node structs
  • :edges - Map of edge ID to LiveFlow.Edge structs
  • :viewport - Current LiveFlow.Viewport state
  • :selected_nodes - Set of selected node IDs
  • :selected_edges - Set of selected edge IDs

Examples

iex> state = LiveFlow.State.new()
iex> node = LiveFlow.Node.new("1", %{x: 0, y: 0}, %{label: "Start"})
iex> state = LiveFlow.State.add_node(state, node)
iex> Map.keys(state.nodes)
["1"]

Summary

Functions

Nodes ready to be drawn: position is replaced by the absolute position, and nodes inside a hidden parent are hidden too.

Position of a node in flow coordinates. Child nodes store their position relative to their parent; this adds up the positions of all ancestors.

Adds an edge to the state.

Adds multiple edges to the state.

Adds a node to the state.

Adds multiple nodes to the state.

Calculates the bounding box of the nodes in flow coordinates.

Returns the direct children of a node (nodes whose parent_id is id).

Clears all selection (nodes and edges).

Edges connected to any of the given node IDs.

Deletes all selected nodes and edges (skipping those with deletable: false).

Number of ancestors of a node (0 for top-level nodes).

Returns the IDs of all descendants of a node (children, grandchildren, ...).

Deselects a node by ID.

Checks if an edge already exists between two nodes.

Gets all edges connected to a node.

Gets all edges as a list.

Gets an edge by ID.

Gets a node by ID.

Nodes with an edge pointing to id.

Nodes intersecting a node (by ID) or a rect %{x, y, width, height}.

Creates a new empty state.

Rectangle of a node in flow coordinates (absolute position and size). Unmeasured nodes count as 100 x 40.

Gets all nodes as a list.

Nodes id has an edge to.

Reconnects one end of an edge to another node/handle.

Removes an edge by ID.

Removes multiple edges by ID.

Removes a node by ID, together with its child nodes (sub-flows) and every edge connected to any of them.

Removes multiple nodes by ID.

Selects all nodes and edges.

Selects an edge by ID.

Selects a node by ID.

Selects multiple nodes by ID.

Gets all selected edges.

Gets all selected nodes.

Moves a node into parent_id (or out of any parent with nil), keeping its absolute position on screen.

Sets the viewport.

Updates an edge by ID with the given attributes.

Updates a node by ID with the given attributes or function.

Updates the viewport with the given values.

Types

t()

@type t() :: %LiveFlow.State{
  edges: %{required(String.t()) => LiveFlow.Edge.t()},
  nodes: %{required(String.t()) => LiveFlow.Node.t()},
  selected_edges: MapSet.t(String.t()),
  selected_nodes: MapSet.t(String.t()),
  viewport: LiveFlow.Viewport.t()
}

Functions

absolute_nodes(state)

@spec absolute_nodes(t()) :: %{required(String.t()) => LiveFlow.Node.t()}

Nodes ready to be drawn: position is replaced by the absolute position, and nodes inside a hidden parent are hidden too.

absolute_position(state, id)

@spec absolute_position(t(), LiveFlow.Node.t() | String.t()) ::
  %{x: number(), y: number()} | nil

Position of a node in flow coordinates. Child nodes store their position relative to their parent; this adds up the positions of all ancestors.

add_edge(state, edge)

@spec add_edge(t(), LiveFlow.Edge.t()) :: t()

Adds an edge to the state.

add_edges(state, edges)

@spec add_edges(t(), [LiveFlow.Edge.t()]) :: t()

Adds multiple edges to the state.

add_node(state, node)

@spec add_node(t(), LiveFlow.Node.t()) :: t()

Adds a node to the state.

add_nodes(state, nodes)

@spec add_nodes(t(), [LiveFlow.Node.t()]) :: t()

Adds multiple nodes to the state.

bounds(state, opts \\ [])

@spec bounds(
  t(),
  keyword()
) :: %{x: number(), y: number(), width: number(), height: number()} | nil

Calculates the bounding box of the nodes in flow coordinates.

Each node contributes its position plus its width/height (measured or explicitly set). Nodes without dimensions count as a point.

Options

  • :nodes - Only include these node IDs. Unknown IDs are ignored, so they never stretch the bounds to the origin.
  • :include_hidden - Include hidden nodes (default: false)

Returns nil when no node matches.

Examples

iex> state = LiveFlow.State.new(nodes: [
...>   LiveFlow.Node.new("a", %{x: 0, y: 0}, %{}, width: 100, height: 50),
...>   LiveFlow.Node.new("b", %{x: 200, y: 100}, %{}, width: 50, height: 50)
...> ])
iex> LiveFlow.State.bounds(state)
%{x: 0.0, y: 0.0, width: 250.0, height: 150.0}
iex> LiveFlow.State.bounds(state, nodes: ["b", "missing"])
%{x: 200.0, y: 100.0, width: 50.0, height: 50.0}

children(state, id)

@spec children(t(), String.t()) :: [LiveFlow.Node.t()]

Returns the direct children of a node (nodes whose parent_id is id).

clear_selection(state)

@spec clear_selection(t()) :: t()

Clears all selection (nodes and edges).

connected_edges(state, ids)

@spec connected_edges(t(), [String.t()]) :: [LiveFlow.Edge.t()]

Edges connected to any of the given node IDs.

delete_selected(state)

@spec delete_selected(t()) :: t()

Deletes all selected nodes and edges (skipping those with deletable: false).

depth(state, node)

@spec depth(t(), LiveFlow.Node.t()) :: non_neg_integer()

Number of ancestors of a node (0 for top-level nodes).

descendant_ids(state, id)

@spec descendant_ids(t(), String.t()) :: [String.t()]

Returns the IDs of all descendants of a node (children, grandchildren, ...).

deselect_node(state, id)

@spec deselect_node(t(), String.t()) :: t()

Deselects a node by ID.

edge_exists?(state, source, target, source_handle \\ nil, target_handle \\ nil)

@spec edge_exists?(t(), String.t(), String.t(), String.t() | nil, String.t() | nil) ::
  boolean()

Checks if an edge already exists between two nodes.

edges_for_node(state, node_id)

@spec edges_for_node(t(), String.t()) :: [LiveFlow.Edge.t()]

Gets all edges connected to a node.

edges_list(state)

@spec edges_list(t()) :: [LiveFlow.Edge.t()]

Gets all edges as a list.

get_edge(state, id)

@spec get_edge(t(), String.t()) :: LiveFlow.Edge.t() | nil

Gets an edge by ID.

get_node(state, id)

@spec get_node(t(), String.t()) :: LiveFlow.Node.t() | nil

Gets a node by ID.

incomers(state, id)

@spec incomers(t(), String.t()) :: [LiveFlow.Node.t()]

Nodes with an edge pointing to id.

intersecting_nodes(state, id_or_rect, opts \\ [])

@spec intersecting_nodes(t(), String.t() | map(), keyword()) :: [LiveFlow.Node.t()]

Nodes intersecting a node (by ID) or a rect %{x, y, width, height}.

Options

  • :partial - Count partial overlaps (default: true). With false only nodes fully inside the rect are returned.

new(opts \\ [])

@spec new(keyword()) :: t()

Creates a new empty state.

Options

  • :nodes - Initial nodes (list or map)
  • :edges - Initial edges (list or map)
  • :viewport - Initial viewport

Examples

iex> LiveFlow.State.new()
%LiveFlow.State{nodes: %{}, edges: %{}}

iex> nodes = [LiveFlow.Node.new("1", %{x: 0, y: 0}, %{})]
iex> state = LiveFlow.State.new(nodes: nodes)
iex> Map.keys(state.nodes)
["1"]

node_rect(state, id)

@spec node_rect(t(), LiveFlow.Node.t() | String.t()) :: map() | nil

Rectangle of a node in flow coordinates (absolute position and size). Unmeasured nodes count as 100 x 40.

nodes_list(state)

@spec nodes_list(t()) :: [LiveFlow.Node.t()]

Gets all nodes as a list.

outgoers(state, id)

@spec outgoers(t(), String.t()) :: [LiveFlow.Node.t()]

Nodes id has an edge to.

reconnect_edge(state, params, opts \\ [])

@spec reconnect_edge(t(), map(), keyword()) :: t()

Reconnects one end of an edge to another node/handle.

Accepts the params of the lf:reconnect_edge event pushed when the user drags an edge endpoint ("edge_id", "end", "node_id", "handle_id").

Options

  • :replace_id - Give the edge a new ID (default: false)
  • :edge_id - ID (or function receiving the updated edge) used when :replace_id is true. Defaults to a unique "e-<integer>" ID.

Examples

def handle_event("lf:reconnect_edge", params, socket) do
  {:noreply, assign(socket, flow: State.reconnect_edge(socket.assigns.flow, params))}
end

remove_edge(state, id)

@spec remove_edge(t(), String.t()) :: t()

Removes an edge by ID.

remove_edges(state, ids)

@spec remove_edges(t(), [String.t()]) :: t()

Removes multiple edges by ID.

remove_node(state, id)

@spec remove_node(t(), String.t()) :: t()

Removes a node by ID, together with its child nodes (sub-flows) and every edge connected to any of them.

remove_nodes(state, ids)

@spec remove_nodes(t(), [String.t()]) :: t()

Removes multiple nodes by ID.

select_all(state)

@spec select_all(t()) :: t()

Selects all nodes and edges.

select_edge(state, id, opts \\ [])

@spec select_edge(t(), String.t(), keyword()) :: t()

Selects an edge by ID.

select_node(state, id, opts \\ [])

@spec select_node(t(), String.t(), keyword()) :: t()

Selects a node by ID.

Options

  • :multi - If true, adds to selection. If false, replaces selection (default: false)

select_nodes(state, ids)

@spec select_nodes(t(), [String.t()]) :: t()

Selects multiple nodes by ID.

selected_edges_list(state)

@spec selected_edges_list(t()) :: [LiveFlow.Edge.t()]

Gets all selected edges.

selected_nodes_list(state)

@spec selected_nodes_list(t()) :: [LiveFlow.Node.t()]

Gets all selected nodes.

set_parent(state, id, parent_id, opts \\ [])

@spec set_parent(t(), String.t(), String.t() | nil, keyword()) :: t()

Moves a node into parent_id (or out of any parent with nil), keeping its absolute position on screen.

set_viewport(state, viewport)

@spec set_viewport(t(), LiveFlow.Viewport.t()) :: t()

Sets the viewport.

update_edge(state, id, attrs)

@spec update_edge(t(), String.t(), keyword()) :: t()

Updates an edge by ID with the given attributes.

update_node(state, id, fun)

@spec update_node(
  t(),
  String.t(),
  keyword() | (LiveFlow.Node.t() -> LiveFlow.Node.t())
) :: t()

Updates a node by ID with the given attributes or function.

When passed a keyword list, updates the node with those attributes. When passed a function, applies the function to the node.

update_viewport(state, attrs)

@spec update_viewport(t(), map()) :: t()

Updates the viewport with the given values.