Tree

An accessible hierarchy with disclosure, bounded logical indentation, and truthful drag-and-drop previews.

Project files

A small expanded branch shows the hierarchy without filling the card.

Project

Code

<link
  rel="component"
  href="@threadlabs/looma/components/ui-tree/ui-tree.html"
>
<link
  rel="component"
  href="@threadlabs/looma/components/ui-tree-item/ui-tree-item.html"
>

<ui-tree
  label="Project files"
>
  <ui-tree-item
    item-id="project"
    label="Project"
    expanded
  >
    <ui-tree-item
      item-id="guide"
      label="Guide"
    ></ui-tree-item>
    <ui-tree-item
      item-id="notes"
      label="Notes"
    ></ui-tree-item>
  </ui-tree-item>
</ui-tree>
<script setup>
  import { UiTree, UiTreeItem } from "@threadlabs/looma";
</script>

<template>
  <UiTree
    label="Project files"
  >
    <UiTreeItem
      item-id="project"
      label="Project"
      :expanded="true"
    >
      <UiTreeItem
        item-id="guide"
        label="Guide"
      />
      <UiTreeItem
        item-id="notes"
        label="Notes"
      />
    </UiTreeItem>
  </UiTree>
</template>
import { UiTree, UiTreeItem } from "@threadlabs/looma";

export default function Example() {
  return <UiTree
    label="Project files"
  >
    <UiTreeItem
      itemId="project"
      label="Project"
      expanded
    >
      <UiTreeItem
        itemId="guide"
        label="Guide"
      />
      <UiTreeItem
        itemId="notes"
        label="Notes"
      />
    </UiTreeItem>
  </UiTree>;
}
/** @jsxImportSource preact */
import { UiTree, UiTreeItem } from "@threadlabs/looma";

export default function Example() {
  return <UiTree
    label="Project files"
  >
    <UiTreeItem
      itemId="project"
      label="Project"
      expanded
    >
      <UiTreeItem
        itemId="guide"
        label="Guide"
      />
      <UiTreeItem
        itemId="notes"
        label="Notes"
      />
    </UiTreeItem>
  </UiTree>;
}
<script>
  import { UiTree, UiTreeItem } from "@threadlabs/looma";
</script>

<UiTree
  label="Project files"
>
  <UiTreeItem
    itemId="project"
    label="Project"
    expanded
  >
    <UiTreeItem
      itemId="guide"
      label="Guide"
    />
    <UiTreeItem
      itemId="notes"
      label="Notes"
    />
  </UiTreeItem>
</UiTree>
import { Component } from "@angular/core";
import { UiTree, UiTreeItem } from "@threadlabs/looma";

@Component({
  selector: "example-view",
  standalone: true,
  imports: [UiTree, UiTreeItem],
  template: `
    <div
      ui-tree
      [label]="'Project files'"
    >
      <div
        ui-tree-item
        [itemId]="'project'"
        [label]="'Project'"
        [expanded]="true"
      >
        <div
          ui-tree-item
          [itemId]="'guide'"
          [label]="'Guide'"
        ></div>
        <div
          ui-tree-item
          [itemId]="'notes'"
          [label]="'Notes'"
        ></div>
      </div>
    </div>
  `,
})
export default class Example {}

Default

A labelled tree of items with leading icons; folders start collapsed and nesting depth is unrestricted.

docs
src
README.md

Code

<link
  rel="component"
  href="@threadlabs/looma/components/ui-tree/ui-tree.html"
>
<link
  rel="component"
  href="@threadlabs/looma/components/ui-tree-item/ui-tree-item.html"
>

<ui-tree
  label="Project files"
>
  <ui-tree-item
    item-id="docs"
    label="docs"
  >
    <svg
      slot="leading"
      aria-hidden="true"
      viewBox="0 0 24 24"
      fill="none"
      stroke="currentColor"
      stroke-width="2"
      stroke-linecap="round"
      stroke-linejoin="round"
    >
      <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"
      ></path>
    </svg>
    <ui-tree-item
      item-id="guide"
      label="guide.md"
    >
      <svg
        slot="leading"
        aria-hidden="true"
        viewBox="0 0 24 24"
        fill="none"
        stroke="currentColor"
        stroke-width="2"
        stroke-linecap="round"
        stroke-linejoin="round"
      >
        <path
          d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
        ></path>
        <path
          d="M14 2v4a2 2 0 0 0 2 2h4"
        ></path>
      </svg>
    </ui-tree-item>
    <ui-tree-item
      item-id="api"
      label="api.md"
    >
      <svg
        slot="leading"
        aria-hidden="true"
        viewBox="0 0 24 24"
        fill="none"
        stroke="currentColor"
        stroke-width="2"
        stroke-linecap="round"
        stroke-linejoin="round"
      >
        <path
          d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
        ></path>
        <path
          d="M14 2v4a2 2 0 0 0 2 2h4"
        ></path>
      </svg>
    </ui-tree-item>
  </ui-tree-item>
  <ui-tree-item
    item-id="src"
    label="src"
  >
    <svg
      slot="leading"
      aria-hidden="true"
      viewBox="0 0 24 24"
      fill="none"
      stroke="currentColor"
      stroke-width="2"
      stroke-linecap="round"
      stroke-linejoin="round"
    >
      <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"
      ></path>
    </svg>
    <ui-tree-item
      item-id="index"
      label="index.ts"
    >
      <svg
        slot="leading"
        aria-hidden="true"
        viewBox="0 0 24 24"
        fill="none"
        stroke="currentColor"
        stroke-width="2"
        stroke-linecap="round"
        stroke-linejoin="round"
      >
        <path
          d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
        ></path>
        <path
          d="M14 2v4a2 2 0 0 0 2 2h4"
        ></path>
      </svg>
    </ui-tree-item>
  </ui-tree-item>
  <ui-tree-item
    item-id="readme"
    label="README.md"
  >
    <svg
      slot="leading"
      aria-hidden="true"
      viewBox="0 0 24 24"
      fill="none"
      stroke="currentColor"
      stroke-width="2"
      stroke-linecap="round"
      stroke-linejoin="round"
    >
      <path
        d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
      ></path>
      <path
        d="M14 2v4a2 2 0 0 0 2 2h4"
      ></path>
    </svg>
  </ui-tree-item>
</ui-tree>
<script setup>
  import { UiTree, UiTreeItem } from "@threadlabs/looma";
</script>

<template>
  <UiTree
    label="Project files"
  >
    <UiTreeItem
      item-id="docs"
      label="docs"
    >
      <template #leading>
        <svg
          aria-hidden="true"
          viewBox="0 0 24 24"
          fill="none"
          stroke="currentColor"
          stroke-width="2"
          stroke-linecap="round"
          stroke-linejoin="round"
        >
          <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"
          ></path>
        </svg>
      </template>
      <UiTreeItem
        item-id="guide"
        label="guide.md"
      >
        <template #leading>
          <svg
            aria-hidden="true"
            viewBox="0 0 24 24"
            fill="none"
            stroke="currentColor"
            stroke-width="2"
            stroke-linecap="round"
            stroke-linejoin="round"
          >
            <path
              d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
            ></path>
            <path
              d="M14 2v4a2 2 0 0 0 2 2h4"
            ></path>
          </svg>
        </template>
      </UiTreeItem>
      <UiTreeItem
        item-id="api"
        label="api.md"
      >
        <template #leading>
          <svg
            aria-hidden="true"
            viewBox="0 0 24 24"
            fill="none"
            stroke="currentColor"
            stroke-width="2"
            stroke-linecap="round"
            stroke-linejoin="round"
          >
            <path
              d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
            ></path>
            <path
              d="M14 2v4a2 2 0 0 0 2 2h4"
            ></path>
          </svg>
        </template>
      </UiTreeItem>
    </UiTreeItem>
    <UiTreeItem
      item-id="src"
      label="src"
    >
      <template #leading>
        <svg
          aria-hidden="true"
          viewBox="0 0 24 24"
          fill="none"
          stroke="currentColor"
          stroke-width="2"
          stroke-linecap="round"
          stroke-linejoin="round"
        >
          <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"
          ></path>
        </svg>
      </template>
      <UiTreeItem
        item-id="index"
        label="index.ts"
      >
        <template #leading>
          <svg
            aria-hidden="true"
            viewBox="0 0 24 24"
            fill="none"
            stroke="currentColor"
            stroke-width="2"
            stroke-linecap="round"
            stroke-linejoin="round"
          >
            <path
              d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
            ></path>
            <path
              d="M14 2v4a2 2 0 0 0 2 2h4"
            ></path>
          </svg>
        </template>
      </UiTreeItem>
    </UiTreeItem>
    <UiTreeItem
      item-id="readme"
      label="README.md"
    >
      <template #leading>
        <svg
          aria-hidden="true"
          viewBox="0 0 24 24"
          fill="none"
          stroke="currentColor"
          stroke-width="2"
          stroke-linecap="round"
          stroke-linejoin="round"
        >
          <path
            d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
          ></path>
          <path
            d="M14 2v4a2 2 0 0 0 2 2h4"
          ></path>
        </svg>
      </template>
    </UiTreeItem>
  </UiTree>
</template>
import { UiTree, UiTreeItem } from "@threadlabs/looma";

export default function Example() {
  return <UiTree
    label="Project files"
  >
    <UiTreeItem
      itemId="docs"
      label="docs"
      slots={{
        leading: <svg
          aria-hidden="true"
          viewBox="0 0 24 24"
          fill="none"
          stroke="currentColor"
          stroke-width="2"
          stroke-linecap="round"
          stroke-linejoin="round"
        >
          <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>,
      }}
    >
      <UiTreeItem
        itemId="guide"
        label="guide.md"
        slots={{
          leading: <svg
            aria-hidden="true"
            viewBox="0 0 24 24"
            fill="none"
            stroke="currentColor"
            stroke-width="2"
            stroke-linecap="round"
            stroke-linejoin="round"
          >
            <path
              d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
            />
            <path
              d="M14 2v4a2 2 0 0 0 2 2h4"
            />
          </svg>,
        }}
      >{" "}{" "}</UiTreeItem>
      <UiTreeItem
        itemId="api"
        label="api.md"
        slots={{
          leading: <svg
            aria-hidden="true"
            viewBox="0 0 24 24"
            fill="none"
            stroke="currentColor"
            stroke-width="2"
            stroke-linecap="round"
            stroke-linejoin="round"
          >
            <path
              d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
            />
            <path
              d="M14 2v4a2 2 0 0 0 2 2h4"
            />
          </svg>,
        }}
      >{" "}{" "}</UiTreeItem>
    </UiTreeItem>
    <UiTreeItem
      itemId="src"
      label="src"
      slots={{
        leading: <svg
          aria-hidden="true"
          viewBox="0 0 24 24"
          fill="none"
          stroke="currentColor"
          stroke-width="2"
          stroke-linecap="round"
          stroke-linejoin="round"
        >
          <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>,
      }}
    >
      <UiTreeItem
        itemId="index"
        label="index.ts"
        slots={{
          leading: <svg
            aria-hidden="true"
            viewBox="0 0 24 24"
            fill="none"
            stroke="currentColor"
            stroke-width="2"
            stroke-linecap="round"
            stroke-linejoin="round"
          >
            <path
              d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
            />
            <path
              d="M14 2v4a2 2 0 0 0 2 2h4"
            />
          </svg>,
        }}
      >{" "}{" "}</UiTreeItem>
    </UiTreeItem>
    <UiTreeItem
      itemId="readme"
      label="README.md"
      slots={{
        leading: <svg
          aria-hidden="true"
          viewBox="0 0 24 24"
          fill="none"
          stroke="currentColor"
          stroke-width="2"
          stroke-linecap="round"
          stroke-linejoin="round"
        >
          <path
            d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
          />
          <path
            d="M14 2v4a2 2 0 0 0 2 2h4"
          />
        </svg>,
      }}
    >{" "}{" "}</UiTreeItem>
  </UiTree>;
}
/** @jsxImportSource preact */
import { UiTree, UiTreeItem } from "@threadlabs/looma";

export default function Example() {
  return <UiTree
    label="Project files"
  >
    <UiTreeItem
      itemId="docs"
      label="docs"
      slots={{
        leading: <svg
          aria-hidden="true"
          viewBox="0 0 24 24"
          fill="none"
          stroke="currentColor"
          stroke-width="2"
          stroke-linecap="round"
          stroke-linejoin="round"
        >
          <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>,
      }}
    >
      <UiTreeItem
        itemId="guide"
        label="guide.md"
        slots={{
          leading: <svg
            aria-hidden="true"
            viewBox="0 0 24 24"
            fill="none"
            stroke="currentColor"
            stroke-width="2"
            stroke-linecap="round"
            stroke-linejoin="round"
          >
            <path
              d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
            />
            <path
              d="M14 2v4a2 2 0 0 0 2 2h4"
            />
          </svg>,
        }}
      >{" "}{" "}</UiTreeItem>
      <UiTreeItem
        itemId="api"
        label="api.md"
        slots={{
          leading: <svg
            aria-hidden="true"
            viewBox="0 0 24 24"
            fill="none"
            stroke="currentColor"
            stroke-width="2"
            stroke-linecap="round"
            stroke-linejoin="round"
          >
            <path
              d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
            />
            <path
              d="M14 2v4a2 2 0 0 0 2 2h4"
            />
          </svg>,
        }}
      >{" "}{" "}</UiTreeItem>
    </UiTreeItem>
    <UiTreeItem
      itemId="src"
      label="src"
      slots={{
        leading: <svg
          aria-hidden="true"
          viewBox="0 0 24 24"
          fill="none"
          stroke="currentColor"
          stroke-width="2"
          stroke-linecap="round"
          stroke-linejoin="round"
        >
          <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>,
      }}
    >
      <UiTreeItem
        itemId="index"
        label="index.ts"
        slots={{
          leading: <svg
            aria-hidden="true"
            viewBox="0 0 24 24"
            fill="none"
            stroke="currentColor"
            stroke-width="2"
            stroke-linecap="round"
            stroke-linejoin="round"
          >
            <path
              d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
            />
            <path
              d="M14 2v4a2 2 0 0 0 2 2h4"
            />
          </svg>,
        }}
      >{" "}{" "}</UiTreeItem>
    </UiTreeItem>
    <UiTreeItem
      itemId="readme"
      label="README.md"
      slots={{
        leading: <svg
          aria-hidden="true"
          viewBox="0 0 24 24"
          fill="none"
          stroke="currentColor"
          stroke-width="2"
          stroke-linecap="round"
          stroke-linejoin="round"
        >
          <path
            d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
          />
          <path
            d="M14 2v4a2 2 0 0 0 2 2h4"
          />
        </svg>,
      }}
    >{" "}{" "}</UiTreeItem>
  </UiTree>;
}
<script>
  import { UiTree, UiTreeItem } from "@threadlabs/looma";
</script>

