diff --git a/lib/live_debugger/components/tree.ex b/lib/live_debugger/components/tree.ex
index a51b79df..b1530eec 100644
--- a/lib/live_debugger/components/tree.ex
+++ b/lib/live_debugger/components/tree.ex
@@ -7,10 +7,13 @@ defmodule LiveDebugger.Components.Tree do
alias LiveDebugger.Structs.TreeNode
+ @max_node_number 20
+
@doc """
Tree component which show nested tree of live view and live components.
You need to pass TreeNode struct to render the tree.
This component emits `select_node` event with 'node_id` param to the `event_target` when a node is clicked. `node_id` is parsed.
+ To calculate `max_opened_node_level` it uses `max_nesting_level/2` function.
"""
attr(:tree_node, :any, required: true, doc: "The TreeNode struct to render")
@@ -19,6 +22,11 @@ defmodule LiveDebugger.Components.Tree do
attr(:selected_node_id, :string, required: true, doc: "The id of the selected node")
attr(:class, :string, default: nil, doc: "CSS class")
+ attr(:max_opened_node_level, :integer,
+ required: true,
+ doc: "The maximum level of the tree to be opened"
+ )
+
def tree(assigns) do
~H"""
<.card class={["h-max bg-gray-200 text-primary", @class]}>
@@ -29,17 +37,42 @@ defmodule LiveDebugger.Components.Tree do
selected_node_id={@selected_node_id}
event_target={@event_target}
root?={true}
+ max_opened_node_level={@max_opened_node_level}
+ level={0}
/>
"""
end
+ @doc """
+ Calculates the maximum level to be opened in the tree.
+ """
+ @spec max_opened_node_level(root_node :: TreeNode.t(), max_nodes :: integer()) :: integer()
+ def max_opened_node_level(root_node, max_nodes \\ @max_node_number) do
+ node_count = count_by_level(root_node)
+
+ node_count
+ |> Enum.reduce_while({0, 0}, fn {level, count}, acc ->
+ {_, parent_count} = acc
+ new_count = count + parent_count
+
+ if new_count > max_nodes do
+ {:halt, {level - 1, new_count}}
+ else
+ {:cont, {level, new_count}}
+ end
+ end)
+ |> elem(0)
+ end
+
attr(:tree_node, :any, required: true)
attr(:event_target, :any, required: true)
attr(:selected_node_id, :string, default: nil)
attr(:root?, :boolean, default: false)
attr(:highlight_bar?, :boolean, default: false)
+ attr(:max_opened_node_level, :integer, default: 0)
+ attr(:level, :integer, default: 0)
defp tree_node(assigns) do
assigns =
@@ -47,6 +80,7 @@ defmodule LiveDebugger.Components.Tree do
|> assign(:tree_node, format_tree_node(assigns.tree_node))
|> assign(:collapsible?, length(assigns.tree_node.children) > 0)
|> assign(:selected?, TreeNode.id(assigns.tree_node) == assigns.selected_node_id)
+ |> assign(:open, assigns.level < assigns.max_opened_node_level)
~H"""
@@ -57,7 +91,7 @@ defmodule LiveDebugger.Components.Tree do
:if={@collapsible?}
id={"collapsible-" <> @tree_node.parsed_id}
chevron_class="text-primary h-5 w-5"
- open={true}
+ open={@open}
class="w-full"
>
<:label>
@@ -71,6 +105,8 @@ defmodule LiveDebugger.Components.Tree do
event_target={@event_target}
root?={false}
highlight_bar?={@selected?}
+ max_opened_node_level={@max_opened_node_level}
+ level={@level + 1}
/>
@@ -155,4 +191,12 @@ defmodule LiveDebugger.Components.Tree do
|> String.split(".")
|> List.last()
end
+
+ defp count_by_level(node, level \\ 0, acc \\ %{}) do
+ acc = Map.update(acc, level, 1, &(&1 + 1))
+
+ Enum.reduce(node.children, acc, fn child, acc ->
+ count_by_level(child, level + 1, acc)
+ end)
+ end
end
diff --git a/lib/live_debugger/live_components/sidebar.ex b/lib/live_debugger/live_components/sidebar.ex
index 413ba26b..6abe9e39 100644
--- a/lib/live_debugger/live_components/sidebar.ex
+++ b/lib/live_debugger/live_components/sidebar.ex
@@ -74,6 +74,7 @@ defmodule LiveDebugger.LiveComponents.Sidebar do
pid={@pid}
socket_id={@socket_id}
tree={@tree}
+ max_opened_node_level={@max_opened_node_level}
node_id={@node_id}
myself={@myself}
/>
@@ -91,6 +92,7 @@ defmodule LiveDebugger.LiveComponents.Sidebar do
pid={@pid}
socket_id={@socket_id}
tree={@tree}
+ max_opened_node_level={@max_opened_node_level}
node_id={@node_id}
myself={@myself}
/>
@@ -137,13 +139,19 @@ defmodule LiveDebugger.LiveComponents.Sidebar do
attr(:tree, :any, required: true)
attr(:node_id, :any, required: true)
attr(:myself, :any, required: true)
+ attr(:max_opened_node_level, :any, required: true)
defp sidebar_content(assigns) do
~H"""
<.basic_info pid={@pid} socket_id={@socket_id} />
<.separate_bar />
- <.component_tree tree={@tree} selected_node_id={@node_id} target={@myself} />
+ <.component_tree
+ tree={@tree}
+ selected_node_id={@node_id}
+ target={@myself}
+ max_opened_node_level={@max_opened_node_level}
+ />
"""
end
@@ -196,6 +204,7 @@ defmodule LiveDebugger.LiveComponents.Sidebar do
attr(:tree, :any, required: true)
attr(:target, :any, required: true)
+ attr(:max_opened_node_level, :any, required: true)
attr(:selected_node_id, :string, default: nil)
defp component_tree(assigns) do
@@ -214,6 +223,7 @@ defmodule LiveDebugger.LiveComponents.Sidebar do
tree_node={tree}
event_target={@target}
class="bg-gray-200"
+ max_opened_node_level={@max_opened_node_level.result}
/>
"""
@@ -253,10 +263,10 @@ defmodule LiveDebugger.LiveComponents.Sidebar do
defp assign_async_tree(socket) do
pid = socket.assigns.pid
- assign_async(socket, :tree, fn ->
+ assign_async(socket, [:tree, :max_opened_node_level], fn ->
with {:ok, channel_state} <- ChannelService.state(pid),
{:ok, tree} <- ChannelService.build_tree(channel_state) do
- {:ok, %{tree: tree}}
+ {:ok, %{tree: tree, max_opened_node_level: Tree.max_opened_node_level(tree)}}
else
error -> handle_error(error, pid, "Failed to build tree: ")
end