defmodule AshHqWeb.Components.TreeView do @moduledoc """ A tree view with collapsable nodes. The component must be supplied with a list of `%Item{}` structs defining the behaviour of each node in the tree. """ use Surface.Component alias AshHqWeb.Components.TreeView.Item alias Surface.Components.LivePatch alias Phoenix.LiveView.JS alias Surface.Components.LivePatch @doc "DOM id for the outer div" prop id, :string, required: true @doc "Any additional CSS classes to add to the outer div" prop class, :css_class @doc "`TreeView.Item` nodes to display" slot default def render(assigns) do ~F"""
""" end defmodule Item do @moduledoc """ Data for an item in the TreeView. """ use Surface.Component alias __MODULE__ prop path, :string, from_context: {Item, :path} @doc "Logical name for the tree view item. Combined with the parent path to build the DOM id." prop name, :string, default: nil @doc "Text to display for the item." prop text, :string, default: "" @doc "Optional icon to display beside text" prop icon, :any @doc "Event handler to run when item clicked, eg JS.patch(~p'/some/path')" prop to, :string @doc "When true, allows the item's children to be hidden with a chevron icon." prop collapsable, :boolean, default: false @doc "The initial collapsed state of the children items." prop collapsed, :boolean, default: false @doc "The initial selection state of the item." prop selected, :boolean, default: false @doc "When true, displays an indentation guide to align deeply nested items." prop indent_guide, :boolean, default: false @doc "Any additional classes to add to the `
  • ` element for the item." prop class, :css_class, default: nil @doc "Children `TreeView.Item` nodes." slot default def render(assigns) do ~F"""
  • {#if @to}
    {#if @icon}{@icon}{/if} {@text}
    {#else} {/if}
  • """ end defp handle_click(js \\ %Phoenix.LiveView.JS{}, id, collapsable) do if collapsable, do: toggle_class(js, "collapsed", to: "##{id}"), else: js end defp toggle_class(js, class, to: selector) do js |> JS.add_class(class, to: "#{selector}:not(.#{class})") |> JS.remove_class(class, to: "#{selector}.#{class}") end end end