{#snippet leading()}
  <svg
    aria-hidden="true"
    viewBox="0 0 24 24"
    fill="none"
    stroke="currentColor"
    stroke-width="2"
    stroke-linecap="round"
    stroke-linejoin="round"
  >
    <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"
    ></path>
  </svg>
{/snippet}

{#snippet treeItemLeading()}
  <svg
    aria-hidden="true"
    viewBox="0 0 24 24"
    fill="none"
    stroke="currentColor"
    stroke-width="2"
    stroke-linecap="round"
    stroke-linejoin="round"
  >
    <path
      d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
    ></path>
    <path
      d="M14 2v4a2 2 0 0 0 2 2h4"
    ></path>
  </svg>
{/snippet}

{#snippet treeItemLeading2()}
  <svg
    aria-hidden="true"
    viewBox="0 0 24 24"
    fill="none"
    stroke="currentColor"
    stroke-width="2"
    stroke-linecap="round"
    stroke-linejoin="round"
  >
    <path
      d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
    ></path>
    <path
      d="M14 2v4a2 2 0 0 0 2 2h4"
    ></path>
  </svg>
{/snippet}

{#snippet treeItemLeading3()}
  <svg
    aria-hidden="true"
    viewBox="0 0 24 24"
    fill="none"
    stroke="currentColor"
    stroke-width="2"
    stroke-linecap="round"
    stroke-linejoin="round"
  >
    <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"
    ></path>
  </svg>
{/snippet}

{#snippet treeItemLeading4()}
  <svg
    aria-hidden="true"
    viewBox="0 0 24 24"
    fill="none"
    stroke="currentColor"
    stroke-width="2"
    stroke-linecap="round"
    stroke-linejoin="round"
  >
    <path
      d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
    ></path>
    <path
      d="M14 2v4a2 2 0 0 0 2 2h4"
    ></path>
  </svg>
{/snippet}

{#snippet treeItemLeading5()}
  <svg
    aria-hidden="true"
    viewBox="0 0 24 24"
    fill="none"
    stroke="currentColor"
    stroke-width="2"
    stroke-linecap="round"
    stroke-linejoin="round"
  >
    <path
      d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
    ></path>
    <path
      d="M14 2v4a2 2 0 0 0 2 2h4"
    ></path>
  </svg>
{/snippet}

<UiTree
  label="Project files"
>
  <UiTreeItem
    itemId="docs"
    label="docs"
    slots={{ leading }}
  >
    <UiTreeItem
      itemId="guide"
      label="guide.md"
      slots={{ leading: treeItemLeading }}
    >  </UiTreeItem>
    <UiTreeItem
      itemId="api"
      label="api.md"
      slots={{ leading: treeItemLeading2 }}
    >  </UiTreeItem>
  </UiTreeItem>
  <UiTreeItem
    itemId="src"
    label="src"
    slots={{ leading: treeItemLeading3 }}
  >
    <UiTreeItem
      itemId="index"
      label="index.ts"
      slots={{ leading: treeItemLeading4 }}
    >  </UiTreeItem>
  </UiTreeItem>
  <UiTreeItem
    itemId="readme"
    label="README.md"
    slots={{ leading: treeItemLeading5 }}
  >  </UiTreeItem>
</UiTree>
import { Component } from "@angular/core";
import { UiTree, UiTreeItem } from "@threadlabs/looma";

@Component({
  selector: "example-view",
  standalone: true,
  imports: [UiTree, UiTreeItem],
  template: `
    <div
      ui-tree
      [label]="'Project files'"
    >
      <div
        ui-tree-item
        [itemId]="'docs'"
        [label]="'docs'"
      >
        <ng-container ngProjectAs="[slot='leading']">
          <svg
            aria-hidden="true"
            viewBox="0 0 24 24"
            fill="none"
            stroke="currentColor"
            stroke-width="2"
            stroke-linecap="round"
            stroke-linejoin="round"
          >
            <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"
            ></path>
          </svg>
        </ng-container>
        <div
          ui-tree-item
          [itemId]="'guide'"
          [label]="'guide.md'"
        >
          <ng-container ngProjectAs="[slot='leading']">
            <svg
              aria-hidden="true"
              viewBox="0 0 24 24"
              fill="none"
              stroke="currentColor"
              stroke-width="2"
              stroke-linecap="round"
              stroke-linejoin="round"
            >
              <path
                d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
              ></path>
              <path
                d="M14 2v4a2 2 0 0 0 2 2h4"
              ></path>
            </svg>
          </ng-container>
        </div>
        <div
          ui-tree-item
          [itemId]="'api'"
          [label]="'api.md'"
        >
          <ng-container ngProjectAs="[slot='leading']">
            <svg
              aria-hidden="true"
              viewBox="0 0 24 24"
              fill="none"
              stroke="currentColor"
              stroke-width="2"
              stroke-linecap="round"
              stroke-linejoin="round"
            >
              <path
                d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
              ></path>
              <path
                d="M14 2v4a2 2 0 0 0 2 2h4"
              ></path>
            </svg>
          </ng-container>
        </div>
      </div>
      <div
        ui-tree-item
        [itemId]="'src'"
        [label]="'src'"
      >
        <ng-container ngProjectAs="[slot='leading']">
          <svg
            aria-hidden="true"
            viewBox="0 0 24 24"
            fill="none"
            stroke="currentColor"
            stroke-width="2"
            stroke-linecap="round"
            stroke-linejoin="round"
          >
            <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"
            ></path>
          </svg>
        </ng-container>
        <div
          ui-tree-item
          [itemId]="'index'"
          [label]="'index.ts'"
        >
          <ng-container ngProjectAs="[slot='leading']">
            <svg
              aria-hidden="true"
              viewBox="0 0 24 24"
              fill="none"
              stroke="currentColor"
              stroke-width="2"
              stroke-linecap="round"
              stroke-linejoin="round"
            >
              <path
                d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
              ></path>
              <path
                d="M14 2v4a2 2 0 0 0 2 2h4"
              ></path>
            </svg>
          </ng-container>
        </div>
      </div>
      <div
        ui-tree-item
        [itemId]="'readme'"
        [label]="'README.md'"
      >
        <ng-container ngProjectAs="[slot='leading']">
          <svg
            aria-hidden="true"
            viewBox="0 0 24 24"
            fill="none"
            stroke="currentColor"
            stroke-width="2"
            stroke-linecap="round"
            stroke-linejoin="round"
          >
            <path
              d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
            ></path>
            <path
              d="M14 2v4a2 2 0 0 0 2 2h4"
            ></path>
          </svg>
        </ng-container>
      </div>
    </div>
  `,
})
export default class Example {}

Expanded and selected items

Items carrying expanded start open; selected marks the current item.

docs
src
README.md

Code

<link
  rel="component"
  href="@threadlabs/looma/components/ui-tree/ui-tree.html"
>
<link
  rel="component"
  href="@threadlabs/looma/components/ui-tree-item/ui-tree-item.html"
>

<ui-tree
  label="Project files"
>
  <ui-tree-item
    item-id="docs"
    label="docs"
    expanded
  >
    <svg
      slot="leading"
      aria-hidden="true"
      viewBox="0 0 24 24"
      fill="none"
      stroke="currentColor"
      stroke-width="2"
      stroke-linecap="round"
      stroke-linejoin="round"
    >
      <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"
      ></path>
    </svg>
    <ui-tree-item
      item-id="guide"
      label="guide.md"
      selected
    >
      <svg
        slot="leading"
        aria-hidden="true"
        viewBox="0 0 24 24"
        fill="none"
        stroke="currentColor"
        stroke-width="2"
        stroke-linecap="round"
        stroke-linejoin="round"
      >
        <path
          d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
        ></path>
        <path
          d="M14 2v4a2 2 0 0 0 2 2h4"
        ></path>
      </svg>
    </ui-tree-item>
    <ui-tree-item
      item-id="api"
      label="api.md"
    >
      <svg
        slot="leading"
        aria-hidden="true"
        viewBox="0 0 24 24"
        fill="none"
        stroke="currentColor"
        stroke-width="2"
        stroke-linecap="round"
        stroke-linejoin="round"
      >
        <path
          d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
        ></path>
        <path
          d="M14 2v4a2 2 0 0 0 2 2h4"
        ></path>
      </svg>
    </ui-tree-item>
  </ui-tree-item>
  <ui-tree-item
    item-id="src"
    label="src"
  >
    <svg
      slot="leading"
      aria-hidden="true"
      viewBox="0 0 24 24"
      fill="none"
      stroke="currentColor"
      stroke-width="2"
      stroke-linecap="round"
      stroke-linejoin="round"
    >
      <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"
      ></path>
    </svg>
    <ui-tree-item
      item-id="index"
      label="index.ts"
    >
      <svg
        slot="leading"
        aria-hidden="true"
        viewBox="0 0 24 24"
        fill="none"
        stroke="currentColor"
        stroke-width="2"
        stroke-linecap="round"
        stroke-linejoin="round"
      >
        <path
          d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
        ></path>
        <path
          d="M14 2v4a2 2 0 0 0 2 2h4"
        ></path>
      </svg>
    </ui-tree-item>
  </ui-tree-item>
  <ui-tree-item
    item-id="readme"
    label="README.md"
  >
    <svg
      slot="leading"
      aria-hidden="true"
      viewBox="0 0 24 24"
      fill="none"
      stroke="currentColor"
      stroke-width="2"
      stroke-linecap="round"
      stroke-linejoin="round"
    >
      <path
        d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
      ></path>
      <path
        d="M14 2v4a2 2 0 0 0 2 2h4"
      ></path>
    </svg>
  </ui-tree-item>
</ui-tree>
<script setup>
  import { UiTree, UiTreeItem } from "@threadlabs/looma";
</script>

<template>
  <UiTree
    label="Project files"
  >
    <UiTreeItem
      item-id="docs"
      label="docs"
      :expanded="true"
    >
      <template #leading>
        <svg
          aria-hidden="true"
          viewBox="0 0 24 24"
          fill="none"
          stroke="currentColor"
          stroke-width="2"
          stroke-linecap="round"
          stroke-linejoin="round"
        >
          <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"
          ></path>
        </svg>
      </template>
      <UiTreeItem
        item-id="guide"
        label="guide.md"
        :selected="true"
      >
        <template #leading>
          <svg
            aria-hidden="true"
            viewBox="0 0 24 24"
            fill="none"
            stroke="currentColor"
            stroke-width="2"
            stroke-linecap="round"
            stroke-linejoin="round"
          >
            <path
              d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
            ></path>
            <path
              d="M14 2v4a2 2 0 0 0 2 2h4"
            ></path>
          </svg>
        </template>
      </UiTreeItem>
      <UiTreeItem
        item-id="api"
        label="api.md"
      >
        <template #leading>
          <svg
            aria-hidden="true"
            viewBox="0 0 24 24"
            fill="none"
            stroke="currentColor"
            stroke-width="2"
            stroke-linecap="round"
            stroke-linejoin="round"
          >
            <path
              d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
            ></path>
            <path
              d="M14 2v4a2 2 0 0 0 2 2h4"
            ></path>
          </svg>
        </template>
      </UiTreeItem>
    </UiTreeItem>
    <UiTreeItem
      item-id="src"
      label="src"
    >
      <template #leading>
        <svg
          aria-hidden="true"
          viewBox="0 0 24 24"
          fill="none"
          stroke="currentColor"
          stroke-width="2"
          stroke-linecap="round"
          stroke-linejoin="round"
        >
          <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"
          ></path>
        </svg>
      </template>
      <UiTreeItem
        item-id="index"
        label="index.ts"
      >
        <template #leading>
          <svg
            aria-hidden="true"
            viewBox="0 0 24 24"
            fill="none"
            stroke="currentColor"
            stroke-width="2"
            stroke-linecap="round"
            stroke-linejoin="round"
          >
            <path
              d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
            ></path>
            <path
              d="M14 2v4a2 2 0 0 0 2 2h4"
            ></path>
          </svg>
        </template>
      </UiTreeItem>
    </UiTreeItem>
    <UiTreeItem
      item-id="readme"
      label="README.md"
    >
      <template #leading>
        <svg
          aria-hidden="true"
          viewBox="0 0 24 24"
          fill="none"
          stroke="currentColor"
          stroke-width="2"
          stroke-linecap="round"
          stroke-linejoin="round"
        >
          <path
            d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
          ></path>
          <path
            d="M14 2v4a2 2 0 0 0 2 2h4"
          ></path>
        </svg>
      </template>
    </UiTreeItem>
  </UiTree>
</template>
import { UiTree, UiTreeItem } from "@threadlabs/looma";

export default function Example() {
  return <UiTree
    label="Project files"
  >
    <UiTreeItem
      itemId="docs"
      label="docs"
      expanded
      slots={{
        leading: <svg
          aria-hidden="true"
          viewBox="0 0 24 24"
          fill="none"
          stroke="currentColor"
          stroke-width="2"
          stroke-linecap="round"
          stroke-linejoin="round"
        >
          <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>,
      }}
    >
      <UiTreeItem
        itemId="guide"
        label="guide.md"
        selected
        slots={{
          leading: <svg
            aria-hidden="true"
            viewBox="0 0 24 24"
            fill="none"
            stroke="currentColor"
            stroke-width="2"
            stroke-linecap="round"
            stroke-linejoin="round"
          >
            <path
              d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
            />
            <path
              d="M14 2v4a2 2 0 0 0 2 2h4"
            />
          </svg>,
        }}
      >{" "}{" "}</UiTreeItem>
      <UiTreeItem
        itemId="api"
        label="api.md"
        slots={{
          leading: <svg
            aria-hidden="true"
            viewBox="0 0 24 24"
            fill="none"
            stroke="currentColor"
            stroke-width="2"
            stroke-linecap="round"
            stroke-linejoin="round"
          >
            <path
              d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
            />
            <path
              d="M14 2v4a2 2 0 0 0 2 2h4"
            />
          </svg>,
        }}
      >{" "}{" "}</UiTreeItem>
    </UiTreeItem>
    <UiTreeItem
      itemId="src"
      label="src"
      slots={{
        leading: <svg
          aria-hidden="true"
          viewBox="0 0 24 24"
          fill="none"
          stroke="currentColor"
          stroke-width="2"
          stroke-linecap="round"
          stroke-linejoin="round"
        >
          <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>,
      }}
    >
      <UiTreeItem
        itemId="index"
        label="index.ts"
        slots={{
          leading: <svg
            aria-hidden="true"
            viewBox="0 0 24 24"
            fill="none"
            stroke="currentColor"
            stroke-width="2"
            stroke-linecap="round"
            stroke-linejoin="round"
          >
            <path
              d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
            />
            <path
              d="M14 2v4a2 2 0 0 0 2 2h4"
            />
          </svg>,
        }}
      >{" "}{" "}</UiTreeItem>
    </UiTreeItem>
    <UiTreeItem
      itemId="readme"
      label="README.md"
      slots={{
        leading: <svg
          aria-hidden="true"
          viewBox="0 0 24 24"
          fill="none"
          stroke="currentColor"
          stroke-width="2"
          stroke-linecap="round"
          stroke-linejoin="round"
        >
          <path
            d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
          />
          <path
            d="M14 2v4a2 2 0 0 0 2 2h4"
          />
        </svg>,
      }}
    >{" "}{" "}</UiTreeItem>
  </UiTree>;
}
/** @jsxImportSource preact */
import { UiTree, UiTreeItem } from "@threadlabs/looma";

export default function Example() {
  return <UiTree
    label="Project files"
  >
    <UiTreeItem
      itemId="docs"
      label="docs"
      expanded
      slots={{
        leading: <svg
          aria-hidden="true"
          viewBox="0 0 24 24"
          fill="none"
          stroke="currentColor"
          stroke-width="2"
          stroke-linecap="round"
          stroke-linejoin="round"
        >
          <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>,
      }}
    >
      <UiTreeItem
        itemId="guide"
        label="guide.md"
        selected
        slots={{
          leading: <svg
            aria-hidden="true"
            viewBox="0 0 24 24"
            fill="none"
            stroke="currentColor"
            stroke-width="2"
            stroke-linecap="round"
            stroke-linejoin="round"
          >
            <path
              d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
            />
            <path
              d="M14 2v4a2 2 0 0 0 2 2h4"
            />
          </svg>,
        }}
      >{" "}{" "}</UiTreeItem>
      <UiTreeItem
        itemId="api"
        label="api.md"
        slots={{
          leading: <svg
            aria-hidden="true"
            viewBox="0 0 24 24"
            fill="none"
            stroke="currentColor"
            stroke-width="2"
            stroke-linecap="round"
            stroke-linejoin="round"
          >
            <path
              d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
            />
            <path
              d="M14 2v4a2 2 0 0 0 2 2h4"
            />
          </svg>,
        }}
      >{" "}{" "}</UiTreeItem>
    </UiTreeItem>
    <UiTreeItem
      itemId="src"
      label="src"
      slots={{
        leading: <svg
          aria-hidden="true"
          viewBox="0 0 24 24"
          fill="none"
          stroke="currentColor"
          stroke-width="2"
          stroke-linecap="round"
          stroke-linejoin="round"
        >
          <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>,
      }}
    >
      <UiTreeItem
        itemId="index"
        label="index.ts"
        slots={{
          leading: <svg
            aria-hidden="true"
            viewBox="0 0 24 24"
            fill="none"
            stroke="currentColor"
            stroke-width="2"
            stroke-linecap="round"
            stroke-linejoin="round"
          >
            <path
              d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
            />
            <path
              d="M14 2v4a2 2 0 0 0 2 2h4"
            />
          </svg>,
        }}
      >{" "}{" "}</UiTreeItem>
    </UiTreeItem>
    <UiTreeItem
      itemId="readme"
      label="README.md"
      slots={{
        leading: <svg
          aria-hidden="true"
          viewBox="0 0 24 24"
          fill="none"
          stroke="currentColor"
          stroke-width="2"
          stroke-linecap="round"
          stroke-linejoin="round"
        >
          <path
            d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
          />
          <path
            d="M14 2v4a2 2 0 0 0 2 2h4"
          />
        </svg>,
      }}
    >{" "}{" "}</UiTreeItem>
  </UiTree>;
}
<script>
  import { UiTree, UiTreeItem } from "@threadlabs/looma";
</script>

{#snippet leading()}
  <svg
    aria-hidden="true"
    viewBox="0 0 24 24"
    fill="none"
    stroke="currentColor"
    stroke-width="2"
    stroke-linecap="round"
    stroke-linejoin="round"
  >
    <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"
    ></path>
  </svg>
{/snippet}

{#snippet treeItemLeading()}
  <svg
    aria-hidden="true"
    viewBox="0 0 24 24"
    fill="none"
    stroke="currentColor"
    stroke-width="2"
    stroke-linecap="round"
    stroke-linejoin="round"
  >
    <path
      d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
    ></path>
    <path
      d="M14 2v4a2 2 0 0 0 2 2h4"
    ></path>
  </svg>
{/snippet}

{#snippet treeItemLeading2()}
  <svg
    aria-hidden="true"
    viewBox="0 0 24 24"
    fill="none"
    stroke="currentColor"
    stroke-width="2"
    stroke-linecap="round"
    stroke-linejoin="round"
  >
    <path
      d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
    ></path>
    <path
      d="M14 2v4a2 2 0 0 0 2 2h4"
    ></path>
  </svg>
{/snippet}

{#snippet treeItemLeading3()}
  <svg
    aria-hidden="true"
    viewBox="0 0 24 24"
    fill="none"
    stroke="currentColor"
    stroke-width="2"
    stroke-linecap="round"
    stroke-linejoin="round"
  >
    <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"
    ></path>
  </svg>
{/snippet}

{#snippet treeItemLeading4()}
  <svg
    aria-hidden="true"
    viewBox="0 0 24 24"
    fill="none"
    stroke="currentColor"
    stroke-width="2"
    stroke-linecap="round"
    stroke-linejoin="round"
  >
    <path
      d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
    ></path>
    <path
      d="M14 2v4a2 2 0 0 0 2 2h4"
    ></path>
  </svg>
{/snippet}

{#snippet treeItemLeading5()}
  <svg
    aria-hidden="true"
    viewBox="0 0 24 24"
    fill="none"
    stroke="currentColor"
    stroke-width="2"
    stroke-linecap="round"
    stroke-linejoin="round"
  >
    <path
      d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
    ></path>
    <path
      d="M14 2v4a2 2 0 0 0 2 2h4"
    ></path>
  </svg>
{/snippet}

<UiTree
  label="Project files"
>
  <UiTreeItem
    itemId="docs"
    label="docs"
    expanded
    slots={{ leading }}
  >
    <UiTreeItem
      itemId="guide"
      label="guide.md"
      selected
      slots={{ leading: treeItemLeading }}
    >  </UiTreeItem>
    <UiTreeItem
      itemId="api"
      label="api.md"
      slots={{ leading: treeItemLeading2 }}
    >  </UiTreeItem>
  </UiTreeItem>
  <UiTreeItem
    itemId="src"
    label="src"
    slots={{ leading: treeItemLeading3 }}
  >
    <UiTreeItem
      itemId="index"
      label="index.ts"
      slots={{ leading: treeItemLeading4 }}
    >  </UiTreeItem>
  </UiTreeItem>
  <UiTreeItem
    itemId="readme"
    label="README.md"
    slots={{ leading: treeItemLeading5 }}
  >  </UiTreeItem>
</UiTree>
import { Component } from "@angular/core";
import { UiTree, UiTreeItem } from "@threadlabs/looma";

@Component({
  selector: "example-view",
  standalone: true,
  imports: [UiTree, UiTreeItem],
  template: `
    <div
      ui-tree
      [label]="'Project files'"
    >
      <div
        ui-tree-item
        [itemId]="'docs'"
        [label]="'docs'"
        [expanded]="true"
      >
        <ng-container ngProjectAs="[slot='leading']">
          <svg
            aria-hidden="true"
            viewBox="0 0 24 24"
            fill="none"
            stroke="currentColor"
            stroke-width="2"
            stroke-linecap="round"
            stroke-linejoin="round"
          >
            <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"
            ></path>
          </svg>
        </ng-container>
        <div
          ui-tree-item
          [itemId]="'guide'"
          [label]="'guide.md'"
          [selected]="true"
        >
          <ng-container ngProjectAs="[slot='leading']">
            <svg
              aria-hidden="true"
              viewBox="0 0 24 24"
              fill="none"
              stroke="currentColor"
              stroke-width="2"
              stroke-linecap="round"
              stroke-linejoin="round"
            >
              <path
                d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
              ></path>
              <path
                d="M14 2v4a2 2 0 0 0 2 2h4"
              ></path>
            </svg>
          </ng-container>
        </div>
        <div
          ui-tree-item
          [itemId]="'api'"
          [label]="'api.md'"
        >
          <ng-container ngProjectAs="[slot='leading']">
            <svg
              aria-hidden="true"
              viewBox="0 0 24 24"
              fill="none"
              stroke="currentColor"
              stroke-width="2"
              stroke-linecap="round"
              stroke-linejoin="round"
            >
              <path
                d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
              ></path>
              <path
                d="M14 2v4a2 2 0 0 0 2 2h4"
              ></path>
            </svg>
          </ng-container>
        </div>
      </div>
      <div
        ui-tree-item
        [itemId]="'src'"
        [label]="'src'"
      >
        <ng-container ngProjectAs="[slot='leading']">
          <svg
            aria-hidden="true"
            viewBox="0 0 24 24"
            fill="none"
            stroke="currentColor"
            stroke-width="2"
            stroke-linecap="round"
            stroke-linejoin="round"
          >
            <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"
            ></path>
          </svg>
        </ng-container>
        <div
          ui-tree-item
          [itemId]="'index'"
          [label]="'index.ts'"
        >
          <ng-container ngProjectAs="[slot='leading']">
            <svg
              aria-hidden="true"
              viewBox="0 0 24 24"
              fill="none"
              stroke="currentColor"
              stroke-width="2"
              stroke-linecap="round"
              stroke-linejoin="round"
            >
              <path
                d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
              ></path>
              <path
                d="M14 2v4a2 2 0 0 0 2 2h4"
              ></path>
            </svg>
          </ng-container>
        </div>
      </div>
      <div
        ui-tree-item
        [itemId]="'readme'"
        [label]="'README.md'"
      >
        <ng-container ngProjectAs="[slot='leading']">
          <svg
            aria-hidden="true"
            viewBox="0 0 24 24"
            fill="none"
            stroke="currentColor"
            stroke-width="2"
            stroke-linecap="round"
            stroke-linejoin="round"
          >
            <path
              d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
            ></path>
            <path
              d="M14 2v4a2 2 0 0 0 2 2h4"
            ></path>
          </svg>
        </ng-container>
      </div>
    </div>
  `,
})
export default class Example {}

max-depth="2" and hover-expand-delay="300"

With sortable items, hover a row to reveal its drag handle. Max-depth rejects drops that would nest deeper than two levels; hover-expand-delay opens a collapsed folder after 300ms of drag hover.

docs
README.md

Code

<link
  rel="component"
  href="@threadlabs/looma/components/ui-tree/ui-tree.html"
>
<link
  rel="component"
  href="@threadlabs/looma/components/ui-tree-item/ui-tree-item.html"
>

<ui-tree
  label="Sortable files"
  max-depth="2"
  hover-expand-delay="300"
>
  <ui-tree-item
    item-id="docs"
    label="docs"
    expanded
    sortable
    container
  >
    <svg
      slot="leading"
      aria-hidden="true"
      viewBox="0 0 24 24"
      fill="none"
      stroke="currentColor"
      stroke-width="2"
      stroke-linecap="round"
      stroke-linejoin="round"
    >
      <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"
      ></path>
    </svg>
    <ui-tree-item
      item-id="guide"
      label="guide.md"
      sortable
    >
      <svg
        slot="leading"
        aria-hidden="true"
        viewBox="0 0 24 24"
        fill="none"
        stroke="currentColor"
        stroke-width="2"
        stroke-linecap="round"
        stroke-linejoin="round"
      >
        <path
          d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
        ></path>
        <path
          d="M14 2v4a2 2 0 0 0 2 2h4"
        ></path>
      </svg>
    </ui-tree-item>
    <ui-tree-item
      item-id="api"
      label="api.md"
      sortable
    >
      <svg
        slot="leading"
        aria-hidden="true"
        viewBox="0 0 24 24"
        fill="none"
        stroke="currentColor"
        stroke-width="2"
        stroke-linecap="round"
        stroke-linejoin="round"
      >
        <path
          d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
        ></path>
        <path
          d="M14 2v4a2 2 0 0 0 2 2h4"
        ></path>
      </svg>
    </ui-tree-item>
  </ui-tree-item>
  <ui-tree-item
    item-id="readme"
    label="README.md"
    sortable
  >
    <svg
      slot="leading"
      aria-hidden="true"
      viewBox="0 0 24 24"
      fill="none"
      stroke="currentColor"
      stroke-width="2"
      stroke-linecap="round"
      stroke-linejoin="round"
    >
      <path
        d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
      ></path>
      <path
        d="M14 2v4a2 2 0 0 0 2 2h4"
      ></path>
    </svg>
  </ui-tree-item>
</ui-tree>
<script setup>
  import { UiTree, UiTreeItem } from "@threadlabs/looma";
</script>

<template>
  <UiTree
    label="Sortable files"
    :max-depth="2"
    :hover-expand-delay="300"
  >
    <UiTreeItem
      item-id="docs"
      label="docs"
      :expanded="true"
      :sortable="true"
      :container="true"
    >
      <template #leading>
        <svg
          aria-hidden="true"
          viewBox="0 0 24 24"
          fill="none"
          stroke="currentColor"
          stroke-width="2"
          stroke-linecap="round"
          stroke-linejoin="round"
        >
          <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"
          ></path>
        </svg>
      </template>
      <UiTreeItem
        item-id="guide"
        label="guide.md"
        :sortable="true"
      >
        <template #leading>
          <svg
            aria-hidden="true"
            viewBox="0 0 24 24"
            fill="none"
            stroke="currentColor"
            stroke-width="2"
            stroke-linecap="round"
            stroke-linejoin="round"
          >
            <path
              d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
            ></path>
            <path
              d="M14 2v4a2 2 0 0 0 2 2h4"
            ></path>
          </svg>
        </template>
      </UiTreeItem>
      <UiTreeItem
        item-id="api"
        label="api.md"
        :sortable="true"
      >
        <template #leading>
          <svg
            aria-hidden="true"
            viewBox="0 0 24 24"
            fill="none"
            stroke="currentColor"
            stroke-width="2"
            stroke-linecap="round"
            stroke-linejoin="round"
          >
            <path
              d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
            ></path>
            <path
              d="M14 2v4a2 2 0 0 0 2 2h4"
            ></path>
          </svg>
        </template>
      </UiTreeItem>
    </UiTreeItem>
    <UiTreeItem
      item-id="readme"
      label="README.md"
      :sortable="true"
    >
      <template #leading>
        <svg
          aria-hidden="true"
          viewBox="0 0 24 24"
          fill="none"
          stroke="currentColor"
          stroke-width="2"
          stroke-linecap="round"
          stroke-linejoin="round"
        >
          <path
            d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
          ></path>
          <path
            d="M14 2v4a2 2 0 0 0 2 2h4"
          ></path>
        </svg>
      </template>
    </UiTreeItem>
  </UiTree>
</template>
import { UiTree, UiTreeItem } from "@threadlabs/looma";

export default function Example() {
  return <UiTree
    label="Sortable files"
    maxDepth={2}
    hoverExpandDelay={300}
  >
    <UiTreeItem
      itemId="docs"
      label="docs"
      expanded
      sortable
      container
      slots={{
        leading: <svg
          aria-hidden="true"
          viewBox="0 0 24 24"
          fill="none"
          stroke="currentColor"
          stroke-width="2"
          stroke-linecap="round"
          stroke-linejoin="round"
        >
          <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>,
      }}
    >
      <UiTreeItem
        itemId="guide"
        label="guide.md"
        sortable
        slots={{
          leading: <svg
            aria-hidden="true"
            viewBox="0 0 24 24"
            fill="none"
            stroke="currentColor"
            stroke-width="2"
            stroke-linecap="round"
            stroke-linejoin="round"
          >
            <path
              d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
            />
            <path
              d="M14 2v4a2 2 0 0 0 2 2h4"
            />
          </svg>,
        }}
      >{" "}{" "}</UiTreeItem>
      <UiTreeItem
        itemId="api"
        label="api.md"
        sortable
        slots={{
          leading: <svg
            aria-hidden="true"
            viewBox="0 0 24 24"
            fill="none"
            stroke="currentColor"
            stroke-width="2"
            stroke-linecap="round"
            stroke-linejoin="round"
          >
            <path
              d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
            />
            <path
              d="M14 2v4a2 2 0 0 0 2 2h4"
            />
          </svg>,
        }}
      >{" "}{" "}</UiTreeItem>
    </UiTreeItem>
    <UiTreeItem
      itemId="readme"
      label="README.md"
      sortable
      slots={{
        leading: <svg
          aria-hidden="true"
          viewBox="0 0 24 24"
          fill="none"
          stroke="currentColor"
          stroke-width="2"
          stroke-linecap="round"
          stroke-linejoin="round"
        >
          <path
            d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
          />
          <path
            d="M14 2v4a2 2 0 0 0 2 2h4"
          />
        </svg>,
      }}
    >{" "}{" "}</UiTreeItem>
  </UiTree>;
}
/** @jsxImportSource preact */
import { UiTree, UiTreeItem } from "@threadlabs/looma";

export default function Example() {
  return <UiTree
    label="Sortable files"
    maxDepth={2}
    hoverExpandDelay={300}
  >
    <UiTreeItem
      itemId="docs"
      label="docs"
      expanded
      sortable
      container
      slots={{
        leading: <svg
          aria-hidden="true"
          viewBox="0 0 24 24"
          fill="none"
          stroke="currentColor"
          stroke-width="2"
          stroke-linecap="round"
          stroke-linejoin="round"
        >
          <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>,
      }}
    >
      <UiTreeItem
        itemId="guide"
        label="guide.md"
        sortable
        slots={{
          leading: <svg
            aria-hidden="true"
            viewBox="0 0 24 24"
            fill="none"
            stroke="currentColor"
            stroke-width="2"
            stroke-linecap="round"
            stroke-linejoin="round"
          >
            <path
              d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
            />
            <path
              d="M14 2v4a2 2 0 0 0 2 2h4"
            />
          </svg>,
        }}
      >{" "}{" "}</UiTreeItem>
      <UiTreeItem
        itemId="api"
        label="api.md"
        sortable
        slots={{
          leading: <svg
            aria-hidden="true"
            viewBox="0 0 24 24"
            fill="none"
            stroke="currentColor"
            stroke-width="2"
            stroke-linecap="round"
            stroke-linejoin="round"
          >
            <path
              d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
            />
            <path
              d="M14 2v4a2 2 0 0 0 2 2h4"
            />
          </svg>,
        }}
      >{" "}{" "}</UiTreeItem>
    </UiTreeItem>
    <UiTreeItem
      itemId="readme"
      label="README.md"
      sortable
      slots={{
        leading: <svg
          aria-hidden="true"
          viewBox="0 0 24 24"
          fill="none"
          stroke="currentColor"
          stroke-width="2"
          stroke-linecap="round"
          stroke-linejoin="round"
        >
          <path
            d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
          />
          <path
            d="M14 2v4a2 2 0 0 0 2 2h4"
          />
        </svg>,
      }}
    >{" "}{" "}</UiTreeItem>
  </UiTree>;
}
<script>
  import { UiTree, UiTreeItem } from "@threadlabs/looma";
</script>

{#snippet leading()}
  <svg
    aria-hidden="true"
    viewBox="0 0 24 24"
    fill="none"
    stroke="currentColor"
    stroke-width="2"
    stroke-linecap="round"
    stroke-linejoin="round"
  >
    <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"
    ></path>
  </svg>
{/snippet}

{#snippet treeItemLeading()}
  <svg
    aria-hidden="true"
    viewBox="0 0 24 24"
    fill="none"
    stroke="currentColor"
    stroke-width="2"
    stroke-linecap="round"
    stroke-linejoin="round"
  >
    <path
      d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
    ></path>
    <path
      d="M14 2v4a2 2 0 0 0 2 2h4"
    ></path>
  </svg>
{/snippet}

{#snippet treeItemLeading2()}
  <svg
    aria-hidden="true"
    viewBox="0 0 24 24"
    fill="none"
    stroke="currentColor"
    stroke-width="2"
    stroke-linecap="round"
    stroke-linejoin="round"
  >
    <path
      d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
    ></path>
    <path
      d="M14 2v4a2 2 0 0 0 2 2h4"
    ></path>
  </svg>
{/snippet}

{#snippet treeItemLeading3()}
  <svg
    aria-hidden="true"
    viewBox="0 0 24 24"
    fill="none"
    stroke="currentColor"
    stroke-width="2"
    stroke-linecap="round"
    stroke-linejoin="round"
  >
    <path
      d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
    ></path>
    <path
      d="M14 2v4a2 2 0 0 0 2 2h4"
    ></path>
  </svg>
{/snippet}

<UiTree
  label="Sortable files"
  maxDepth={2}
  hoverExpandDelay={300}
>
  <UiTreeItem
    itemId="docs"
    label="docs"
    expanded
    sortable
    container
    slots={{ leading }}
  >
    <UiTreeItem
      itemId="guide"
      label="guide.md"
      sortable
      slots={{ leading: treeItemLeading }}
    >  </UiTreeItem>
    <UiTreeItem
      itemId="api"
      label="api.md"
      sortable
      slots={{ leading: treeItemLeading2 }}
    >  </UiTreeItem>
  </UiTreeItem>
  <UiTreeItem
    itemId="readme"
    label="README.md"
    sortable
    slots={{ leading: treeItemLeading3 }}
  >  </UiTreeItem>
</UiTree>
import { Component } from "@angular/core";
import { UiTree, UiTreeItem } from "@threadlabs/looma";

@Component({
  selector: "example-view",
  standalone: true,
  imports: [UiTree, UiTreeItem],
  template: `
    <div
      ui-tree
      [label]="'Sortable files'"
      [maxDepth]="2"
      [hoverExpandDelay]="300"
    >
      <div
        ui-tree-item
        [itemId]="'docs'"
        [label]="'docs'"
        [expanded]="true"
        [sortable]="true"
        [container]="true"
      >
        <ng-container ngProjectAs="[slot='leading']">
          <svg
            aria-hidden="true"
            viewBox="0 0 24 24"
            fill="none"
            stroke="currentColor"
            stroke-width="2"
            stroke-linecap="round"
            stroke-linejoin="round"
          >
            <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"
            ></path>
          </svg>
        </ng-container>
        <div
          ui-tree-item
          [itemId]="'guide'"
          [label]="'guide.md'"
          [sortable]="true"
        >
          <ng-container ngProjectAs="[slot='leading']">
            <svg
              aria-hidden="true"
              viewBox="0 0 24 24"
              fill="none"
              stroke="currentColor"
              stroke-width="2"
              stroke-linecap="round"
              stroke-linejoin="round"
            >
              <path
                d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
              ></path>
              <path
                d="M14 2v4a2 2 0 0 0 2 2h4"
              ></path>
            </svg>
          </ng-container>
        </div>
        <div
          ui-tree-item
          [itemId]="'api'"
          [label]="'api.md'"
          [sortable]="true"
        >
          <ng-container ngProjectAs="[slot='leading']">
            <svg
              aria-hidden="true"
              viewBox="0 0 24 24"
              fill="none"
              stroke="currentColor"
              stroke-width="2"
              stroke-linecap="round"
              stroke-linejoin="round"
            >
              <path
                d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
              ></path>
              <path
                d="M14 2v4a2 2 0 0 0 2 2h4"
              ></path>
            </svg>
          </ng-container>
        </div>
      </div>
      <div
        ui-tree-item
        [itemId]="'readme'"
        [label]="'README.md'"
        [sortable]="true"
      >
        <ng-container ngProjectAs="[slot='leading']">
          <svg
            aria-hidden="true"
            viewBox="0 0 24 24"
            fill="none"
            stroke="currentColor"
            stroke-width="2"
            stroke-linecap="round"
            stroke-linejoin="round"
          >
            <path
              d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
            ></path>
            <path
              d="M14 2v4a2 2 0 0 0 2 2h4"
            ></path>
          </svg>
        </ng-container>
      </div>
    </div>
  `,
})
export default class Example {}

marquee

With marquee, a name too long for its row scrolls while that row is hovered or keyboard focused, rests at the end, then scrolls again. A name that fits beside its controls does not move.

Overview
How to Use This Handbook and Everything Inside It

Code

<link
  rel="component"
  href="@threadlabs/looma/components/ui-tree/ui-tree.html"
>
<link
  rel="component"
  href="@threadlabs/looma/components/ui-tree-item/ui-tree-item.html"
>
<link
  rel="component"
  href="@threadlabs/looma/components/ui-icon-button/ui-icon-button.html"
>

<ui-tree
  label="Pages"
  marquee
  style="width: 15rem"
>
  <ui-tree-item
    item-id="overview"
    label="Overview"
  >
    <ui-icon-button
      slot="actions"
      label="More actions for the overview"
      size="sm"
    >
      <svg
        aria-hidden="true"
        viewBox="0 0 24 24"
        fill="currentColor"
      >
        <circle
          cx="5"
          cy="12"
          r="1.6"
        ></circle>
        <circle
          cx="12"
          cy="12"
          r="1.6"
        ></circle>
        <circle
          cx="19"
          cy="12"
          r="1.6"
        ></circle>
      </svg>
    </ui-icon-button>
  </ui-tree-item>
  <ui-tree-item
    item-id="handbook"
    label="How to Use This Handbook and Everything Inside It"
  >
    <ui-icon-button
      slot="actions"
      label="More actions for the handbook"
      size="sm"
    >
      <svg
        aria-hidden="true"
        viewBox="0 0 24 24"
        fill="currentColor"
      >
        <circle
          cx="5"
          cy="12"
          r="1.6"
        ></circle>
        <circle
          cx="12"
          cy="12"
          r="1.6"
        ></circle>
        <circle
          cx="19"
          cy="12"
          r="1.6"
        ></circle>
      </svg>
    </ui-icon-button>
  </ui-tree-item>
</ui-tree>
<script setup>
  import { UiIconButton, UiTree, UiTreeItem } from "@threadlabs/looma";
</script>

<template>
  <UiTree
    label="Pages"
    :marquee="true"
    style="width: 15rem"
  >
    <UiTreeItem
      item-id="overview"
      label="Overview"
    >
      <template #actions>
        <UiIconButton
          label="More actions for the overview"
          size="sm"
        >
          <svg
            aria-hidden="true"
            viewBox="0 0 24 24"
            fill="currentColor"
          >
            <circle
              cx="5"
              cy="12"
              r="1.6"
            ></circle>
            <circle
              cx="12"
              cy="12"
              r="1.6"
            ></circle>
            <circle
              cx="19"
              cy="12"
              r="1.6"
            ></circle>
          </svg>
        </UiIconButton>
      </template>
    </UiTreeItem>
    <UiTreeItem
      item-id="handbook"
      label="How to Use This Handbook and Everything Inside It"
    >
      <template #actions>
        <UiIconButton
          label="More actions for the handbook"
          size="sm"
        >
          <svg
            aria-hidden="true"
            viewBox="0 0 24 24"
            fill="currentColor"
          >
            <circle
              cx="5"
              cy="12"
              r="1.6"
            ></circle>
            <circle
              cx="12"
              cy="12"
              r="1.6"
            ></circle>
            <circle
              cx="19"
              cy="12"
              r="1.6"
            ></circle>
          </svg>
        </UiIconButton>
      </template>
    </UiTreeItem>
  </UiTree>
</template>
import { UiIconButton, UiTree, UiTreeItem } from "@threadlabs/looma";

export default function Example() {
  return <UiTree
    label="Pages"
    marquee
    style={{"width":"15rem"}}
  >
    <UiTreeItem
      itemId="overview"
      label="Overview"
      slots={{
        actions: <UiIconButton
          label="More actions for the overview"
          size="sm"
        >
          <svg
            aria-hidden="true"
            viewBox="0 0 24 24"
            fill="currentColor"
          >
            <circle
              cx="5"
              cy="12"
              r="1.6"
            />
            <circle
              cx="12"
              cy="12"
              r="1.6"
            />
            <circle
              cx="19"
              cy="12"
              r="1.6"
            />
          </svg>
        </UiIconButton>,
      }}
    >{" "}{" "}</UiTreeItem>
    <UiTreeItem
      itemId="handbook"
      label="How to Use This Handbook and Everything Inside It"
      slots={{
        actions: <UiIconButton
          label="More actions for the handbook"
          size="sm"
        >
          <svg
            aria-hidden="true"
            viewBox="0 0 24 24"
            fill="currentColor"
          >
            <circle
              cx="5"
              cy="12"
              r="1.6"
            />
            <circle
              cx="12"
              cy="12"
              r="1.6"
            />
            <circle
              cx="19"
              cy="12"
              r="1.6"
            />
          </svg>
        </UiIconButton>,
      }}
    >{" "}{" "}</UiTreeItem>
  </UiTree>;
}
/** @jsxImportSource preact */
import { UiIconButton, UiTree, UiTreeItem } from "@threadlabs/looma";

export default function Example() {
  return <UiTree
    label="Pages"
    marquee
    style={{"width":"15rem"}}
  >
    <UiTreeItem
      itemId="overview"
      label="Overview"
      slots={{
        actions: <UiIconButton
          label="More actions for the overview"
          size="sm"
        >
          <svg
            aria-hidden="true"
            viewBox="0 0 24 24"
            fill="currentColor"
          >
            <circle
              cx="5"
              cy="12"
              r="1.6"
            />
            <circle
              cx="12"
              cy="12"
              r="1.6"
            />
            <circle
              cx="19"
              cy="12"
              r="1.6"
            />
          </svg>
        </UiIconButton>,
      }}
    >{" "}{" "}</UiTreeItem>
    <UiTreeItem
      itemId="handbook"
      label="How to Use This Handbook and Everything Inside It"
      slots={{
        actions: <UiIconButton
          label="More actions for the handbook"
          size="sm"
        >
          <svg
            aria-hidden="true"
            viewBox="0 0 24 24"
            fill="currentColor"
          >
            <circle
              cx="5"
              cy="12"
              r="1.6"
            />
            <circle
              cx="12"
              cy="12"
              r="1.6"
            />
            <circle
              cx="19"
              cy="12"
              r="1.6"
            />
          </svg>
        </UiIconButton>,
      }}
    >{" "}{" "}</UiTreeItem>
  </UiTree>;
}
<script>
  import { UiIconButton, UiTree, UiTreeItem } from "@threadlabs/looma";
</script>

{#snippet actions()}
  <UiIconButton
    label="More actions for the overview"
    size="sm"
  >
    <svg
      aria-hidden="true"
      viewBox="0 0 24 24"
      fill="currentColor"
    >
      <circle
        cx="5"
        cy="12"
        r="1.6"
      ></circle>
      <circle
        cx="12"
        cy="12"
        r="1.6"
      ></circle>
      <circle
        cx="19"
        cy="12"
        r="1.6"
      ></circle>
    </svg>
  </UiIconButton>
{/snippet}

{#snippet treeItemActions()}
  <UiIconButton
    label="More actions for the handbook"
    size="sm"
  >
    <svg
      aria-hidden="true"
      viewBox="0 0 24 24"
      fill="currentColor"
    >
      <circle
        cx="5"
        cy="12"
        r="1.6"
      ></circle>
      <circle
        cx="12"
        cy="12"
        r="1.6"
      ></circle>
      <circle
        cx="19"
        cy="12"
        r="1.6"
      ></circle>
    </svg>
  </UiIconButton>
{/snippet}

<UiTree
  label="Pages"
  marquee
  style="width: 15rem"
>
  <UiTreeItem
    itemId="overview"
    label="Overview"
    slots={{ actions }}
  >  </UiTreeItem>
  <UiTreeItem
    itemId="handbook"
    label="How to Use This Handbook and Everything Inside It"
    slots={{ actions: treeItemActions }}
  >  </UiTreeItem>
</UiTree>
import { Component } from "@angular/core";
import { UiIconButton, UiTree, UiTreeItem } from "@threadlabs/looma";

@Component({
  selector: "example-view",
  standalone: true,
  imports: [UiTree, UiTreeItem, UiIconButton],
  template: `
    <div
      ui-tree
      [label]="'Pages'"
      [marquee]="true"
      style="width: 15rem"
    >
      <div
        ui-tree-item
        [itemId]="'overview'"
        [label]="'Overview'"
      >
        <ng-container ngProjectAs="[slot='actions']">
          <button
            ui-icon-button
            [label]="'More actions for the overview'"
            [size]="'sm'"
          >
            <svg
              aria-hidden="true"
              viewBox="0 0 24 24"
              fill="currentColor"
            >
              <circle
                cx="5"
                cy="12"
                r="1.6"
              ></circle>
              <circle
                cx="12"
                cy="12"
                r="1.6"
              ></circle>
              <circle
                cx="19"
                cy="12"
                r="1.6"
              ></circle>
            </svg>
          </button>
        </ng-container>
      </div>
      <div
        ui-tree-item
        [itemId]="'handbook'"
        [label]="'How to Use This Handbook and Everything Inside It'"
      >
        <ng-container ngProjectAs="[slot='actions']">
          <button
            ui-icon-button
            [label]="'More actions for the handbook'"
            [size]="'sm'"
          >
            <svg
              aria-hidden="true"
              viewBox="0 0 24 24"
              fill="currentColor"
            >
              <circle
                cx="5"
                cy="12"
                r="1.6"
              ></circle>
              <circle
                cx="12"
                cy="12"
                r="1.6"
              ></circle>
              <circle
                cx="19"
                cy="12"
                r="1.6"
              ></circle>
            </svg>
          </button>
        </ng-container>
      </div>
    </div>
  `,
})
export default class Example {}

Link rows

An icon in leading and a link in label. The whole leaf row follows the link, and with marquee only the name scrolls; the icon stays put.

Code

<link
  rel="component"
  href="@threadlabs/looma/components/ui-tree/ui-tree.html"
>
<link
  rel="component"
  href="@threadlabs/looma/components/ui-tree-item/ui-tree-item.html"
>
<link
  rel="component"
  href="@threadlabs/looma/components/ui-icon/ui-icon.html"
>

<ui-tree
  label="Pages"
  marquee
  style="width: 15rem"
>
  <ui-tree-item
    item-id="welcome"
    label="Welcome"
  >
    <ui-icon
      slot="leading"
      name="pilcrow"
    ></ui-icon>
    <a
      slot="label"
      href="#welcome"
    >Welcome</a>
  </ui-tree-item>
  <ui-tree-item
    item-id="handbook"
    label="How to Use This Handbook and Everything Inside It"
  >
    <ui-icon
      slot="leading"
      name="pilcrow"
    ></ui-icon>
    <a
      slot="label"
      href="#handbook"
    >How to Use This Handbook and Everything Inside It</a>
  </ui-tree-item>
</ui-tree>
<script setup>
  import { UiIcon, UiTree, UiTreeItem } from "@threadlabs/looma";
</script>

<template>
  <UiTree
    label="Pages"
    :marquee="true"
    style="width: 15rem"
  >
    <UiTreeItem
      item-id="welcome"
      label="Welcome"
    >
      <template #leading>
        <UiIcon
          name="pilcrow"
        />
      </template>
      <template #label>
        <a
          href="#welcome"
        >Welcome</a>
      </template>
    </UiTreeItem>
    <UiTreeItem
      item-id="handbook"
      label="How to Use This Handbook and Everything Inside It"
    >
      <template #leading>
        <UiIcon
          name="pilcrow"
        />
      </template>
      <template #label>
        <a
          href="#handbook"
        >How to Use This Handbook and Everything Inside It</a>
      </template>
    </UiTreeItem>
  </UiTree>
</template>
import { UiIcon, UiTree, UiTreeItem } from "@threadlabs/looma";

export default function Example() {
  return <UiTree
    label="Pages"
    marquee
    style={{"width":"15rem"}}
  >
    <UiTreeItem
      itemId="welcome"
      label="Welcome"
      slots={{
        leading: <UiIcon
          name="pilcrow"
        />,
        label: <a
          href="#welcome"
        >Welcome</a>,
      }}
    >{" "}{" "}{" "}</UiTreeItem>
    <UiTreeItem
      itemId="handbook"
      label="How to Use This Handbook and Everything Inside It"
      slots={{
        leading: <UiIcon
          name="pilcrow"
        />,
        label: <a
          href="#handbook"
        >How to Use This Handbook and Everything Inside It</a>,
      }}
    >{" "}{" "}{" "}</UiTreeItem>
  </UiTree>;
}
/** @jsxImportSource preact */
import { UiIcon, UiTree, UiTreeItem } from "@threadlabs/looma";

export default function Example() {
  return <UiTree
    label="Pages"
    marquee
    style={{"width":"15rem"}}
  >
    <UiTreeItem
      itemId="welcome"
      label="Welcome"
      slots={{
        leading: <UiIcon
          name="pilcrow"
        />,
        label: <a
          href="#welcome"
        >Welcome</a>,
      }}
    >{" "}{" "}{" "}</UiTreeItem>
    <UiTreeItem
      itemId="handbook"
      label="How to Use This Handbook and Everything Inside It"
      slots={{
        leading: <UiIcon
          name="pilcrow"
        />,
        label: <a
          href="#handbook"
        >How to Use This Handbook and Everything Inside It</a>,
      }}
    >{" "}{" "}{" "}</UiTreeItem>
  </UiTree>;
}
<script>
  import { UiIcon, UiTree, UiTreeItem } from "@threadlabs/looma";
</script>

{#snippet leading()}
  <UiIcon
    name="pilcrow"
  />
{/snippet}

{#snippet label()}
  <a
    href="#welcome"
  >Welcome</a>
{/snippet}

{#snippet treeItemLeading()}
  <UiIcon
    name="pilcrow"
  />
{/snippet}

{#snippet treeItemLabel()}
  <a
    href="#handbook"
  >How to Use This Handbook and Everything Inside It</a>
{/snippet}

<UiTree
  label="Pages"
  marquee
  style="width: 15rem"
>
  <UiTreeItem
    itemId="welcome"
    label="Welcome"
    slots={{ leading, label }}
  >   </UiTreeItem>
  <UiTreeItem
    itemId="handbook"
    label="How to Use This Handbook and Everything Inside It"
    slots={{ leading: treeItemLeading, label: treeItemLabel }}
  >   </UiTreeItem>
</UiTree>
import { Component } from "@angular/core";
import { UiIcon, UiTree, UiTreeItem } from "@threadlabs/looma";

@Component({
  selector: "example-view",
  standalone: true,
  imports: [UiTree, UiTreeItem, UiIcon],
  template: `
    <div
      ui-tree
      [label]="'Pages'"
      [marquee]="true"
      style="width: 15rem"
    >
      <div
        ui-tree-item
        [itemId]="'welcome'"
        [label]="'Welcome'"
      >
        <ng-container ngProjectAs="[slot='leading']">
          <span
            ui-icon
            name="pilcrow"
          ></span>
        </ng-container>
        <ng-container ngProjectAs="[slot='label']">
          <a
            href="#welcome"
          >Welcome</a>
        </ng-container>
      </div>
      <div
        ui-tree-item
        [itemId]="'handbook'"
        [label]="'How to Use This Handbook and Everything Inside It'"
      >
        <ng-container ngProjectAs="[slot='leading']">
          <span
            ui-icon
            name="pilcrow"
          ></span>
        </ng-container>
        <ng-container ngProjectAs="[slot='label']">
          <a
            href="#handbook"
          >How to Use This Handbook and Everything Inside It</a>
        </ng-container>
      </div>
    </div>
  `,
})
export default class Example {}

Move with clicks or keys

Activate a grip and choose another row with a click, or use Up/Down and Enter. This example applies reorder requests to the authored tree.

Overview
Details
History

Code

<link
  rel="component"
  href="@threadlabs/looma/components/ui-tree/ui-tree.html"
>
<link
  rel="component"
  href="@threadlabs/looma/components/ui-tree-item/ui-tree-item.html"
>

<ui-tree
  label="Pages"
>
  <ui-tree-item
    item-id="overview"
    label="Overview"
    sortable
  ></ui-tree-item>
  <ui-tree-item
    item-id="details"
    label="Details"
    sortable
  ></ui-tree-item>
  <ui-tree-item
    item-id="history"
    label="History"
    sortable
  ></ui-tree-item>
</ui-tree>
<script setup>
  import { UiTree, UiTreeItem } from "@threadlabs/looma";
</script>

<template>
  <UiTree
    label="Pages"
  >
    <UiTreeItem
      item-id="overview"
      label="Overview"
      :sortable="true"
    />
    <UiTreeItem
      item-id="details"
      label="Details"
      :sortable="true"
    />
    <UiTreeItem
      item-id="history"
      label="History"
      :sortable="true"
    />
  </UiTree>
</template>
import { UiTree, UiTreeItem } from "@threadlabs/looma";

export default function Example() {
  return <UiTree
    label="Pages"
  >
    <UiTreeItem
      itemId="overview"
      label="Overview"
      sortable
    />
    <UiTreeItem
      itemId="details"
      label="Details"
      sortable
    />
    <UiTreeItem
      itemId="history"
      label="History"
      sortable
    />
  </UiTree>;
}
/** @jsxImportSource preact */
import { UiTree, UiTreeItem } from "@threadlabs/looma";

export default function Example() {
  return <UiTree
    label="Pages"
  >
    <UiTreeItem
      itemId="overview"
      label="Overview"
      sortable
    />
    <UiTreeItem
      itemId="details"
      label="Details"
      sortable
    />
    <UiTreeItem
      itemId="history"
      label="History"
      sortable
    />
  </UiTree>;
}
<script>
  import { UiTree, UiTreeItem } from "@threadlabs/looma";
</script>

<UiTree
  label="Pages"
>
  <UiTreeItem
    itemId="overview"
    label="Overview"
    sortable
  />
  <UiTreeItem
    itemId="details"
    label="Details"
    sortable
  />
  <UiTreeItem
    itemId="history"
    label="History"
    sortable
  />
</UiTree>
import { Component } from "@angular/core";
import { UiTree, UiTreeItem } from "@threadlabs/looma";

@Component({
  selector: "example-view",
  standalone: true,
  imports: [UiTree, UiTreeItem],
  template: `
    <div
      ui-tree
      [label]="'Pages'"
    >
      <div
        ui-tree-item
        [itemId]="'overview'"
        [label]="'Overview'"
        [sortable]="true"
      ></div>
      <div
        ui-tree-item
        [itemId]="'details'"
        [label]="'Details'"
        [sortable]="true"
      ></div>
      <div
        ui-tree-item
        [itemId]="'history'"
        [label]="'History'"
        [sortable]="true"
      ></div>
    </div>
  `,
})
export default class Example {}

Multiple selection

A selectable tree shows checkboxes. This example applies each select request to its authored items so multiple choices stay checked.

Reports
Notes

Code

<link
  rel="component"
  href="@threadlabs/looma/components/ui-tree/ui-tree.html"
>
<link
  rel="component"
  href="@threadlabs/looma/components/ui-tree-item/ui-tree-item.html"
>

<ui-tree
  label="Documents"
  selection="multiple"
>
  <ui-tree-item
    item-id="reports"
    label="Reports"
    expanded
  >
    <ui-tree-item
      item-id="quarterly"
      label="Quarterly"
      selected
    ></ui-tree-item>
    <ui-tree-item
      item-id="annual"
      label="Annual"
    ></ui-tree-item>
  </ui-tree-item>
  <ui-tree-item
    item-id="notes"
    label="Notes"
  ></ui-tree-item>
</ui-tree>
<script setup>
  import { UiTree, UiTreeItem } from "@threadlabs/looma";
</script>

<template>
  <UiTree
    label="Documents"
    selection="multiple"
  >
    <UiTreeItem
      item-id="reports"
      label="Reports"
      :expanded="true"
    >
      <UiTreeItem
        item-id="quarterly"
        label="Quarterly"
        :selected="true"
      />
      <UiTreeItem
        item-id="annual"
        label="Annual"
      />
    </UiTreeItem>
    <UiTreeItem
      item-id="notes"
      label="Notes"
    />
  </UiTree>
</template>
import { UiTree, UiTreeItem } from "@threadlabs/looma";

export default function Example() {
  return <UiTree
    label="Documents"
    selection="multiple"
  >
    <UiTreeItem
      itemId="reports"
      label="Reports"
      expanded
    >
      <UiTreeItem
        itemId="quarterly"
        label="Quarterly"
        selected
      />
      <UiTreeItem
        itemId="annual"
        label="Annual"
      />
    </UiTreeItem>
    <UiTreeItem
      itemId="notes"
      label="Notes"
    />
  </UiTree>;
}
/** @jsxImportSource preact */
import { UiTree, UiTreeItem } from "@threadlabs/looma";

export default function Example() {
  return <UiTree
    label="Documents"
    selection="multiple"
  >
    <UiTreeItem
      itemId="reports"
      label="Reports"
      expanded
    >
      <UiTreeItem
        itemId="quarterly"
        label="Quarterly"
        selected
      />
      <UiTreeItem
        itemId="annual"
        label="Annual"
      />
    </UiTreeItem>
    <UiTreeItem
      itemId="notes"
      label="Notes"
    />
  </UiTree>;
}
<script>
  import { UiTree, UiTreeItem } from "@threadlabs/looma";
</script>

<UiTree
  label="Documents"
  selection="multiple"
>
  <UiTreeItem
    itemId="reports"
    label="Reports"
    expanded
  >
    <UiTreeItem
      itemId="quarterly"
      label="Quarterly"
      selected
    />
    <UiTreeItem
      itemId="annual"
      label="Annual"
    />
  </UiTreeItem>
  <UiTreeItem
    itemId="notes"
    label="Notes"
  />
</UiTree>
import { Component } from "@angular/core";
import { UiTree, UiTreeItem } from "@threadlabs/looma";

@Component({
  selector: "example-view",
  standalone: true,
  imports: [UiTree, UiTreeItem],
  template: `
    <div
      ui-tree
      [label]="'Documents'"
      [selection]="'multiple'"
    >
      <div
        ui-tree-item
        [itemId]="'reports'"
        [label]="'Reports'"
        [expanded]="true"
      >
        <div
          ui-tree-item
          [itemId]="'quarterly'"
          [label]="'Quarterly'"
          [selected]="true"
        ></div>
        <div
          ui-tree-item
          [itemId]="'annual'"
          [label]="'Annual'"
        ></div>
      </div>
      <div
        ui-tree-item
        [itemId]="'notes'"
        [label]="'Notes'"
      ></div>
    </div>
  `,
})
export default class Example {}

Default

Each item renders its label, an optional leading icon, and a disclosure control when it has children.

docs
src
README.md

Code

<link
  rel="component"
  href="@threadlabs/looma/components/ui-tree/ui-tree.html"
>
<link
  rel="component"
  href="@threadlabs/looma/components/ui-tree-item/ui-tree-item.html"
>

<ui-tree
  label="Project files"
>
  <ui-tree-item
    item-id="docs"
    label="docs"
    expanded
  >
    <svg
      slot="leading"
      aria-hidden="true"
      viewBox="0 0 24 24"
      fill="none"
      stroke="currentColor"
      stroke-width="2"
      stroke-linecap="round"
      stroke-linejoin="round"
    >
      <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"
      ></path>
    </svg>
    <ui-tree-item
      item-id="guide"
      label="guide.md"
    >
      <svg
        slot="leading"
        aria-hidden="true"
        viewBox="0 0 24 24"
        fill="none"
        stroke="currentColor"
        stroke-width="2"
        stroke-linecap="round"
        stroke-linejoin="round"
      >
        <path
          d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
        ></path>
        <path
          d="M14 2v4a2 2 0 0 0 2 2h4"
        ></path>
      </svg>
    </ui-tree-item>
    <ui-tree-item
      item-id="api"
      label="api.md"
    >
      <svg
        slot="leading"
        aria-hidden="true"
        viewBox="0 0 24 24"
        fill="none"
        stroke="currentColor"
        stroke-width="2"
        stroke-linecap="round"
        stroke-linejoin="round"
      >
        <path
          d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
        ></path>
        <path
          d="M14 2v4a2 2 0 0 0 2 2h4"
        ></path>
      </svg>
    </ui-tree-item>
  </ui-tree-item>
  <ui-tree-item
    item-id="src"
    label="src"
  >
    <svg
      slot="leading"
      aria-hidden="true"
      viewBox="0 0 24 24"
      fill="none"
      stroke="currentColor"
      stroke-width="2"
      stroke-linecap="round"
      stroke-linejoin="round"
    >
      <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"
      ></path>
    </svg>
    <ui-tree-item
      item-id="index"
      label="index.ts"
    >
      <svg
        slot="leading"
        aria-hidden="true"
        viewBox="0 0 24 24"
        fill="none"
        stroke="currentColor"
        stroke-width="2"
        stroke-linecap="round"
        stroke-linejoin="round"
      >
        <path
          d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
        ></path>
        <path
          d="M14 2v4a2 2 0 0 0 2 2h4"
        ></path>
      </svg>
    </ui-tree-item>
  </ui-tree-item>
  <ui-tree-item
    item-id="readme"
    label="README.md"
  >
    <svg
      slot="leading"
      aria-hidden="true"
      viewBox="0 0 24 24"
      fill="none"
      stroke="currentColor"
      stroke-width="2"
      stroke-linecap="round"
      stroke-linejoin="round"
    >
      <path
        d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
      ></path>
      <path
        d="M14 2v4a2 2 0 0 0 2 2h4"
      ></path>
    </svg>
  </ui-tree-item>
</ui-tree>
<script setup>
  import { UiTree, UiTreeItem } from "@threadlabs/looma";
</script>

<template>
  <UiTree
    label="Project files"
  >
    <UiTreeItem
      item-id="docs"
      label="docs"
      :expanded="true"
    >
      <template #leading>
        <svg
          aria-hidden="true"
          viewBox="0 0 24 24"
          fill="none"
          stroke="currentColor"
          stroke-width="2"
          stroke-linecap="round"
          stroke-linejoin="round"
        >
          <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"
          ></path>
        </svg>
      </template>
      <UiTreeItem
        item-id="guide"
        label="guide.md"
      >
        <template #leading>
          <svg
            aria-hidden="true"
            viewBox="0 0 24 24"
            fill="none"
            stroke="currentColor"
            stroke-width="2"
            stroke-linecap="round"
            stroke-linejoin="round"
          >
            <path
              d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
            ></path>
            <path
              d="M14 2v4a2 2 0 0 0 2 2h4"
            ></path>
          </svg>
        </template>
      </UiTreeItem>
      <UiTreeItem
        item-id="api"
        label="api.md"
      >
        <template #leading>
          <svg
            aria-hidden="true"
            viewBox="0 0 24 24"
            fill="none"
            stroke="currentColor"
            stroke-width="2"
            stroke-linecap="round"
            stroke-linejoin="round"
          >
            <path
              d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
            ></path>
            <path
              d="M14 2v4a2 2 0 0 0 2 2h4"
            ></path>
          </svg>
        </template>
      </UiTreeItem>
    </UiTreeItem>
    <UiTreeItem
      item-id="src"
      label="src"
    >
      <template #leading>
        <svg
          aria-hidden="true"
          viewBox="0 0 24 24"
          fill="none"
          stroke="currentColor"
          stroke-width="2"
          stroke-linecap="round"
          stroke-linejoin="round"
        >
          <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"
          ></path>
        </svg>
      </template>
      <UiTreeItem
        item-id="index"
        label="index.ts"
      >
        <template #leading>
          <svg
            aria-hidden="true"
            viewBox="0 0 24 24"
            fill="none"
            stroke="currentColor"
            stroke-width="2"
            stroke-linecap="round"
            stroke-linejoin="round"
          >
            <path
              d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
            ></path>
            <path
              d="M14 2v4a2 2 0 0 0 2 2h4"
            ></path>
          </svg>
        </template>
      </UiTreeItem>
    </UiTreeItem>
    <UiTreeItem
      item-id="readme"
      label="README.md"
    >
      <template #leading>
        <svg
          aria-hidden="true"
          viewBox="0 0 24 24"
          fill="none"
          stroke="currentColor"
          stroke-width="2"
          stroke-linecap="round"
          stroke-linejoin="round"
        >
          <path
            d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
          ></path>
          <path
            d="M14 2v4a2 2 0 0 0 2 2h4"
          ></path>
        </svg>
      </template>
    </UiTreeItem>
  </UiTree>
</template>
import { UiTree, UiTreeItem } from "@threadlabs/looma";

export default function Example() {
  return <UiTree
    label="Project files"
  >
    <UiTreeItem
      itemId="docs"
      label="docs"
      expanded
      slots={{
        leading: <svg
          aria-hidden="true"
          viewBox="0 0 24 24"
          fill="none"
          stroke="currentColor"
          stroke-width="2"
          stroke-linecap="round"
          stroke-linejoin="round"
        >
          <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>,
      }}
    >
      <UiTreeItem
        itemId="guide"
        label="guide.md"
        slots={{
          leading: <svg
            aria-hidden="true"
            viewBox="0 0 24 24"
            fill="none"
            stroke="currentColor"
            stroke-width="2"
            stroke-linecap="round"
            stroke-linejoin="round"
          >
            <path
              d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
            />
            <path
              d="M14 2v4a2 2 0 0 0 2 2h4"
            />
          </svg>,
        }}
      >{" "}{" "}</UiTreeItem>
      <UiTreeItem
        itemId="api"
        label="api.md"
        slots={{
          leading: <svg
            aria-hidden="true"
            viewBox="0 0 24 24"
            fill="none"
            stroke="currentColor"
            stroke-width="2"
            stroke-linecap="round"
            stroke-linejoin="round"
          >
            <path
              d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
            />
            <path
              d="M14 2v4a2 2 0 0 0 2 2h4"
            />
          </svg>,
        }}
      >{" "}{" "}</UiTreeItem>
    </UiTreeItem>
    <UiTreeItem
      itemId="src"
      label="src"
      slots={{
        leading: <svg
          aria-hidden="true"
          viewBox="0 0 24 24"
          fill="none"
          stroke="currentColor"
          stroke-width="2"
          stroke-linecap="round"
          stroke-linejoin="round"
        >
          <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>,
      }}
    >
      <UiTreeItem
        itemId="index"
        label="index.ts"
        slots={{
          leading: <svg
            aria-hidden="true"
            viewBox="0 0 24 24"
            fill="none"
            stroke="currentColor"
            stroke-width="2"
            stroke-linecap="round"
            stroke-linejoin="round"
          >
            <path
              d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
            />
            <path
              d="M14 2v4a2 2 0 0 0 2 2h4"
            />
          </svg>,
        }}
      >{" "}{" "}</UiTreeItem>
    </UiTreeItem>
    <UiTreeItem
      itemId="readme"
      label="README.md"
      slots={{
        leading: <svg
          aria-hidden="true"
          viewBox="0 0 24 24"
          fill="none"
          stroke="currentColor"
          stroke-width="2"
          stroke-linecap="round"
          stroke-linejoin="round"
        >
          <path
            d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
          />
          <path
            d="M14 2v4a2 2 0 0 0 2 2h4"
          />
        </svg>,
      }}
    >{" "}{" "}</UiTreeItem>
  </UiTree>;
}
/** @jsxImportSource preact */
import { UiTree, UiTreeItem } from "@threadlabs/looma";

export default function Example() {
  return <UiTree
    label="Project files"
  >
    <UiTreeItem
      itemId="docs"
      label="docs"
      expanded
      slots={{
        leading: <svg
          aria-hidden="true"
          viewBox="0 0 24 24"
          fill="none"
          stroke="currentColor"
          stroke-width="2"
          stroke-linecap="round"
          stroke-linejoin="round"
        >
          <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>,
      }}
    >
      <UiTreeItem
        itemId="guide"
        label="guide.md"
        slots={{
          leading: <svg
            aria-hidden="true"
            viewBox="0 0 24 24"
            fill="none"
            stroke="currentColor"
            stroke-width="2"
            stroke-linecap="round"
            stroke-linejoin="round"
          >
            <path
              d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
            />
            <path
              d="M14 2v4a2 2 0 0 0 2 2h4"
            />
          </svg>,
        }}
      >{" "}{" "}</UiTreeItem>
      <UiTreeItem
        itemId="api"
        label="api.md"
        slots={{
          leading: <svg
            aria-hidden="true"
            viewBox="0 0 24 24"
            fill="none"
            stroke="currentColor"
            stroke-width="2"
            stroke-linecap="round"
            stroke-linejoin="round"
          >
            <path
              d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
            />
            <path
              d="M14 2v4a2 2 0 0 0 2 2h4"
            />
          </svg>,
        }}
      >{" "}{" "}</UiTreeItem>
    </UiTreeItem>
    <UiTreeItem
      itemId="src"
      label="src"
      slots={{
        leading: <svg
          aria-hidden="true"
          viewBox="0 0 24 24"
          fill="none"
          stroke="currentColor"
          stroke-width="2"
          stroke-linecap="round"
          stroke-linejoin="round"
        >
          <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>,
      }}
    >
      <UiTreeItem
        itemId="index"
        label="index.ts"
        slots={{
          leading: <svg
            aria-hidden="true"
            viewBox="0 0 24 24"
            fill="none"
            stroke="currentColor"
            stroke-width="2"
            stroke-linecap="round"
            stroke-linejoin="round"
          >
            <path
              d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
            />
            <path
              d="M14 2v4a2 2 0 0 0 2 2h4"
            />
          </svg>,
        }}
      >{" "}{" "}</UiTreeItem>
    </UiTreeItem>
    <UiTreeItem
      itemId="readme"
      label="README.md"
      slots={{
        leading: <svg
          aria-hidden="true"
          viewBox="0 0 24 24"
          fill="none"
          stroke="currentColor"
          stroke-width="2"
          stroke-linecap="round"
          stroke-linejoin="round"
        >
          <path
            d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
          />
          <path
            d="M14 2v4a2 2 0 0 0 2 2h4"
          />
        </svg>,
      }}
    >{" "}{" "}</UiTreeItem>
  </UiTree>;
}
<script>
  import { UiTree, UiTreeItem } from "@threadlabs/looma";
</script>

{#snippet leading()}
  <svg
    aria-hidden="true"
    viewBox="0 0 24 24"
    fill="none"
    stroke="currentColor"
    stroke-width="2"
    stroke-linecap="round"
    stroke-linejoin="round"
  >
    <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"
    ></path>
  </svg>
{/snippet}

{#snippet treeItemLeading()}
  <svg
    aria-hidden="true"
    viewBox="0 0 24 24"
    fill="none"
    stroke="currentColor"
    stroke-width="2"
    stroke-linecap="round"
    stroke-linejoin="round"
  >
    <path
      d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
    ></path>
    <path
      d="M14 2v4a2 2 0 0 0 2 2h4"
    ></path>
  </svg>
{/snippet}

{#snippet treeItemLeading2()}
  <svg
    aria-hidden="true"
    viewBox="0 0 24 24"
    fill="none"
    stroke="currentColor"
    stroke-width="2"
    stroke-linecap="round"
    stroke-linejoin="round"
  >
    <path
      d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
    ></path>
    <path
      d="M14 2v4a2 2 0 0 0 2 2h4"
    ></path>
  </svg>
{/snippet}

{#snippet treeItemLeading3()}
  <svg
    aria-hidden="true"
    viewBox="0 0 24 24"
    fill="none"
    stroke="currentColor"
    stroke-width="2"
    stroke-linecap="round"
    stroke-linejoin="round"
  >
    <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"
    ></path>
  </svg>
{/snippet}

{#snippet treeItemLeading4()}
  <svg
    aria-hidden="true"
    viewBox="0 0 24 24"
    fill="none"
    stroke="currentColor"
    stroke-width="2"
    stroke-linecap="round"
    stroke-linejoin="round"
  >
    <path
      d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
    ></path>
    <path
      d="M14 2v4a2 2 0 0 0 2 2h4"
    ></path>
  </svg>
{/snippet}

{#snippet treeItemLeading5()}
  <svg
    aria-hidden="true"
    viewBox="0 0 24 24"
    fill="none"
    stroke="currentColor"
    stroke-width="2"
    stroke-linecap="round"
    stroke-linejoin="round"
  >
    <path
      d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
    ></path>
    <path
      d="M14 2v4a2 2 0 0 0 2 2h4"
    ></path>
  </svg>
{/snippet}

<UiTree
  label="Project files"
>
  <UiTreeItem
    itemId="docs"
    label="docs"
    expanded
    slots={{ leading }}
  >
    <UiTreeItem
      itemId="guide"
      label="guide.md"
      slots={{ leading: treeItemLeading }}
    >  </UiTreeItem>
    <UiTreeItem
      itemId="api"
      label="api.md"
      slots={{ leading: treeItemLeading2 }}
    >  </UiTreeItem>
  </UiTreeItem>
  <UiTreeItem
    itemId="src"
    label="src"
    slots={{ leading: treeItemLeading3 }}
  >
    <UiTreeItem
      itemId="index"
      label="index.ts"
      slots={{ leading: treeItemLeading4 }}
    >  </UiTreeItem>
  </UiTreeItem>
  <UiTreeItem
    itemId="readme"
    label="README.md"
    slots={{ leading: treeItemLeading5 }}
  >  </UiTreeItem>
</UiTree>
import { Component } from "@angular/core";
import { UiTree, UiTreeItem } from "@threadlabs/looma";

@Component({
  selector: "example-view",
  standalone: true,
  imports: [UiTree, UiTreeItem],
  template: `
    <div
      ui-tree
      [label]="'Project files'"
    >
      <div
        ui-tree-item
        [itemId]="'docs'"
        [label]="'docs'"
        [expanded]="true"
      >
        <ng-container ngProjectAs="[slot='leading']">
          <svg
            aria-hidden="true"
            viewBox="0 0 24 24"
            fill="none"
            stroke="currentColor"
            stroke-width="2"
            stroke-linecap="round"
            stroke-linejoin="round"
          >
            <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"
            ></path>
          </svg>
        </ng-container>
        <div
          ui-tree-item
          [itemId]="'guide'"
          [label]="'guide.md'"
        >
          <ng-container ngProjectAs="[slot='leading']">
            <svg
              aria-hidden="true"
              viewBox="0 0 24 24"
              fill="none"
              stroke="currentColor"
              stroke-width="2"
              stroke-linecap="round"
              stroke-linejoin="round"
            >
              <path
                d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
              ></path>
              <path
                d="M14 2v4a2 2 0 0 0 2 2h4"
              ></path>
            </svg>
          </ng-container>
        </div>
        <div
          ui-tree-item
          [itemId]="'api'"
          [label]="'api.md'"
        >
          <ng-container ngProjectAs="[slot='leading']">
            <svg
              aria-hidden="true"
              viewBox="0 0 24 24"
              fill="none"
              stroke="currentColor"
              stroke-width="2"
              stroke-linecap="round"
              stroke-linejoin="round"
            >
              <path
                d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
              ></path>
              <path
                d="M14 2v4a2 2 0 0 0 2 2h4"
              ></path>
            </svg>
          </ng-container>
        </div>
      </div>
      <div
        ui-tree-item
        [itemId]="'src'"
        [label]="'src'"
      >
        <ng-container ngProjectAs="[slot='leading']">
          <svg
            aria-hidden="true"
            viewBox="0 0 24 24"
            fill="none"
            stroke="currentColor"
            stroke-width="2"
            stroke-linecap="round"
            stroke-linejoin="round"
          >
            <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"
            ></path>
          </svg>
        </ng-container>
        <div
          ui-tree-item
          [itemId]="'index'"
          [label]="'index.ts'"
        >
          <ng-container ngProjectAs="[slot='leading']">
            <svg
              aria-hidden="true"
              viewBox="0 0 24 24"
              fill="none"
              stroke="currentColor"
              stroke-width="2"
              stroke-linecap="round"
              stroke-linejoin="round"
            >
              <path
                d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
              ></path>
              <path
                d="M14 2v4a2 2 0 0 0 2 2h4"
              ></path>
            </svg>
          </ng-container>
        </div>
      </div>
      <div
        ui-tree-item
        [itemId]="'readme'"
        [label]="'README.md'"
      >
        <ng-container ngProjectAs="[slot='leading']">
          <svg
            aria-hidden="true"
            viewBox="0 0 24 24"
            fill="none"
            stroke="currentColor"
            stroke-width="2"
            stroke-linecap="round"
            stroke-linejoin="round"
          >
            <path
              d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
            ></path>
            <path
              d="M14 2v4a2 2 0 0 0 2 2h4"
            ></path>
          </svg>
        </ng-container>
      </div>
    </div>
  `,
})
export default class Example {}

container

Container marks an item that can hold children even while empty, so it still accepts items dropped inside it.

archive
notes.md

Code

<link
  rel="component"
  href="@threadlabs/looma/components/ui-tree/ui-tree.html"
>
<link
  rel="component"
  href="@threadlabs/looma/components/ui-tree-item/ui-tree-item.html"
>

<ui-tree
  label="Sortable files"
>
  <ui-tree-item
    item-id="archive"
    label="archive"
    container
    sortable
  >
    <svg
      slot="leading"
      aria-hidden="true"
      viewBox="0 0 24 24"
      fill="none"
      stroke="currentColor"
      stroke-width="2"
      stroke-linecap="round"
      stroke-linejoin="round"
    >
      <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"
      ></path>
    </svg>
  </ui-tree-item>
  <ui-tree-item
    item-id="notes"
    label="notes.md"
    sortable
  >
    <svg
      slot="leading"
      aria-hidden="true"
      viewBox="0 0 24 24"
      fill="none"
      stroke="currentColor"
      stroke-width="2"
      stroke-linecap="round"
      stroke-linejoin="round"
    >
      <path
        d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
      ></path>
      <path
        d="M14 2v4a2 2 0 0 0 2 2h4"
      ></path>
    </svg>
  </ui-tree-item>
</ui-tree>
<script setup>
  import { UiTree, UiTreeItem } from "@threadlabs/looma";
</script>

<template>
  <UiTree
    label="Sortable files"
  >
    <UiTreeItem
      item-id="archive"
      label="archive"
      :container="true"
      :sortable="true"
    >
      <template #leading>
        <svg
          aria-hidden="true"
          viewBox="0 0 24 24"
          fill="none"
          stroke="currentColor"
          stroke-width="2"
          stroke-linecap="round"
          stroke-linejoin="round"
        >
          <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"
          ></path>
        </svg>
      </template>
    </UiTreeItem>
    <UiTreeItem
      item-id="notes"
      label="notes.md"
      :sortable="true"
    >
      <template #leading>
        <svg
          aria-hidden="true"
          viewBox="0 0 24 24"
          fill="none"
          stroke="currentColor"
          stroke-width="2"
          stroke-linecap="round"
          stroke-linejoin="round"
        >
          <path
            d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
          ></path>
          <path
            d="M14 2v4a2 2 0 0 0 2 2h4"
          ></path>
        </svg>
      </template>
    </UiTreeItem>
  </UiTree>
</template>
import { UiTree, UiTreeItem } from "@threadlabs/looma";

export default function Example() {
  return <UiTree
    label="Sortable files"
  >
    <UiTreeItem
      itemId="archive"
      label="archive"
      container
      sortable
      slots={{
        leading: <svg
          aria-hidden="true"
          viewBox="0 0 24 24"
          fill="none"
          stroke="currentColor"
          stroke-width="2"
          stroke-linecap="round"
          stroke-linejoin="round"
        >
          <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>,
      }}
    >{" "}{" "}</UiTreeItem>
    <UiTreeItem
      itemId="notes"
      label="notes.md"
      sortable
      slots={{
        leading: <svg
          aria-hidden="true"
          viewBox="0 0 24 24"
          fill="none"
          stroke="currentColor"
          stroke-width="2"
          stroke-linecap="round"
          stroke-linejoin="round"
        >
          <path
            d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
          />
          <path
            d="M14 2v4a2 2 0 0 0 2 2h4"
          />
        </svg>,
      }}
    >{" "}{" "}</UiTreeItem>
  </UiTree>;
}
/** @jsxImportSource preact */
import { UiTree, UiTreeItem } from "@threadlabs/looma";

export default function Example() {
  return <UiTree
    label="Sortable files"
  >
    <UiTreeItem
      itemId="archive"
      label="archive"
      container
      sortable
      slots={{
        leading: <svg
          aria-hidden="true"
          viewBox="0 0 24 24"
          fill="none"
          stroke="currentColor"
          stroke-width="2"
          stroke-linecap="round"
          stroke-linejoin="round"
        >
          <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>,
      }}
    >{" "}{" "}</UiTreeItem>
    <UiTreeItem
      itemId="notes"
      label="notes.md"
      sortable
      slots={{
        leading: <svg
          aria-hidden="true"
          viewBox="0 0 24 24"
          fill="none"
          stroke="currentColor"
          stroke-width="2"
          stroke-linecap="round"
          stroke-linejoin="round"
        >
          <path
            d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
          />
          <path
            d="M14 2v4a2 2 0 0 0 2 2h4"
          />
        </svg>,
      }}
    >{" "}{" "}</UiTreeItem>
  </UiTree>;
}
<script>
  import { UiTree, UiTreeItem } from "@threadlabs/looma";
</script>

{#snippet leading()}
  <svg
    aria-hidden="true"
    viewBox="0 0 24 24"
    fill="none"
    stroke="currentColor"
    stroke-width="2"
    stroke-linecap="round"
    stroke-linejoin="round"
  >
    <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"
    ></path>
  </svg>
{/snippet}

{#snippet treeItemLeading()}
  <svg
    aria-hidden="true"
    viewBox="0 0 24 24"
    fill="none"
    stroke="currentColor"
    stroke-width="2"
    stroke-linecap="round"
    stroke-linejoin="round"
  >
    <path
      d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
    ></path>
    <path
      d="M14 2v4a2 2 0 0 0 2 2h4"
    ></path>
  </svg>
{/snippet}

<UiTree
  label="Sortable files"
>
  <UiTreeItem
    itemId="archive"
    label="archive"
    container
    sortable
    slots={{ leading }}
  >  </UiTreeItem>
  <UiTreeItem
    itemId="notes"
    label="notes.md"
    sortable
    slots={{ leading: treeItemLeading }}
  >  </UiTreeItem>
</UiTree>
import { Component } from "@angular/core";
import { UiTree, UiTreeItem } from "@threadlabs/looma";

@Component({
  selector: "example-view",
  standalone: true,
  imports: [UiTree, UiTreeItem],
  template: `
    <div
      ui-tree
      [label]="'Sortable files'"
    >
      <div
        ui-tree-item
        [itemId]="'archive'"
        [label]="'archive'"
        [container]="true"
        [sortable]="true"
      >
        <ng-container ngProjectAs="[slot='leading']">
          <svg
            aria-hidden="true"
            viewBox="0 0 24 24"
            fill="none"
            stroke="currentColor"
            stroke-width="2"
            stroke-linecap="round"
            stroke-linejoin="round"
          >
            <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"
            ></path>
          </svg>
        </ng-container>
      </div>
      <div
        ui-tree-item
        [itemId]="'notes'"
        [label]="'notes.md'"
        [sortable]="true"
      >
        <ng-container ngProjectAs="[slot='leading']">
          <svg
            aria-hidden="true"
            viewBox="0 0 24 24"
            fill="none"
            stroke="currentColor"
            stroke-width="2"
            stroke-linecap="round"
            stroke-linejoin="round"
          >
            <path
              d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
            ></path>
            <path
              d="M14 2v4a2 2 0 0 0 2 2h4"
            ></path>
          </svg>
        </ng-container>
      </div>
    </div>
  `,
})
export default class Example {}

disabled

A disabled item stays visible but cannot be selected, expanded, or dragged.

docs
src
README.md

Code

<link
  rel="component"
  href="@threadlabs/looma/components/ui-tree/ui-tree.html"
>
<link
  rel="component"
  href="@threadlabs/looma/components/ui-tree-item/ui-tree-item.html"
>

<ui-tree
  label="Project files"
>
  <ui-tree-item
    item-id="docs"
    label="docs"
    expanded
  >
    <svg
      slot="leading"
      aria-hidden="true"
      viewBox="0 0 24 24"
      fill="none"
      stroke="currentColor"
      stroke-width="2"
      stroke-linecap="round"
      stroke-linejoin="round"
    >
      <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"
      ></path>
    </svg>
    <ui-tree-item
      item-id="guide"
      label="guide.md"
    >
      <svg
        slot="leading"
        aria-hidden="true"
        viewBox="0 0 24 24"
        fill="none"
        stroke="currentColor"
        stroke-width="2"
        stroke-linecap="round"
        stroke-linejoin="round"
      >
        <path
          d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
        ></path>
        <path
          d="M14 2v4a2 2 0 0 0 2 2h4"
        ></path>
      </svg>
    </ui-tree-item>
    <ui-tree-item
      item-id="api"
      label="api.md"
    >
      <svg
        slot="leading"
        aria-hidden="true"
        viewBox="0 0 24 24"
        fill="none"
        stroke="currentColor"
        stroke-width="2"
        stroke-linecap="round"
        stroke-linejoin="round"
      >
        <path
          d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
        ></path>
        <path
          d="M14 2v4a2 2 0 0 0 2 2h4"
        ></path>
      </svg>
    </ui-tree-item>
  </ui-tree-item>
  <ui-tree-item
    item-id="src"
    label="src"
  >
    <svg
      slot="leading"
      aria-hidden="true"
      viewBox="0 0 24 24"
      fill="none"
      stroke="currentColor"
      stroke-width="2"
      stroke-linecap="round"
      stroke-linejoin="round"
    >
      <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"
      ></path>
    </svg>
    <ui-tree-item
      item-id="index"
      label="index.ts"
    >
      <svg
        slot="leading"
        aria-hidden="true"
        viewBox="0 0 24 24"
        fill="none"
        stroke="currentColor"
        stroke-width="2"
        stroke-linecap="round"
        stroke-linejoin="round"
      >
        <path
          d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
        ></path>
        <path
          d="M14 2v4a2 2 0 0 0 2 2h4"
        ></path>
      </svg>
    </ui-tree-item>
  </ui-tree-item>
  <ui-tree-item
    item-id="readme"
    label="README.md"
    disabled
  >
    <svg
      slot="leading"
      aria-hidden="true"
      viewBox="0 0 24 24"
      fill="none"
      stroke="currentColor"
      stroke-width="2"
      stroke-linecap="round"
      stroke-linejoin="round"
    >
      <path
        d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
      ></path>
      <path
        d="M14 2v4a2 2 0 0 0 2 2h4"
      ></path>
    </svg>
  </ui-tree-item>
</ui-tree>
<script setup>
  import { UiTree, UiTreeItem } from "@threadlabs/looma";
</script>

<template>
  <UiTree
    label="Project files"
  >
    <UiTreeItem
      item-id="docs"
      label="docs"
      :expanded="true"
    >
      <template #leading>
        <svg
          aria-hidden="true"
          viewBox="0 0 24 24"
          fill="none"
          stroke="currentColor"
          stroke-width="2"
          stroke-linecap="round"
          stroke-linejoin="round"
        >
          <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"
          ></path>
        </svg>
      </template>
      <UiTreeItem
        item-id="guide"
        label="guide.md"
      >
        <template #leading>
          <svg
            aria-hidden="true"
            viewBox="0 0 24 24"
            fill="none"
            stroke="currentColor"
            stroke-width="2"
            stroke-linecap="round"
            stroke-linejoin="round"
          >
            <path
              d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
            ></path>
            <path
              d="M14 2v4a2 2 0 0 0 2 2h4"
            ></path>
          </svg>
        </template>
      </UiTreeItem>
      <UiTreeItem
        item-id="api"
        label="api.md"
      >
        <template #leading>
          <svg
            aria-hidden="true"
            viewBox="0 0 24 24"
            fill="none"
            stroke="currentColor"
            stroke-width="2"
            stroke-linecap="round"
            stroke-linejoin="round"
          >
            <path
              d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
            ></path>
            <path
              d="M14 2v4a2 2 0 0 0 2 2h4"
            ></path>
          </svg>
        </template>
      </UiTreeItem>
    </UiTreeItem>
    <UiTreeItem
      item-id="src"
      label="src"
    >
      <template #leading>
        <svg
          aria-hidden="true"
          viewBox="0 0 24 24"
          fill="none"
          stroke="currentColor"
          stroke-width="2"
          stroke-linecap="round"
          stroke-linejoin="round"
        >
          <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"
          ></path>
        </svg>
      </template>
      <UiTreeItem
        item-id="index"
        label="index.ts"
      >
        <template #leading>
          <svg
            aria-hidden="true"
            viewBox="0 0 24 24"
            fill="none"
            stroke="currentColor"
            stroke-width="2"
            stroke-linecap="round"
            stroke-linejoin="round"
          >
            <path
              d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
            ></path>
            <path
              d="M14 2v4a2 2 0 0 0 2 2h4"
            ></path>
          </svg>
        </template>
      </UiTreeItem>
    </UiTreeItem>
    <UiTreeItem
      item-id="readme"
      label="README.md"
      :disabled="true"
    >
      <template #leading>
        <svg
          aria-hidden="true"
          viewBox="0 0 24 24"
          fill="none"
          stroke="currentColor"
          stroke-width="2"
          stroke-linecap="round"
          stroke-linejoin="round"
        >
          <path
            d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
          ></path>
          <path
            d="M14 2v4a2 2 0 0 0 2 2h4"
          ></path>
        </svg>
      </template>
    </UiTreeItem>
  </UiTree>
</template>
import { UiTree, UiTreeItem } from "@threadlabs/looma";

export default function Example() {
  return <UiTree
    label="Project files"
  >
    <UiTreeItem
      itemId="docs"
      label="docs"
      expanded
      slots={{
        leading: <svg
          aria-hidden="true"
          viewBox="0 0 24 24"
          fill="none"
          stroke="currentColor"
          stroke-width="2"
          stroke-linecap="round"
          stroke-linejoin="round"
        >
          <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>,
      }}
    >
      <UiTreeItem
        itemId="guide"
        label="guide.md"
        slots={{
          leading: <svg
            aria-hidden="true"
            viewBox="0 0 24 24"
            fill="none"
            stroke="currentColor"
            stroke-width="2"
            stroke-linecap="round"
            stroke-linejoin="round"
          >
            <path
              d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
            />
            <path
              d="M14 2v4a2 2 0 0 0 2 2h4"
            />
          </svg>,
        }}
      >{" "}{" "}</UiTreeItem>
      <UiTreeItem
        itemId="api"
        label="api.md"
        slots={{
          leading: <svg
            aria-hidden="true"
            viewBox="0 0 24 24"
            fill="none"
            stroke="currentColor"
            stroke-width="2"
            stroke-linecap="round"
            stroke-linejoin="round"
          >
            <path
              d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
            />
            <path
              d="M14 2v4a2 2 0 0 0 2 2h4"
            />
          </svg>,
        }}
      >{" "}{" "}</UiTreeItem>
    </UiTreeItem>
    <UiTreeItem
      itemId="src"
      label="src"
      slots={{
        leading: <svg
          aria-hidden="true"
          viewBox="0 0 24 24"
          fill="none"
          stroke="currentColor"
          stroke-width="2"
          stroke-linecap="round"
          stroke-linejoin="round"
        >
          <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>,
      }}
    >
      <UiTreeItem
        itemId="index"
        label="index.ts"
        slots={{
          leading: <svg
            aria-hidden="true"
            viewBox="0 0 24 24"
            fill="none"
            stroke="currentColor"
            stroke-width="2"
            stroke-linecap="round"
            stroke-linejoin="round"
          >
            <path
              d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
            />
            <path
              d="M14 2v4a2 2 0 0 0 2 2h4"
            />
          </svg>,
        }}
      >{" "}{" "}</UiTreeItem>
    </UiTreeItem>
    <UiTreeItem
      itemId="readme"
      label="README.md"
      disabled
      slots={{
        leading: <svg
          aria-hidden="true"
          viewBox="0 0 24 24"
          fill="none"
          stroke="currentColor"
          stroke-width="2"
          stroke-linecap="round"
          stroke-linejoin="round"
        >
          <path
            d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
          />
          <path
            d="M14 2v4a2 2 0 0 0 2 2h4"
          />
        </svg>,
      }}
    >{" "}{" "}</UiTreeItem>
  </UiTree>;
}
/** @jsxImportSource preact */
import { UiTree, UiTreeItem } from "@threadlabs/looma";

export default function Example() {
  return <UiTree
    label="Project files"
  >
    <UiTreeItem
      itemId="docs"
      label="docs"
      expanded
      slots={{
        leading: <svg
          aria-hidden="true"
          viewBox="0 0 24 24"
          fill="none"
          stroke="currentColor"
          stroke-width="2"
          stroke-linecap="round"
          stroke-linejoin="round"
        >
          <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>,
      }}
    >
      <UiTreeItem
        itemId="guide"
        label="guide.md"
        slots={{
          leading: <svg
            aria-hidden="true"
            viewBox="0 0 24 24"
            fill="none"
            stroke="currentColor"
            stroke-width="2"
            stroke-linecap="round"
            stroke-linejoin="round"
          >
            <path
              d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
            />
            <path
              d="M14 2v4a2 2 0 0 0 2 2h4"
            />
          </svg>,
        }}
      >{" "}{" "}</UiTreeItem>
      <UiTreeItem
        itemId="api"
        label="api.md"
        slots={{
          leading: <svg
            aria-hidden="true"
            viewBox="0 0 24 24"
            fill="none"
            stroke="currentColor"
            stroke-width="2"
            stroke-linecap="round"
            stroke-linejoin="round"
          >
            <path
              d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
            />
            <path
              d="M14 2v4a2 2 0 0 0 2 2h4"
            />
          </svg>,
        }}
      >{" "}{" "}</UiTreeItem>
    </UiTreeItem>
    <UiTreeItem
      itemId="src"
      label="src"
      slots={{
        leading: <svg
          aria-hidden="true"
          viewBox="0 0 24 24"
          fill="none"
          stroke="currentColor"
          stroke-width="2"
          stroke-linecap="round"
          stroke-linejoin="round"
        >
          <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>,
      }}
    >
      <UiTreeItem
        itemId="index"
        label="index.ts"
        slots={{
          leading: <svg
            aria-hidden="true"
            viewBox="0 0 24 24"
            fill="none"
            stroke="currentColor"
            stroke-width="2"
            stroke-linecap="round"
            stroke-linejoin="round"
          >
            <path
              d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
            />
            <path
              d="M14 2v4a2 2 0 0 0 2 2h4"
            />
          </svg>,
        }}
      >{" "}{" "}</UiTreeItem>
    </UiTreeItem>
    <UiTreeItem
      itemId="readme"
      label="README.md"
      disabled
      slots={{
        leading: <svg
          aria-hidden="true"
          viewBox="0 0 24 24"
          fill="none"
          stroke="currentColor"
          stroke-width="2"
          stroke-linecap="round"
          stroke-linejoin="round"
        >
          <path
            d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
          />
          <path
            d="M14 2v4a2 2 0 0 0 2 2h4"
          />
        </svg>,
      }}
    >{" "}{" "}</UiTreeItem>
  </UiTree>;
}
<script>
  import { UiTree, UiTreeItem } from "@threadlabs/looma";
</script>

{#snippet leading()}
  <svg
    aria-hidden="true"
    viewBox="0 0 24 24"
    fill="none"
    stroke="currentColor"
    stroke-width="2"
    stroke-linecap="round"
    stroke-linejoin="round"
  >
    <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"
    ></path>
  </svg>
{/snippet}

{#snippet treeItemLeading()}
  <svg
    aria-hidden="true"
    viewBox="0 0 24 24"
    fill="none"
    stroke="currentColor"
    stroke-width="2"
    stroke-linecap="round"
    stroke-linejoin="round"
  >
    <path
      d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
    ></path>
    <path
      d="M14 2v4a2 2 0 0 0 2 2h4"
    ></path>
  </svg>
{/snippet}

{#snippet treeItemLeading2()}
  <svg
    aria-hidden="true"
    viewBox="0 0 24 24"
    fill="none"
    stroke="currentColor"
    stroke-width="2"
    stroke-linecap="round"
    stroke-linejoin="round"
  >
    <path
      d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
    ></path>
    <path
      d="M14 2v4a2 2 0 0 0 2 2h4"
    ></path>
  </svg>
{/snippet}

{#snippet treeItemLeading3()}
  <svg
    aria-hidden="true"
    viewBox="0 0 24 24"
    fill="none"
    stroke="currentColor"
    stroke-width="2"
    stroke-linecap="round"
    stroke-linejoin="round"
  >
    <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"
    ></path>
  </svg>
{/snippet}

{#snippet treeItemLeading4()}
  <svg
    aria-hidden="true"
    viewBox="0 0 24 24"
    fill="none"
    stroke="currentColor"
    stroke-width="2"
    stroke-linecap="round"
    stroke-linejoin="round"
  >
    <path
      d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
    ></path>
    <path
      d="M14 2v4a2 2 0 0 0 2 2h4"
    ></path>
  </svg>
{/snippet}

{#snippet treeItemLeading5()}
  <svg
    aria-hidden="true"
    viewBox="0 0 24 24"
    fill="none"
    stroke="currentColor"
    stroke-width="2"
    stroke-linecap="round"
    stroke-linejoin="round"
  >
    <path
      d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
    ></path>
    <path
      d="M14 2v4a2 2 0 0 0 2 2h4"
    ></path>
  </svg>
{/snippet}

<UiTree
  label="Project files"
>
  <UiTreeItem
    itemId="docs"
    label="docs"
    expanded
    slots={{ leading }}
  >
    <UiTreeItem
      itemId="guide"
      label="guide.md"
      slots={{ leading: treeItemLeading }}
    >  </UiTreeItem>
    <UiTreeItem
      itemId="api"
      label="api.md"
      slots={{ leading: treeItemLeading2 }}
    >  </UiTreeItem>
  </UiTreeItem>
  <UiTreeItem
    itemId="src"
    label="src"
    slots={{ leading: treeItemLeading3 }}
  >
    <UiTreeItem
      itemId="index"
      label="index.ts"
      slots={{ leading: treeItemLeading4 }}
    >  </UiTreeItem>
  </UiTreeItem>
  <UiTreeItem
    itemId="readme"
    label="README.md"
    disabled
    slots={{ leading: treeItemLeading5 }}
  >  </UiTreeItem>
</UiTree>
import { Component } from "@angular/core";
import { UiTree, UiTreeItem } from "@threadlabs/looma";

@Component({
  selector: "example-view",
  standalone: true,
  imports: [UiTree, UiTreeItem],
  template: `
    <div
      ui-tree
      [label]="'Project files'"
    >
      <div
        ui-tree-item
        [itemId]="'docs'"
        [label]="'docs'"
        [expanded]="true"
      >
        <ng-container ngProjectAs="[slot='leading']">
          <svg
            aria-hidden="true"
            viewBox="0 0 24 24"
            fill="none"
            stroke="currentColor"
            stroke-width="2"
            stroke-linecap="round"
            stroke-linejoin="round"
          >
            <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"
            ></path>
          </svg>
        </ng-container>
        <div
          ui-tree-item
          [itemId]="'guide'"
          [label]="'guide.md'"
        >
          <ng-container ngProjectAs="[slot='leading']">
            <svg
              aria-hidden="true"
              viewBox="0 0 24 24"
              fill="none"
              stroke="currentColor"
              stroke-width="2"
              stroke-linecap="round"
              stroke-linejoin="round"
            >
              <path
                d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
              ></path>
              <path
                d="M14 2v4a2 2 0 0 0 2 2h4"
              ></path>
            </svg>
          </ng-container>
        </div>
        <div
          ui-tree-item
          [itemId]="'api'"
          [label]="'api.md'"
        >
          <ng-container ngProjectAs="[slot='leading']">
            <svg
              aria-hidden="true"
              viewBox="0 0 24 24"
              fill="none"
              stroke="currentColor"
              stroke-width="2"
              stroke-linecap="round"
              stroke-linejoin="round"
            >
              <path
                d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
              ></path>
              <path
                d="M14 2v4a2 2 0 0 0 2 2h4"
              ></path>
            </svg>
          </ng-container>
        </div>
      </div>
      <div
        ui-tree-item
        [itemId]="'src'"
        [label]="'src'"
      >
        <ng-container ngProjectAs="[slot='leading']">
          <svg
            aria-hidden="true"
            viewBox="0 0 24 24"
            fill="none"
            stroke="currentColor"
            stroke-width="2"
            stroke-linecap="round"
            stroke-linejoin="round"
          >
            <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"
            ></path>
          </svg>
        </ng-container>
        <div
          ui-tree-item
          [itemId]="'index'"
          [label]="'index.ts'"
        >
          <ng-container ngProjectAs="[slot='leading']">
            <svg
              aria-hidden="true"
              viewBox="0 0 24 24"
              fill="none"
              stroke="currentColor"
              stroke-width="2"
              stroke-linecap="round"
              stroke-linejoin="round"
            >
              <path
                d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
              ></path>
              <path
                d="M14 2v4a2 2 0 0 0 2 2h4"
              ></path>
            </svg>
          </ng-container>
        </div>
      </div>
      <div
        ui-tree-item
        [itemId]="'readme'"
        [label]="'README.md'"
        [disabled]="true"
      >
        <ng-container ngProjectAs="[slot='leading']">
          <svg
            aria-hidden="true"
            viewBox="0 0 24 24"
            fill="none"
            stroke="currentColor"
            stroke-width="2"
            stroke-linecap="round"
            stroke-linejoin="round"
          >
            <path
              d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
            ></path>
            <path
              d="M14 2v4a2 2 0 0 0 2 2h4"
            ></path>
          </svg>
        </ng-container>
      </div>
    </div>
  `,
})
export default class Example {}

Actions slot

Content in the actions slot sits at the end of the row, such as a per-item menu button.

docs

Code

<link
  rel="component"
  href="@threadlabs/looma/components/ui-tree/ui-tree.html"
>
<link
  rel="component"
  href="@threadlabs/looma/components/ui-tree-item/ui-tree-item.html"
>
<link
  rel="component"
  href="@threadlabs/looma/components/ui-icon-button/ui-icon-button.html"
>

<ui-tree
  label="Project files"
>
  <ui-tree-item
    item-id="docs"
    label="docs"
    expanded
  >
    <svg
      slot="leading"
      aria-hidden="true"
      viewBox="0 0 24 24"
      fill="none"
      stroke="currentColor"
      stroke-width="2"
      stroke-linecap="round"
      stroke-linejoin="round"
    >
      <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"
      ></path>
    </svg>
    <ui-icon-button
      slot="actions"
      label="More actions for docs"
      size="sm"
    >
      <svg
        aria-hidden="true"
        viewBox="0 0 24 24"
        fill="currentColor"
      >
        <circle
          cx="5"
          cy="12"
          r="1.6"
        ></circle>
        <circle
          cx="12"
          cy="12"
          r="1.6"
        ></circle>
        <circle
          cx="19"
          cy="12"
          r="1.6"
        ></circle>
      </svg>
    </ui-icon-button>
    <ui-tree-item
      item-id="guide"
      label="guide.md"
    >
      <svg
        slot="leading"
        aria-hidden="true"
        viewBox="0 0 24 24"
        fill="none"
        stroke="currentColor"
        stroke-width="2"
        stroke-linecap="round"
        stroke-linejoin="round"
      >
        <path
          d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
        ></path>
        <path
          d="M14 2v4a2 2 0 0 0 2 2h4"
        ></path>
      </svg>
      <ui-icon-button
        slot="actions"
        label="More actions for guide.md"
        size="sm"
      >
        <svg
          aria-hidden="true"
          viewBox="0 0 24 24"
          fill="currentColor"
        >
          <circle
            cx="5"
            cy="12"
            r="1.6"
          ></circle>
          <circle
            cx="12"
            cy="12"
            r="1.6"
          ></circle>
          <circle
            cx="19"
            cy="12"
            r="1.6"
          ></circle>
        </svg>
      </ui-icon-button>
    </ui-tree-item>
  </ui-tree-item>
</ui-tree>
<script setup>
  import { UiIconButton, UiTree, UiTreeItem } from "@threadlabs/looma";
</script>

<template>
  <UiTree
    label="Project files"
  >
    <UiTreeItem
      item-id="docs"
      label="docs"
      :expanded="true"
    >
      <template #leading>
        <svg
          aria-hidden="true"
          viewBox="0 0 24 24"
          fill="none"
          stroke="currentColor"
          stroke-width="2"
          stroke-linecap="round"
          stroke-linejoin="round"
        >
          <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"
          ></path>
        </svg>
      </template>
      <template #actions>
        <UiIconButton
          label="More actions for docs"
          size="sm"
        >
          <svg
            aria-hidden="true"
            viewBox="0 0 24 24"
            fill="currentColor"
          >
            <circle
              cx="5"
              cy="12"
              r="1.6"
            ></circle>
            <circle
              cx="12"
              cy="12"
              r="1.6"
            ></circle>
            <circle
              cx="19"
              cy="12"
              r="1.6"
            ></circle>
          </svg>
        </UiIconButton>
      </template>
      <UiTreeItem
        item-id="guide"
        label="guide.md"
      >
        <template #leading>
          <svg
            aria-hidden="true"
            viewBox="0 0 24 24"
            fill="none"
            stroke="currentColor"
            stroke-width="2"
            stroke-linecap="round"
            stroke-linejoin="round"
          >
            <path
              d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
            ></path>
            <path
              d="M14 2v4a2 2 0 0 0 2 2h4"
            ></path>
          </svg>
        </template>
        <template #actions>
          <UiIconButton
            label="More actions for guide.md"
            size="sm"
          >
            <svg
              aria-hidden="true"
              viewBox="0 0 24 24"
              fill="currentColor"
            >
              <circle
                cx="5"
                cy="12"
                r="1.6"
              ></circle>
              <circle
                cx="12"
                cy="12"
                r="1.6"
              ></circle>
              <circle
                cx="19"
                cy="12"
                r="1.6"
              ></circle>
            </svg>
          </UiIconButton>
        </template>
      </UiTreeItem>
    </UiTreeItem>
  </UiTree>
</template>
import { UiIconButton, UiTree, UiTreeItem } from "@threadlabs/looma";

export default function Example() {
  return <UiTree
    label="Project files"
  >
    <UiTreeItem
      itemId="docs"
      label="docs"
      expanded
      slots={{
        leading: <svg
          aria-hidden="true"
          viewBox="0 0 24 24"
          fill="none"
          stroke="currentColor"
          stroke-width="2"
          stroke-linecap="round"
          stroke-linejoin="round"
        >
          <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>,
        actions: <UiIconButton
          label="More actions for docs"
          size="sm"
        >
          <svg
            aria-hidden="true"
            viewBox="0 0 24 24"
            fill="currentColor"
          >
            <circle
              cx="5"
              cy="12"
              r="1.6"
            />
            <circle
              cx="12"
              cy="12"
              r="1.6"
            />
            <circle
              cx="19"
              cy="12"
              r="1.6"
            />
          </svg>
        </UiIconButton>,
      }}
    >
      <UiTreeItem
        itemId="guide"
        label="guide.md"
        slots={{
          leading: <svg
            aria-hidden="true"
            viewBox="0 0 24 24"
            fill="none"
            stroke="currentColor"
            stroke-width="2"
            stroke-linecap="round"
            stroke-linejoin="round"
          >
            <path
              d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
            />
            <path
              d="M14 2v4a2 2 0 0 0 2 2h4"
            />
          </svg>,
          actions: <UiIconButton
            label="More actions for guide.md"
            size="sm"
          >
            <svg
              aria-hidden="true"
              viewBox="0 0 24 24"
              fill="currentColor"
            >
              <circle
                cx="5"
                cy="12"
                r="1.6"
              />
              <circle
                cx="12"
                cy="12"
                r="1.6"
              />
              <circle
                cx="19"
                cy="12"
                r="1.6"
              />
            </svg>
          </UiIconButton>,
        }}
      >{" "}{" "}{" "}</UiTreeItem>
    </UiTreeItem>
  </UiTree>;
}
/** @jsxImportSource preact */
import { UiIconButton, UiTree, UiTreeItem } from "@threadlabs/looma";

export default function Example() {
  return <UiTree
    label="Project files"
  >
    <UiTreeItem
      itemId="docs"
      label="docs"
      expanded
      slots={{
        leading: <svg
          aria-hidden="true"
          viewBox="0 0 24 24"
          fill="none"
          stroke="currentColor"
          stroke-width="2"
          stroke-linecap="round"
          stroke-linejoin="round"
        >
          <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>,
        actions: <UiIconButton
          label="More actions for docs"
          size="sm"
        >
          <svg
            aria-hidden="true"
            viewBox="0 0 24 24"
            fill="currentColor"
          >
            <circle
              cx="5"
              cy="12"
              r="1.6"
            />
            <circle
              cx="12"
              cy="12"
              r="1.6"
            />
            <circle
              cx="19"
              cy="12"
              r="1.6"
            />
          </svg>
        </UiIconButton>,
      }}
    >
      <UiTreeItem
        itemId="guide"
        label="guide.md"
        slots={{
          leading: <svg
            aria-hidden="true"
            viewBox="0 0 24 24"
            fill="none"
            stroke="currentColor"
            stroke-width="2"
            stroke-linecap="round"
            stroke-linejoin="round"
          >
            <path
              d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
            />
            <path
              d="M14 2v4a2 2 0 0 0 2 2h4"
            />
          </svg>,
          actions: <UiIconButton
            label="More actions for guide.md"
            size="sm"
          >
            <svg
              aria-hidden="true"
              viewBox="0 0 24 24"
              fill="currentColor"
            >
              <circle
                cx="5"
                cy="12"
                r="1.6"
              />
              <circle
                cx="12"
                cy="12"
                r="1.6"
              />
              <circle
                cx="19"
                cy="12"
                r="1.6"
              />
            </svg>
          </UiIconButton>,
        }}
      >{" "}{" "}{" "}</UiTreeItem>
    </UiTreeItem>
  </UiTree>;
}
<script>
  import { UiIconButton, UiTree, UiTreeItem } from "@threadlabs/looma";
</script>

{#snippet leading()}
  <svg
    aria-hidden="true"
    viewBox="0 0 24 24"
    fill="none"
    stroke="currentColor"
    stroke-width="2"
    stroke-linecap="round"
    stroke-linejoin="round"
  >
    <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"
    ></path>
  </svg>
{/snippet}

{#snippet actions()}
  <UiIconButton
    label="More actions for docs"
    size="sm"
  >
    <svg
      aria-hidden="true"
      viewBox="0 0 24 24"
      fill="currentColor"
    >
      <circle
        cx="5"
        cy="12"
        r="1.6"
      ></circle>
      <circle
        cx="12"
        cy="12"
        r="1.6"
      ></circle>
      <circle
        cx="19"
        cy="12"
        r="1.6"
      ></circle>
    </svg>
  </UiIconButton>
{/snippet}

{#snippet treeItemLeading()}
  <svg
    aria-hidden="true"
    viewBox="0 0 24 24"
    fill="none"
    stroke="currentColor"
    stroke-width="2"
    stroke-linecap="round"
    stroke-linejoin="round"
  >
    <path
      d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
    ></path>
    <path
      d="M14 2v4a2 2 0 0 0 2 2h4"
    ></path>
  </svg>
{/snippet}

{#snippet treeItemActions()}
  <UiIconButton
    label="More actions for guide.md"
    size="sm"
  >
    <svg
      aria-hidden="true"
      viewBox="0 0 24 24"
      fill="currentColor"
    >
      <circle
        cx="5"
        cy="12"
        r="1.6"
      ></circle>
      <circle
        cx="12"
        cy="12"
        r="1.6"
      ></circle>
      <circle
        cx="19"
        cy="12"
        r="1.6"
      ></circle>
    </svg>
  </UiIconButton>
{/snippet}

<UiTree
  label="Project files"
>
  <UiTreeItem
    itemId="docs"
    label="docs"
    expanded
    slots={{ leading, actions }}
  >
    <UiTreeItem
      itemId="guide"
      label="guide.md"
      slots={{ leading: treeItemLeading, actions: treeItemActions }}
    >   </UiTreeItem>
  </UiTreeItem>
</UiTree>
import { Component } from "@angular/core";
import { UiIconButton, UiTree, UiTreeItem } from "@threadlabs/looma";

@Component({
  selector: "example-view",
  standalone: true,
  imports: [UiTree, UiTreeItem, UiIconButton],
  template: `
    <div
      ui-tree
      [label]="'Project files'"
    >
      <div
        ui-tree-item
        [itemId]="'docs'"
        [label]="'docs'"
        [expanded]="true"
      >
        <ng-container ngProjectAs="[slot='leading']">
          <svg
            aria-hidden="true"
            viewBox="0 0 24 24"
            fill="none"
            stroke="currentColor"
            stroke-width="2"
            stroke-linecap="round"
            stroke-linejoin="round"
          >
            <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"
            ></path>
          </svg>
        </ng-container>
        <ng-container ngProjectAs="[slot='actions']">
          <button
            ui-icon-button
            [label]="'More actions for docs'"
            [size]="'sm'"
          >
            <svg
              aria-hidden="true"
              viewBox="0 0 24 24"
              fill="currentColor"
            >
              <circle
                cx="5"
                cy="12"
                r="1.6"
              ></circle>
              <circle
                cx="12"
                cy="12"
                r="1.6"
              ></circle>
              <circle
                cx="19"
                cy="12"
                r="1.6"
              ></circle>
            </svg>
          </button>
        </ng-container>
        <div
          ui-tree-item
          [itemId]="'guide'"
          [label]="'guide.md'"
        >
          <ng-container ngProjectAs="[slot='leading']">
            <svg
              aria-hidden="true"
              viewBox="0 0 24 24"
              fill="none"
              stroke="currentColor"
              stroke-width="2"
              stroke-linecap="round"
              stroke-linejoin="round"
            >
              <path
                d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
              ></path>
              <path
                d="M14 2v4a2 2 0 0 0 2 2h4"
              ></path>
            </svg>
          </ng-container>
          <ng-container ngProjectAs="[slot='actions']">
            <button
              ui-icon-button
              [label]="'More actions for guide.md'"
              [size]="'sm'"
            >
              <svg
                aria-hidden="true"
                viewBox="0 0 24 24"
                fill="currentColor"
              >
                <circle
                  cx="5"
                  cy="12"
                  r="1.6"
                ></circle>
                <circle
                  cx="12"
                  cy="12"
                  r="1.6"
                ></circle>
                <circle
                  cx="19"
                  cy="12"
                  r="1.6"
                ></circle>
              </svg>
            </button>
          </ng-container>
        </div>
      </div>
    </div>
  `,
})
export default class Example {}

Lazy branch

An empty branch can expand and report busy while the application loads its children after the expand event.

Remote folder

Code

<link
  rel="component"
  href="@threadlabs/looma/components/ui-tree/ui-tree.html"
>
<link
  rel="component"
  href="@threadlabs/looma/components/ui-tree-item/ui-tree-item.html"
>

<ui-tree
  label="Folders"
>
  <ui-tree-item
    item-id="remote"
    label="Remote folder"
    lazy
  ></ui-tree-item>
</ui-tree>
<script setup>
  import { UiTree, UiTreeItem } from "@threadlabs/looma";
</script>

<template>
  <UiTree
    label="Folders"
  >
    <UiTreeItem
      item-id="remote"
      label="Remote folder"
      :lazy="true"
    />
  </UiTree>
</template>
import { UiTree, UiTreeItem } from "@threadlabs/looma";

export default function Example() {
  return <UiTree
    label="Folders"
  >
    <UiTreeItem
      itemId="remote"
      label="Remote folder"
      lazy
    />
  </UiTree>;
}
/** @jsxImportSource preact */
import { UiTree, UiTreeItem } from "@threadlabs/looma";

export default function Example() {
  return <UiTree
    label="Folders"
  >
    <UiTreeItem
      itemId="remote"
      label="Remote folder"
      lazy
    />
  </UiTree>;
}
<script>
  import { UiTree, UiTreeItem } from "@threadlabs/looma";
</script>

<UiTree
  label="Folders"
>
  <UiTreeItem
    itemId="remote"
    label="Remote folder"
    lazy
  />
</UiTree>
import { Component } from "@angular/core";
import { UiTree, UiTreeItem } from "@threadlabs/looma";

@Component({
  selector: "example-view",
  standalone: true,
  imports: [UiTree, UiTreeItem],
  template: `
    <div
      ui-tree
      [label]="'Folders'"
    >
      <div
        ui-tree-item
        [itemId]="'remote'"
        [label]="'Remote folder'"
        [lazy]="true"
      ></div>
    </div>
  `,
})
export default class Example {}

Persistent actions

A status stays visible at the end of the row while the title fades beneath it, even before hover or focus.

A long project guide name that continues beneath its status 3

Code

<link
  rel="component"
  href="@threadlabs/looma/components/ui-tree/ui-tree.html"
>
<link
  rel="component"
  href="@threadlabs/looma/components/ui-tree-item/ui-tree-item.html"
>
<link
  rel="component"
  href="@threadlabs/looma/components/ui-badge/ui-badge.html"
>

<ui-tree
  label="Project files"
>
  <ui-tree-item
    item-id="guide"
    label="A long project guide name that continues beneath its status"
    actions-visible
  >
    <ui-badge
      slot="actions"
      tone="info"
      variant="outline"
      size="xs"
    >3</ui-badge>
  </ui-tree-item>
</ui-tree>
<script setup>
  import { UiBadge, UiTree, UiTreeItem } from "@threadlabs/looma";
</script>

<template>
  <UiTree
    label="Project files"
  >
    <UiTreeItem
      item-id="guide"
      label="A long project guide name that continues beneath its status"
      :actions-visible="true"
    >
      <template #actions>
        <UiBadge
          tone="info"
          variant="outline"
          size="xs"
        >3</UiBadge>
      </template>
    </UiTreeItem>
  </UiTree>
</template>
import { UiBadge, UiTree, UiTreeItem } from "@threadlabs/looma";

export default function Example() {
  return <UiTree
    label="Project files"
  >
    <UiTreeItem
      itemId="guide"
      label="A long project guide name that continues beneath its status"
      actionsVisible
      slots={{
        actions: <UiBadge
          tone="info"
          variant="outline"
          size="xs"
        >3</UiBadge>,
      }}
    >{" "}{" "}</UiTreeItem>
  </UiTree>;
}
/** @jsxImportSource preact */
import { UiBadge, UiTree, UiTreeItem } from "@threadlabs/looma";

export default function Example() {
  return <UiTree
    label="Project files"
  >
    <UiTreeItem
      itemId="guide"
      label="A long project guide name that continues beneath its status"
      actionsVisible
      slots={{
        actions: <UiBadge
          tone="info"
          variant="outline"
          size="xs"
        >3</UiBadge>,
      }}
    >{" "}{" "}</UiTreeItem>
  </UiTree>;
}
<script>
  import { UiBadge, UiTree, UiTreeItem } from "@threadlabs/looma";
</script>

{#snippet actions()}
  <UiBadge
    tone="info"
    variant="outline"
    size="xs"
  >3</UiBadge>
{/snippet}

<UiTree
  label="Project files"
>
  <UiTreeItem
    itemId="guide"
    label="A long project guide name that continues beneath its status"
    actionsVisible
    slots={{ actions }}
  >  </UiTreeItem>
</UiTree>
import { Component } from "@angular/core";
import { UiBadge, UiTree, UiTreeItem } from "@threadlabs/looma";

@Component({
  selector: "example-view",
  standalone: true,
  imports: [UiTree, UiTreeItem, UiBadge],
  template: `
    <div
      ui-tree
      [label]="'Project files'"
    >
      <div
        ui-tree-item
        [itemId]="'guide'"
        [label]="'A long project guide name that continues beneath its status'"
        [actionsVisible]="true"
      >
        <ng-container ngProjectAs="[slot='actions']">
          <span
            ui-badge
            [tone]="'info'"
            [variant]="'outline'"
            [size]="'xs'"
          >3</span>
        </ng-container>
      </div>
    </div>
  `,
})
export default class Example {}

API

Package: @threadlabs/looma · Native root: div

Attributes

NameDescriptionPropertyTypeDefaultAllowed values
densityCompact trades padding and type size for rows that fit more on screen.densitykeywordcomfortablecomfortable, compact
hover-expand-delayHow long, in milliseconds, a dragged item must hover over the middle of a collapsed branch before the branch opens to receive it. 0 opens it at once.hoverExpandDelaynumber700—
labelThe tree's accessible name, announced when focus enters it. Name what the tree holds.labelstringTree—
marqueeScrolls a name too long for its row while that row is hovered or keyboard focused, rests at the end, then scrolls it again. Off by default: a tree of names that fit should not move. An item may set its own marquee to opt in or out on its own.marqueebooleanfalse—
max-depthThe deepest level a drag may produce, counting the top level as 1. A drop that would place the dragged item, or any item beneath it, deeper than this is refused and reported as reorder-rejected. 0 means no limit.maxDepthnumber0—
move-activationWhich handle activations start guided moving. click preserves two-click moving with any pointer. keyboard-touch leaves ordinary mouse and pen clicks inert while retaining keyboard activation and touch taps. Actual pointer dragging works in either mode. Guided moves show Cancel on the source row; Escape or reactivating that source handle also cancels without requesting a reorder.moveActivationkeywordclickclick, keyboard-touch
selectionHow selection works. None keeps rows for navigation and expansion; single requests one selected item; multiple shows checkboxes and requests an item with its descendants. The application owns each Tree Item's selected state and responds to select. Single-selection rows also request activate on a row click or Enter, including an already selected row. Space requests selection only; controls and disabled rows do not activate.selectionkeywordnonenone, single, multiple

Framework props

NameTypeDefaultAllowed values
density"comfortable" | "compact"comfortablecomfortable, compact
hoverExpandDelaynumber700—
labelstringTree—
marqueebooleanfalse—
maxDepthnumber0—
moveActivation"click" | "keyboard-touch"clickclick, keyboard-touch
selection"none" | "single" | "multiple"nonenone, single, multiple

Events

NameDetailDescription
activateobject
  • id: string (required)
  • trigger: keyword (required)
Emitted with the declared detail payload.
reorderobject
  • sourceId: string (required)
  • targetId: string (required)
  • position: keyword (required)
  • sourceType: string (required)
  • targetType: string (required)
  • sourceScope: string (required)
  • targetScope: string (required)
  • trigger: keyword (required)
Emitted with the declared detail payload.
reorder-rejectedobject
  • sourceId: string (required)
  • targetId: string (required)
  • position: keyword (required)
  • reason: keyword (required)
  • maxDepth: integer (required)
  • resultingDepth: integer (required)
  • trigger: keyword (required)
Emitted with the declared detail payload.
selectobject
  • ids: list(string) (required)
  • trigger: keyword (required)
Emitted when a selectable option becomes active.

Slots

NamePurpose
defaultDefault child content.

Design tokens

Set shared theme properties on a theme boundary. Component hooks apply only to their owning component.

Component tokens

TokenDeclared valuesFallback
--ui-tree-drop-color——
--ui-tree-gutter—0px
--ui-tree-indent——

Shared tokens

TokenFallback
--ui-control-size-sm—
--ui-font-size-sm—
--ui-space-2—
--ui-text-secondary—

Tree Item API

The row primitive used inside ui-tree.

Package: @threadlabs/looma · Native root: div

Attributes

NameDescriptionPropertyTypeDefaultAllowed values
acceptsA comma-separated list of the dragType values this branch accepts dropped inside it. Empty accepts any type. It does not affect drops before or after the item.acceptsstring—
actions-visibleKeeps the actions slot visible before hover or focus, for persistent status or controls. The label fades beneath its measured width on pointer devices; touch rows retain a separate actions column with a short end fade. Does not change keyboard navigation or start the marquee.actionsVisiblebooleanfalse—
containerMakes the item a branch even before it has children: it gets the expand button and aria-expanded, and accepts drops inside it. An item with nested items is a branch without it.containerbooleanfalse—
disabledMakes the item unavailable: faded, skipped by keyboard navigation, unable to expand or be dragged, and closed to drops inside it.disabledbooleanfalse—
drag-typeThe item's kind, for drag rules: it can be dropped before or after only items of the same type, and inside a branch only if that branch's accepts allows it. Reported as sourceType and targetType.dragTypestringitem—
drop-depthOverrides the level this item counts as when the tree checks maxDepth for a drop onto it. Unset, its level in the rendered tree is used; set it when the item sits deeper in the real hierarchy than the rendered tree shows.dropDepthnumber——
drop-scopeNames the list the item sits in, such as its parent's id, and is reported as sourceScope and targetScope in the tree's reorder events, so a handler can tell a reorder within one list from a move between lists. It never restricts where an item can be dropped; dragType and accepts do that.dropScopestring—
expandedWhether the branch shows its children, initially or when set externally. The expand button, a click on the row, and the arrow keys toggle it from there, each reported as an expand event. Only meaningful for a branch.expandedbooleanfalse—
item-idThe item's identifier, reported as sourceId and targetId in the tree's reorder events and as id in its expand event. A sortable item needs one to be dragged, and a drop onto an item without one is not reported.itemIdstring—
labelThe item's accessible name, also shown as the row text unless the label slot supplies content.labelstring—
lazyKeeps an empty item expandable while its children are supplied later. Expanding emits expand; the item is aria-busy until child Tree Items are appended. Clear lazy if loading finishes with no children.lazybooleanfalse—
marqueeScrolls a name too long for its row while a pointer hovers the row or keyboard focus is in it, rests at the end, then scrolls it again from the start. A touch does not start it. Off by default: a tree of short names should not move, and a product decides whether reading the whole name matters more than a still list.marqueebooleanfalse—
selectedMarks the item selected: it sets aria-selected and highlights the row. This is controlled state; respond to the parent Tree's select request by updating it.selectedbooleanfalse—
sortableGives the item a move handle. Drag it, or activate it according to the tree's moveActivation and choose a target by a second click or the keyboard. A guided move exposes Cancel on this row. The tree reports reorder and leaves moving the item to you. Needs itemId; disabled items show no handle.sortablebooleanfalse—
subtree-depthOverrides how many levels sit beneath this item when the tree checks maxDepth for a drag of it. Unset, the tree counts the rendered descendants; set it when some are not rendered, such as children that load later.subtreeDepthnumber——

Framework props

NameTypeDefaultAllowed values
acceptsstring—
actionsVisiblebooleanfalse—
containerbooleanfalse—
disabledbooleanfalse—
dragTypestringitem—
dropDepthnumber——
dropScopestring—
expandedbooleanfalse—
itemIdstring—
labelstring—
lazybooleanfalse—
marqueebooleanfalse—
selectedbooleanfalse—
sortablebooleanfalse—
subtreeDepthnumber——

Events

NameDetailDescription
expandobject
  • id: string (required)
  • expanded: boolean (required)
  • trigger: keyword (required)
Emitted with the declared detail payload.

Slots

NamePurpose
leadingNamed content for the leading region.
labelNamed content for the label region.
actionsNamed content for the actions region.
defaultDefault child content.

Design tokens

Set shared theme properties on a theme boundary. Component hooks apply only to their owning component.

Component tokens

TokenDeclared valuesFallback
--ui-tree-item-font-size—var(--ui-font-size-sm)
--ui-tree-item-label-padding-block—var(--ui-space-1)
--ui-tree-item-label-padding-inline—var(--ui-space-2)
--ui-tree-item-min-block-size32px | 44px—

Shared tokens

TokenFallback
--ui-accent-line-width—
--ui-accent-solid—
--ui-accent-subtle—
--ui-border-width—
--ui-disabled-surface—
--ui-disabled-text—
--ui-focus-ring—
--ui-focus-width—
--ui-font-size-sm—
--ui-motion-base—
--ui-motion-ease—
--ui-motion-fast—
--ui-radius-round—
--ui-radius-sm—
--ui-selection-border—
--ui-selection-radius—
--ui-selection-surface—
--ui-selection-text—
--ui-space-1—
--ui-space-2—
--ui-surface-elevated—
--ui-surface-hover—
--ui-surface-muted—
--ui-text-muted—
--ui-text-primary—
--ui-text-secondary—