# Scrub Field

> Number chip you drag to scrub, with rubber-band limits and click to type.

A number field you scrub like a design-tool inspector. Drag sideways to change the value; a pill above the cursor shows the change since you grabbed it. Past the limits the number rubber-bands and springs back on release. Hold Shift for coarse steps, Alt for fine steps, press Escape to cancel. Click without dragging to type a value.

<demo-scrub-field>



</demo-scrub-field>

## Installation

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

## Usage

```vue
<script setup lang="ts">
  import { ref } from 'vue';
  import ScrubField from '~/components/ui/scrub-field/ScrubField.vue';

  const radius = ref(24);
</script>

<template>
  <ScrubField
    v-model="radius"
    label="Radius"
    suffix="px"
    :min="0"
    :max="100"
    :step="1"
    :rubber-reach="8"
    @commit="(value) => console.log('committed', value)"
  />
</template>
```

The shown number is a motion value. Inside the range it follows the pointer step by step; past a limit it is bent with a logarithmic curve, so the overshoot slows down the farther you pull. The emitted value is always clamped to `min`–`max`.

## Props

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

<tbody>
  <tr>
    <td>
      <code>
        label
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        'Radius'
      </code>
    </td>
    
    <td>
      Label on the left. It is also the drag handle.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        suffix
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        'px'
      </code>
    </td>
    
    <td>
      Unit after the number.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        model-value
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        undefined
      </code>
    </td>
    
    <td>
      Controlled value (<code>
        v-model
      </code>
      
      ).
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        default-value
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        24
      </code>
    </td>
    
    <td>
      Initial value when uncontrolled. Also the reference for the dirty ring.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        min
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0
      </code>
    </td>
    
    <td>
      Lowest value.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        max
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        100
      </code>
    </td>
    
    <td>
      Highest value.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        step
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        1
      </code>
    </td>
    
    <td>
      Value change per drag step and per arrow key.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        size
      </code>
    </td>
    
    <td>
      <code>
        'sm' | 'md' | 'lg'
      </code>
    </td>
    
    <td>
      <code>
        'md'
      </code>
    </td>
    
    <td>
      Chip height 28, 34 or 44 px; width and font follow.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        sensitivity
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        2
      </code>
    </td>
    
    <td>
      Pixels of drag per step.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        rubber-reach
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        8
      </code>
    </td>
    
    <td>
      How far past a limit the number can stretch, as a percent of the range. <code>
        0
      </code>
      
       is a hard stop.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        return-duration
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        300
      </code>
    </td>
    
    <td>
      Spring back duration after an overshoot, in ms.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        coarse-multiplier
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        10
      </code>
    </td>
    
    <td>
      Step multiplier while Shift is held, and for Page Up / Page Down.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        fine-multiplier
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.1
      </code>
    </td>
    
    <td>
      Step multiplier while Alt is held.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        show-delta
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        true
      </code>
    </td>
    
    <td>
      Show the change pill above the cursor while dragging.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        show-dirty
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        false
      </code>
    </td>
    
    <td>
      Ring the chip when the value differs from <code>
        default-value
      </code>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        show-fill
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        true
      </code>
    </td>
    
    <td>
      Tint the chip from the left in proportion to the value.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        accent
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        '#f5f5f5'
      </code>
    </td>
    
    <td>
      Fill, dirty ring and delta pill color.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        chip-color
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        '#27272a'
      </code>
    </td>
    
    <td>
      Chip background.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        disabled
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        false
      </code>
    </td>
    
    <td>
      Dims the chip and ignores input.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        class
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        ''
      </code>
    </td>
    
    <td>
      Extra classes for the chip.
    </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: number)
      </code>
    </td>
    
    <td>
      Every value change, live while dragging.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        change
      </code>
    </td>
    
    <td>
      <code>
        (value: number)
      </code>
    </td>
    
    <td>
      Same as above.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        commit
      </code>
    </td>
    
    <td>
      <code>
        (value: number)
      </code>
    </td>
    
    <td>
      Once per gesture: drag release, arrow key, or typed value.
    </td>
  </tr>
</tbody>
</table>

## Accessibility

- The value is an `input` with `role="spinbutton"`, `aria-valuenow`, `aria-valuemin`, `aria-valuemax` and `aria-valuetext` including the unit.
- The label is a real `<label>` for the input. Tab focuses the input and selects its text.
- Arrow Up / Down step (Shift and Alt change the step), Page Up / Page Down step by the coarse multiplier, Home and End jump to the limits. Enter commits, Escape reverts.
- The chip shows a focus ring while the input has focus.
- With `prefers-reduced-motion: reduce`, the chip does not lean and overshoots snap back without a spring.
- Ported from [React Bits](https://reactbits.dev/micro/scrub-field) by David Haz.
