# Pattern Donut

A breakdown card for "where did the time go" questions. The inner ring shows each category as a solid slice. The outer ring splits every category into its causes, and the texture tells the rank: lines for the top cause, dots for the second, checker for the third. The center shows the active category and its share. Hover or focus a slice or a legend item to highlight it, and click to pin it. The pill at the bottom counts the fewest categories that reach 75 % of the total. The rings sweep in clockwise on load.

<demo-pattern-donut>



</demo-pattern-donut>

## Installation

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

## Usage

```vue
<script setup lang="ts">
  import { ref } from 'vue';
  import PatternDonut from '~/components/ui/pattern-donut/PatternDonut.vue';
  import type { PatternDonutCategory } from '~/components/ui/pattern-donut/types';

  const stops: PatternDonutCategory[] = [
    {
      label: 'Tooling',
      value: 412,
      color: '#f5d03b',
      causes: [
        { label: 'Tool change', value: 220 },
        { label: 'Tool breakage', value: 118 },
        { label: 'Fixture swap', value: 74 },
      ],
    },
    { label: 'Maint', value: 318, color: '#a46cf5' },
    { label: 'Material', value: 221, color: '#3ee0d8' },
    { label: 'Setup', value: 168, color: '#f590b4' },
    { label: 'Quality', value: 77, color: '#f2742b' },
    { label: 'Other', value: 40, color: '#8a8a8a' },
  ];

  const selected = ref<string | null>(null);
</script>

<template>
  <PatternDonut
    v-model:selected="selected"
    :data="stops"
    title="Why Lines Stop"
    description="minutes · last 28 days"
    unit="min"
    :delta="-14"
    delta-label="vs Aug"
  />
</template>
```

Causes are sorted by value inside each category. A category without `causes` fills its outer segment with the first texture.

## Props

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

<tbody>
  <tr>
    <td>
      <code>
        data
      </code>
    </td>
    
    <td>
      <code>
        PatternDonutCategory[]
      </code>
    </td>
    
    <td>
      —
    </td>
    
    <td>
      Categories, drawn clockwise from 12 o'clock.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        selected
      </code>
    </td>
    
    <td>
      <code>
        string | null
      </code>
    </td>
    
    <td>
      <code>
        null
      </code>
    </td>
    
    <td>
      <code>
        v-model
      </code>
      
      . Label of the pinned category.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        title
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        ''
      </code>
    </td>
    
    <td>
      Card title.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        description
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        ''
      </code>
    </td>
    
    <td>
      Line under the title.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        unit
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        ''
      </code>
    </td>
    
    <td>
      Unit after the total, e.g. <code>
        min
      </code>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        delta
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      —
    </td>
    
    <td>
      Change against the last period, in percent. Hidden when not set.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        delta-label
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        ''
      </code>
    </td>
    
    <td>
      Text after the delta, e.g. <code>
        vs Aug
      </code>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        lower-is-better
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        true
      </code>
    </td>
    
    <td>
      A falling delta shows green. Set <code>
        false
      </code>
      
       when a rise is good news.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        pattern-labels
      </code>
    </td>
    
    <td>
      <code>
        string[]
      </code>
    </td>
    
    <td>
      <code>
        ['top cause', '2nd', '3rd']
      </code>
    </td>
    
    <td>
      Labels for the pattern key, in rank order.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        patterns
      </code>
    </td>
    
    <td>
      <code>
        PatternDonutPatternKind[]
      </code>
    </td>
    
    <td>
      <code>
        ['lines', 'dots', 'checker', 'crosshatch', 'grid', 'stripes']
      </code>
    </td>
    
    <td>
      Texture for each cause rank.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        insight-threshold
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.75
      </code>
    </td>
    
    <td>
      Share (0–1) that the insight pill counts the top categories up to.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        insight-suffix
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        'of lost minutes'
      </code>
    </td>
    
    <td>
      Words after the share in the insight pill.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        hide-insight
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        false
      </code>
    </td>
    
    <td>
      Hides the insight pill.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        locale
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        'en-US'
      </code>
    </td>
    
    <td>
      Locale for number formatting.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        class
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        ''
      </code>
    </td>
    
    <td>
      Extra classes for the root element.
    </td>
  </tr>
</tbody>
</table>

## Category options

<table>
<thead>
  <tr>
    <th>
      Option
    </th>
    
    <th>
      Type
    </th>
    
    <th>
      Description
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        label
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      Category name. Also the value of <code>
        v-model:selected
      </code>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        value
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      Category total.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        color
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      Any CSS color for the slice, textures and legend.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        causes
      </code>
    </td>
    
    <td>
      <code>
        { label, value }[]
      </code>
    </td>
    
    <td>
      Breakdown shown in the outer ring.
    </td>
  </tr>
</tbody>
</table>

## Slots

<table>
<thead>
  <tr>
    <th>
      Slot
    </th>
    
    <th>
      Props
    </th>
    
    <th>
      Description
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        insight
      </code>
    </td>
    
    <td>
      <code>
        { count, share }
      </code>
    </td>
    
    <td>
      Replaces the insight text. <code>
        share
      </code>
      
       is a fraction from 0 to 1.
    </td>
  </tr>
</tbody>
</table>

## Accessibility

- Each inner slice is a button with a label such as "Tooling: 412 min, 33%". Only one slice is in the tab order; arrow keys, Home and End move between slices.
- Legend items are buttons with the same arrow-key movement. Both set `aria-pressed` when pinned.
- Enter or Space pins a slice; Escape clears the pin.
- The center label is announced through `aria-live`. Outer segments have a `<title>` with the cause and its value.
- The sweep-in animation and the dimming transition are off when the user prefers reduced motion.
