# Branched Menu

> Collapsible menu that branches each section from a trunk and traces a line to your pick.

A sidebar menu where each section folds open into a tree. Its children hang off a trunk on curved branches. Pick a child and an accent line draws from the top of the trunk down its branch to the pick, while a marker on the rail slides to that section's header. Click a header to fold or unfold it.

<demo-branched-menu>



</demo-branched-menu>

## Installation

```bash
npx shadcn-vue@latest add "https://nxui.geoql.in/r/branched-menu.json"
```

## Usage

```vue
<script setup lang="ts">
  import BranchedMenu from '~/components/ui/branched-menu/BranchedMenu.vue';
  import type { BranchedMenuItem } from '~/components/ui/branched-menu';

  const items: BranchedMenuItem[] = [
    {
      label: 'Getting started',
      children: [
        { value: 'install', label: 'Installation', icon: 'lucide:download' },
        { value: 'quick', label: 'Quick start', icon: 'lucide:rocket' },
      ],
    },
    { label: 'Changelog', value: 'changelog' },
  ];

  function onSelect(value: string) {
    console.log(value);
  }
</script>

<template>
  <BranchedMenu
    :items="items"
    :default-open="0"
    default-active="quick"
    @select="onSelect"
  />
</template>
```

Each branch is one SVG path from the trunk, around an arc of `radius`, to just before `indent`. The accent path for a child runs from the top of the trunk to that child; its dash offset animates from its full length to 0 when it is picked, so the line appears to travel. Sections fold with a `grid-template-rows` transition from `0fr` to `1fr`.

## Props

<table>
<thead>
  <tr>
    <th>
      Prop
    </th>
    
    <th>
      Type
    </th>
    
    <th>
      Default
    </th>
    
    <th>
      Description
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        items
      </code>
    </td>
    
    <td>
      <code>
        BranchedMenuItem[]
      </code>
    </td>
    
    <td>
      two sample sections
    </td>
    
    <td>
      Sections: a label plus children (<code>
        value
      </code>
      
      , <code>
        label
      </code>
      
      , <code>
        icon
      </code>
      
      ), or a leaf with a value. A section with children folds; a leaf selects.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        default-open
      </code>
    </td>
    
    <td>
      <code>
        number | number[]
      </code>
    </td>
    
    <td>
      <code>
        0
      </code>
    </td>
    
    <td>
      The section, or sections, open at first. -1 for none.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        default-active
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        ''
      </code>
    </td>
    
    <td>
      Value selected at first. Empty picks the open section's first child.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        color
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        'var(--foreground)'
      </code>
    </td>
    
    <td>
      The ink. Idle text is a mix of it.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        accent-color
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        'var(--foreground)'
      </code>
    </td>
    
    <td>
      The active line, the active label and the marker.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        line-color
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        'color-mix(in oklab, var(--foreground) 24%, var(--background))'
      </code>
    </td>
    
    <td>
      The rail, trunk and branches. A solid colour, so joints never darken.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        width
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        240
      </code>
    </td>
    
    <td>
      The widest the menu may be, in px. It shrinks to its content.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        row-height
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        36
      </code>
    </td>
    
    <td>
      Height of a child row in px.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        indent
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        40
      </code>
    </td>
    
    <td>
      Where child rows start, in px. Branches end just before it.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        trunk
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        14
      </code>
    </td>
    
    <td>
      The trunk line's x position, in px.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        radius
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        10
      </code>
    </td>
    
    <td>
      The curve of each branch, in px.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        line-width
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        1.5
      </code>
    </td>
    
    <td>
      Stroke width of the lines.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        font-size
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        14
      </code>
    </td>
    
    <td>
      Child text size in px. Headers are 1px larger.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        draw-duration
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        400
      </code>
    </td>
    
    <td>
      The accent line's travel, in ms.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        fold-duration
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        300
      </code>
    </td>
    
    <td>
      A section's fold and unfold, in ms.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        class
      </code>
    </td>
    
    <td>
      <code>
        HTMLAttributes['class']
      </code>
    </td>
    
    <td>
      —
    </td>
    
    <td>
      Extra classes for the nav.
    </td>
  </tr>
</tbody>
</table>

## Events

<table>
<thead>
  <tr>
    <th>
      Event
    </th>
    
    <th>
      Payload
    </th>
    
    <th>
      Description
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        select
      </code>
    </td>
    
    <td>
      <code>
        (value: string, item: BranchedMenuItem | BranchedMenuChild)
      </code>
    </td>
    
    <td>
      A child or a leaf was picked.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        toggle
      </code>
    </td>
    
    <td>
      <code>
        (index: number, open: boolean)
      </code>
    </td>
    
    <td>
      A section folded or unfolded.
    </td>
  </tr>
</tbody>
</table>

## Accessibility

- Rendered as a `<nav>` of real buttons. Section headers carry `aria-expanded`; the picked child or leaf carries `aria-current="true"`.
- Children of a folded section are `inert` and leave the tab order.
- Keyboard focus draws a ring on headers and rows.
- With `prefers-reduced-motion: reduce` the fold, the line draw and the marker slide are instant.
- Ported from [React Bits](https://reactbits.dev/micro/branched-menu) by David Haz.
