lib/live_admin/components/nav.ex

defmodule LiveAdmin.Components.Nav do
  use Phoenix.LiveComponent
  use Phoenix.HTML

  import LiveAdmin,
    only: [resource_title: 1, route_with_params: 2, trans: 1]

  @impl true
  def render(assigns) do
    nested_resources =
      Enum.reduce(assigns.resources, %{}, fn {key, resource}, groups ->
        path =
          resource.__live_admin_config__(:schema)
          |> Module.split()
          |> case do
            list when length(list) == 1 -> list
            list -> Enum.drop(list, -1)
          end
          |> Enum.map(&Access.key(&1, %{}))

        update_in(groups, path, fn subs -> Map.put(subs, {key, resource}, %{}) end)
      end)

    assigns = assign(assigns, :nested_resources, nested_resources)

    ~H"""
    <div class="nav">
      <ul class="nav__list">
        <li class="nav__item--group"><%= @title %></li>
        <li class="nav__item--group">
          <.link navigate={@base_path}><%= trans("Home") %></.link>
        </li>
        <li class="nav__item--group">
          <.nav_group
            items={@nested_resources}
            base_path={@base_path}
            current_resource={assigns[:resource]}
          />
        </li>
        <li class="nav__item--group">
          <.link navigate={route_with_params(assigns, resource_path: "session")}>
            <%= trans("Session") %>
          </.link>
        </li>
      </ul>
    </div>
    """
  end

  defp nav_group(assigns) do
    ~H"""
    <ul>
      <%= for {parent, children} <- Enum.sort(@items) do %>
        <%= if match?({_key, _resource}, parent) do %>
          <li class={"nav__item#{if elem(parent, 1) == @current_resource, do: "--selected"}"}>
            <.link navigate={route_with_params(assigns, resource_path: elem(parent, 0))}>
              <%= resource_title(elem(parent, 1)) %>
            </.link>
          </li>
        <% else %>
          <li class="nav__item--drop">
            <input type="checkbox" id={"menu-group-#{parent}"} checked={open?(assigns, parent)} />
            <label for={"menu-group-#{parent}"}><%= parent %></label>
            <.nav_group items={children} base_path={@base_path} current_resource={@current_resource} />
          </li>
        <% end %>
      <% end %>
    </ul>
    """
  end

  defp open?(assigns, schema) do
    assigns.current_resource
    |> case do
      nil ->
        true

      resource ->
        resource.__live_admin_config__(:schema)
        |> Module.split()
        |> Enum.drop(-1)
        |> Enum.member?(schema)
    end
  end
end