# Rubber Segment

> Segmented control whose rubber thumb stretches across the gap and squashes into place.

A segmented control with a rubber thumb. Tap a slot and the thumb dilates across the old and new slot, then contracts and squashes into place. Grab the active slot to drag the thumb, rubber-band it past the ends, or flick it to glide to a far slot. Arrow keys, Home and End move the selection.

<demo-rubber-segment>



</demo-rubber-segment>

## Installation

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

## Usage

```vue
<script setup lang="ts">
  import { ref } from 'vue';
  import RubberSegment from '~/components/ui/rubber-segment/RubberSegment.vue';

  const period = ref('Week');
</script>

<template>
  <RubberSegment
    v-model="period"
    :items="[
      'Day',
      'Week',
      { value: 'month', label: 'Month', icon: 'lucide:calendar' },
    ]"
    :stretch="100"
    :squash="3"
    @change="(value) => console.log(value)"
  />
</template>
```

The thumb is one element clipped with `clip-path: inset(...)`. Its left and right edges are two independent springs, so the leading edge can arrive before the trailing edge. The labels inside the thumb are a copy of the track labels, so the active label color is revealed exactly where the thumb is.

## 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>
        (string | { value: string; label: string; icon?: string })[]
      </code>
    </td>
    
    <td>
      —
    </td>
    
    <td>
      The slots, in order. A string is both value and label. <code>
        icon
      </code>
      
       is an Iconify name.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        model-value
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        undefined
      </code>
    </td>
    
    <td>
      Controlled value (<code>
        v-model
      </code>
      
      ). A change from outside jumps the thumb.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        default-value
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      first item
    </td>
    
    <td>
      Initial value when uncontrolled.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        track-color
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        '#27272a'
      </code>
    </td>
    
    <td>
      The well behind the slots.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        thumb-color
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        '#fafafa'
      </code>
    </td>
    
    <td>
      The rubber thumb, and the focus ring.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        text-color
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        '#fafafa'
      </code>
    </td>
    
    <td>
      Idle labels, drawn at 70%.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        active-text-color
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        '#18181b'
      </code>
    </td>
    
    <td>
      The label revealed inside the thumb.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        size
      </code>
    </td>
    
    <td>
      <code>
        'sm' | 'md' | 'lg'
      </code>
    </td>
    
    <td>
      <code>
        'md'
      </code>
    </td>
    
    <td>
      Track height 28, 36 or 44 px; font and padding follow.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        radius
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        10
      </code>
    </td>
    
    <td>
      Track corner radius in px.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        inset
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        3
      </code>
    </td>
    
    <td>
      Gap between thumb and track edge. The thumb corner is radius minus inset.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        equal-slots
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        true
      </code>
    </td>
    
    <td>
      Every slot the same width. Off, slots hug their labels.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        stretch
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        100
      </code>
    </td>
    
    <td>
      How far a tap dilates the thumb across both slots. <code>
        0
      </code>
      
       is a plain slide.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        squash
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        3
      </code>
    </td>
    
    <td>
      Pixels the trailing edge lands past the slot edge before it relaxes.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        speed
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        1
      </code>
    </td>
    
    <td>
      Scales every phase together. <code>
        0.25
      </code>
      
       is slow motion.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        glide
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        75
      </code>
    </td>
    
    <td>
      How far a flick carries the thumb. <code>
        0
      </code>
      
       always lands on the nearest slot.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        draggable
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        true
      </code>
    </td>
    
    <td>
      Lets the thumb be grabbed, dragged and flicked.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        disabled
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        false
      </code>
    </td>
    
    <td>
      Dims the control and ignores input.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        aria-label
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        'Segmented control'
      </code>
    </td>
    
    <td>
      Accessible name of the radio group.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        class
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        ''
      </code>
    </td>
    
    <td>
      Extra classes for the track.
    </td>
  </tr>
</tbody>
</table>

## Events

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

<tbody>
  <tr>
    <td>
      <code>
        update:modelValue
      </code>
    </td>
    
    <td>
      <code>
        (value: string)
      </code>
    </td>
    
    <td>
      Fires on a tap, a drag release and every arrow key.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        change
      </code>
    </td>
    
    <td>
      <code>
        (value: string, index: number)
      </code>
    </td>
    
    <td>
      Same moments, with the slot index.
    </td>
  </tr>
</tbody>
</table>

## Accessibility

- The track is a `radiogroup`; each slot is a `radio` with `aria-checked`. Only the active slot is in the tab order.
- Arrow keys move the selection, Home and End jump to the ends. Keyboard changes jump the thumb without animation.
- Focus shows a ring in the thumb color.
- With `prefers-reduced-motion: reduce`, the thumb jumps instead of stretching, and drags do not rubber-band.
- Ported from [React Bits](https://reactbits.dev/micro/rubber-segment) by David Haz.
