# Swipe Row

> List row that swipes open to reveal actions and deletes on a full swipe.

A list row you drag sideways to reveal a drawer of actions. Release past the halfway point and the drawer snaps open; keep dragging past the commit point and the delete block leaps across the row, so letting go deletes it and the row folds shut. Flicks carry momentum, and the row rubber-bands at both ends.

<demo-swipe-row>



</demo-swipe-row>

## Installation

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

## Usage

```vue
<script setup lang="ts">
  import { ref } from 'vue';
  import SwipeRow from '~/components/ui/swipe-row/SwipeRow.vue';
  import type { SwipeAction } from '~/components/ui/swipe-row/types';

  const actions: SwipeAction[] = [
    { id: 'delete', label: 'Delete', icon: 'lucide:trash-2' },
    { id: 'archive', label: 'Archive', icon: 'lucide:archive', dismiss: true },
  ];
  const visible = ref(true);
  const open = ref(false);
</script>

<template>
  <SwipeRow
    v-if="visible"
    v-model:open="open"
    label="Design review notes"
    :actions="actions"
    @commit="visible = false"
  >
    <Icon name="lucide:notebook-pen" class="size-5 opacity-60" />
    <span class="flex-1 text-sm font-medium">Design review notes</span>
    <span class="text-xs opacity-50">9:41</span>
  </SwipeRow>
</template>
```

The default slot is the row content you drag. Remove the row in `@commit` — it fires once the row has folded shut.

## Props

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

<tbody>
  <tr>
    <td>
      <code>
        actions
      </code>
    </td>
    
    <td>
      <code>
        SwipeAction[]
      </code>
    </td>
    
    <td>
      <code>
        [{ id: 'delete', label: 'Delete' }]
      </code>
    </td>
    
    <td>
      The drawer, outermost first. Each has <code>
        id
      </code>
      
      , <code>
        label
      </code>
      
      , optional <code>
        icon
      </code>
      
       (Iconify name), <code>
        color
      </code>
      
      , <code>
        dismiss
      </code>
      
       and <code>
        onSelect
      </code>
      
      . The first is the full-swipe action and always folds the row.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        action-color
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        '#e5484d'
      </code>
    </td>
    
    <td>
      The full-swipe block that leaps and fills the row.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        drawer-color
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        '#3f3f46'
      </code>
    </td>
    
    <td>
      The drawer base and the secondary actions.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        row-color
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        '#27272a'
      </code>
    </td>
    
    <td>
      The surface.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        text-color
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        '#f5f5f5'
      </code>
    </td>
    
    <td>
      Row text and the keyboard toggle.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        height
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        64
      </code>
    </td>
    
    <td>
      Row height in pixels. The fold closes from it.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        radius
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        16
      </code>
    </td>
    
    <td>
      Corner radius of the row and the clipped drawer.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        action-width
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        80
      </code>
    </td>
    
    <td>
      Width of each action in pixels, so the drawer width too.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        direction
      </code>
    </td>
    
    <td>
      <code>
        'left' | 'right'
      </code>
    </td>
    
    <td>
      <code>
        'left'
      </code>
    </td>
    
    <td>
      Which way you swipe. Everything mirrors.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        snap-bounce
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.2
      </code>
    </td>
    
    <td>
      Overshoot of the open or close settle after a flick. Slow releases never bounce.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        resistance
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.55
      </code>
    </td>
    
    <td>
      How much of the finger the row keeps past the drawer, and the rubber-band beyond the commit point.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        collapse-ms
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        200
      </code>
    </td>
    
    <td>
      How long a deleted row takes to fold shut.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        commit-at
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.6
      </code>
    </td>
    
    <td>
      Fraction of the row width the surface must uncover for the block to leap and a release to delete.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        full-swipe
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        true
      </code>
    </td>
    
    <td>
      Off, the drawer is the end: no leap, no delete.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        disabled
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        false
      </code>
    </td>
    
    <td>
      Dims the row and ignores input.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        open
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        undefined
      </code>
    </td>
    
    <td>
      Controlled open state (<code>
        v-model:open
      </code>
      
      ). Outside changes settle the row.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        close-on-action
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        true
      </code>
    </td>
    
    <td>
      Closes the drawer after an action is pressed.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        haptic
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        true
      </code>
    </td>
    
    <td>
      A short vibration on touch when the block leaps.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        label
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        'List item'
      </code>
    </td>
    
    <td>
      Accessible name of the row.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        class
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        ''
      </code>
    </td>
    
    <td>
      Extra classes for the root.
    </td>
  </tr>
</tbody>
</table>

## Events

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

<tbody>
  <tr>
    <td>
      <code>
        update:open
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      A gesture or key opened or closed the drawer.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        action
      </code>
    </td>
    
    <td>
      <code>
        SwipeAction
      </code>
    </td>
    
    <td>
      Any action button was pressed.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        commit
      </code>
    </td>
    
    <td>
      <code>
        SwipeAction
      </code>
    </td>
    
    <td>
      A full swipe or a folding action finished and the row has shut. Remove the row here.
    </td>
  </tr>
</tbody>
</table>

## Slots

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

<tbody>
  <tr>
    <td>
      <code>
        default
      </code>
    </td>
    
    <td>
      The row content you drag.
    </td>
  </tr>
</tbody>
</table>

## Accessibility

- The row is a `group` named by `label`. A visually hidden toggle button shows on keyboard focus and controls the drawer (`aria-expanded`, `aria-controls`).
- On the toggle: Enter, Space or the swipe-direction arrow opens the drawer; the opposite arrow or Escape closes it; Delete or Backspace runs the full-swipe action while open.
- The closed drawer is `inert`, so its buttons are skipped by Tab until it opens.
- A polite live region announces "Release to Delete", the revealed action count and the action that ran.
- With reduced motion, settles become short eased tweens with no spring overshoot.
- Ported from [React Bits](https://reactbits.dev/micro/swipe-row) by David Haz.
