Tree

Renders a hierarchical list as a tree.

Read more Read less

A good use case for this component is a folder structure. For navigation and other menus, a regular nested list should be preferred.

Maturity: Developing

Missing features

  • Selecting a node. The component renders aria-selected from the selected attribute and never changes it, so the caller has to.

Usage

<.tree id="dog-tree" label="Dogs">
  <.tree_item>
    Breeds
    <:items>
      <.tree_item>Golden Retriever</.tree_item>
      <.tree_item>Labrador Retriever</.tree_item>
    </:items>
  </.tree_item>
  <.tree_item>
    Characteristics
    <:items>
      <.tree_item>Playful</.tree_item>
      <.tree_item>Loyal</.tree_item>
    </:items>
  </.tree_item>
</.tree>

From data

A tree_item with an :items slot is a branch, even if the slot is empty. If you render the tree from data, conditionally hide the slot with an :if attribute.

<.tree id="breed-tree" label="Breeds">
  <.breed_node :for={node <- @nodes} node={node} />
</.tree>
defp breed_node(assigns) do
  ~H"""
  <.tree_item>
    {@node.name}
    <:items :if={@node.children != []}>
      <.breed_node :for={child <- @node.children} node={child} />
    </:items>
  </.tree_item>
  """
end

CSS

You can target the wrapper with an attribute selector for the role:

[role="tree"] {}

This component needs the Doggo.Tree JavaScript hook. See Phoenix LiveView Hooks for registering it.

Keyboard

  • Down and Up - move between the visible items. A collapsed branch takes its children out of the sequence.
  • Right - expand the focused branch, or move to its first child if it is already expanded.
  • Left - collapse the focused branch, or move to its parent if it is already collapsed or is a leaf.
  • Home and End - first and last visible item.
  • Printable characters - move to the next visible item whose label starts with what you type.

The tree is a single tab stop. Expanding and collapsing is the hook's own state, and it survives a LiveView patch. Selecting an item is not: the component renders aria-selected from the selected attribute of tree_item, so wire your own click or key handler and re-render.

  • Breeds
    • Sporting
      • Retrievers
        • Golden Retriever
        • Labrador Retriever
      • Irish Setter
  • Characteristics
    • Playful
    • Loyal
<div style="inline-size: 100%">
  <.tree id="tree-single-default" label="Dogs">
    <.tree_item>
      Breeds
      <:items>
        <.tree_item>
          Sporting
          <:items>
            <.tree_item>
              Retrievers
              <:items>
                <.tree_item>Golden Retriever</.tree_item>
                <.tree_item>Labrador Retriever</.tree_item>
              </:items>
            </.tree_item>
            <.tree_item>Irish Setter</.tree_item>
          </:items>
        </.tree_item>
        <.tree_item expanded={false}>
          Working
          <:items>
            <.tree_item>Boxer</.tree_item>
            <.tree_item>Great Dane</.tree_item>
          </:items>
        </.tree_item>
      </:items>
    </.tree_item>
    <.tree_item>
      Characteristics
      <:items>
        <.tree_item>Playful</.tree_item>
        <.tree_item>Loyal</.tree_item>
      </:items>
    </.tree_item>
  </.tree>
</div>
  • Breeds
    • Golden Retriever
    • Labrador Retriever
  • Characteristics
    • Playful
    • Loyal
<div style="inline-size: 100%">
  <.tree id="tree-single-with-icons" label="Dogs">
    <.tree_item>
      <svg
      xmlns="http://www.w3.org/2000/svg"
      width="24"
      height="24"
      viewBox="0 0 24 24"
      fill="none"
      stroke="currentColor"
      stroke-width="2"
      stroke-linecap="round"
      stroke-linejoin="round"
      class="lucide lucide-folder"
      aria-hidden="true"
    >
      <path d="M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z" />
    </svg>
     Breeds
      <:items>
        <.tree_item>
          <svg
      xmlns="http://www.w3.org/2000/svg"
      width="24"
      height="24"
      viewBox="0 0 24 24"
      fill="none"
      stroke="currentColor"
      stroke-width="2"
      stroke-linecap="round"
      stroke-linejoin="round"
      class="lucide lucide-paw-print"
      aria-hidden="true"
    >
      <circle cx="11" cy="4" r="2" />
      <circle cx="18" cy="8" r="2" />
      <circle cx="20" cy="16" r="2" />
      <path d="M9 10a5 5 0 0 1 5 5v3.5a3.5 3.5 0 0 1-6.84 1.045Q6.52 17.48 4.46 16.84A3.5 3.5 0 0 1 5.5 10Z" />
    </svg>
     Golden Retriever
        </.tree_item>
        <.tree_item>
          <svg
      xmlns="http://www.w3.org/2000/svg"
      width="24"
      height="24"
      viewBox="0 0 24 24"
      fill="none"
      stroke="currentColor"
      stroke-width="2"
      stroke-linecap="round"
      stroke-linejoin="round"
      class="lucide lucide-paw-print"
      aria-hidden="true"
    >
      <circle cx="11" cy="4" r="2" />
      <circle cx="18" cy="8" r="2" />
      <circle cx="20" cy="16" r="2" />
      <path d="M9 10a5 5 0 0 1 5 5v3.5a3.5 3.5 0 0 1-6.84 1.045Q6.52 17.48 4.46 16.84A3.5 3.5 0 0 1 5.5 10Z" />
    </svg>
     Labrador Retriever
        </.tree_item>
      </:items>
    </.tree_item>
    <.tree_item>
      <svg
      xmlns="http://www.w3.org/2000/svg"
      width="24"
      height="24"
      viewBox="0 0 24 24"
      fill="none"
      stroke="currentColor"
      stroke-width="2"
      stroke-linecap="round"
      stroke-linejoin="round"
      class="lucide lucide-folder"
      aria-hidden="true"
    >
      <path d="M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z" />
    </svg>
     Characteristics
      <:items>
        <.tree_item>
          <svg
      xmlns="http://www.w3.org/2000/svg"
      width="24"
      height="24"
      viewBox="0 0 24 24"
      fill="none"
      stroke="currentColor"
      stroke-width="2"
      stroke-linecap="round"
      stroke-linejoin="round"
      class="lucide lucide-paw-print"
      aria-hidden="true"
    >
      <circle cx="11" cy="4" r="2" />
      <circle cx="18" cy="8" r="2" />
      <circle cx="20" cy="16" r="2" />
      <path d="M9 10a5 5 0 0 1 5 5v3.5a3.5 3.5 0 0 1-6.84 1.045Q6.52 17.48 4.46 16.84A3.5 3.5 0 0 1 5.5 10Z" />
    </svg>
     Playful
        </.tree_item>
        <.tree_item>
          <svg
      xmlns="http://www.w3.org/2000/svg"
      width="24"
      height="24"
      viewBox="0 0 24 24"
      fill="none"
      stroke="currentColor"
      stroke-width="2"
      stroke-linecap="round"
      stroke-linejoin="round"
      class="lucide lucide-paw-print"
      aria-hidden="true"
    >
      <circle cx="11" cy="4" r="2" />
      <circle cx="18" cy="8" r="2" />
      <circle cx="20" cy="16" r="2" />
      <path d="M9 10a5 5 0 0 1 5 5v3.5a3.5 3.5 0 0 1-6.84 1.045Q6.52 17.48 4.46 16.84A3.5 3.5 0 0 1 5.5 10Z" />
    </svg>
     Loyal
        </.tree_item>
      </:items>
    </.tree_item>
  </.tree>
</div>