Skip to content

Commit

Permalink
collapse tree on specific level when number of children is too big (#83)
Browse files Browse the repository at this point in the history
* collapse tree on specific level when number of children is too big

* calculate max_nesting_level only on tree rebuild

* improve max_opened_node_level function
  • Loading branch information
GuzekAlan authored Feb 14, 2025
1 parent c754120 commit 3acab52
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 4 deletions.
46 changes: 45 additions & 1 deletion lib/live_debugger/components/tree.ex
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand All @@ -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]}>
Expand All @@ -29,24 +37,50 @@ 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}
/>
</div>
</.card>
"""
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 =
assigns
|> 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"""
<div class="relative flex max-w-full">
Expand All @@ -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>
Expand All @@ -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}
/>
</div>
</.collapsible>
Expand Down Expand Up @@ -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
16 changes: 13 additions & 3 deletions lib/live_debugger/live_components/sidebar.ex
Original file line number Diff line number Diff line change
Expand Up @@ -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}
/>
Expand All @@ -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}
/>
Expand Down Expand Up @@ -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"""
<div class="flex flex-col gap-2 p-2">
<.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}
/>
</div>
"""
end
Expand Down Expand Up @@ -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
Expand All @@ -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}
/>
</.async_result>
"""
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit 3acab52

Please sign in to comment.