UNPKG

svelte-swipeout

Version:

iOS-style swipeable list component for Svelte 5 with delete animations and mobile-optimized touch handling

303 lines (238 loc) 7.78 kB
# Enhanced Swipeout Component for Svelte 5 A production-ready, accessible, and performant swipe-to-reveal actions component for Svelte 5 applications. Inspired by iOS Mail and Gmail mobile interfaces. ## ✨ Features ### Core Functionality - 🎯 **Smooth Gesture Detection** - Advanced touch and mouse gesture recognition with velocity tracking - 📱 **Mobile-First Design** - Optimized for touch devices with proper touch target sizes -**High Performance** - 60fps animations using CSS transforms and requestAnimationFrame - 🎨 **Fully Themeable** - CSS variables for complete customization -**Accessible** - Full keyboard navigation and screen reader support - 🌗 **Dark Mode Ready** - Automatic dark mode adaptation ### Advanced Features - 🔄 **State Management** - Built-in state machine for predictable behavior - 🎭 **Multiple Actions** - Support for multiple actions on both sides -**Overswipe Actions** - Trigger immediate actions with full swipe - 🔔 **Haptic Feedback** - Native haptic feedback on supported devices - 🎯 **Snap Points** - Configurable snap positions for partial reveals - 🔒 **Confirmation Mode** - Require confirmation for destructive actions - 📊 **Loading States** - Built-in loading indicators for async actions - 🎪 **Spring Animations** - Physics-based animations for natural feel ## 📦 Installation ```bash npm install @your-org/svelte-swipeout # or pnpm add @your-org/svelte-swipeout ``` ## 🚀 Quick Start ```svelte <script lang="ts"> import { Swipeout, SwipeoutItem, SwipeoutContent, SwipeoutActionsRight, SwipeoutAction } from '@your-org/svelte-swipeout'; import { Trash2 } from 'lucide-svelte'; function handleDelete() { console.log('Item deleted'); } </script> <Swipeout> <SwipeoutItem> <SwipeoutContent> <div class="p-4"> <h3>Swipe me!</h3> <p>Reveal actions by swiping</p> </div> </SwipeoutContent> <SwipeoutActionsRight> <SwipeoutAction icon={Trash2} label="Delete" type="destructive" onclick={handleDelete} /> </SwipeoutActionsRight> </SwipeoutItem> </Swipeout> ``` ## 📖 API Reference ### `<Swipeout>` Component The container component that provides context and configuration. #### Props | Prop | Type | Default | Description | |------|------|---------|-------------| | `threshold` | `number` | `40` | Minimum distance (px) to trigger swipe | | `velocityThreshold` | `number` | `0.3` | Minimum velocity to trigger swipe | | `animationDuration` | `number` | `300` | Animation duration in milliseconds | | `animationType` | `'spring' \| 'ease' \| 'linear'` | `'ease'` | Animation easing type | | `rubberBand` | `boolean` | `true` | Enable rubber band effect on overswipe | | `closeOnOpen` | `boolean` | `true` | Close other items when opening | | `hapticFeedback` | `boolean` | `false` | Enable haptic feedback | | `snapPoints` | `number[]` | `[0.5, 1]` | Snap points as percentages | | `keyboardNavigation` | `boolean` | `true` | Enable keyboard navigation | | `disabled` | `boolean` | `false` | Disable all interactions | #### Events - `onSwipeStart` - Fired when swipe begins - `onSwipeMove` - Fired during swipe movement - `onSwipeEnd` - Fired when swipe ends - `onOpen` - Fired when item opens - `onClose` - Fired when item closes #### Methods ```javascript // Get reference to component let swipeoutRef; // Available methods swipeoutRef.closeAll(); swipeoutRef.closeItem(id); swipeoutRef.openItem(id, 'left' | 'right'); ``` ### `<SwipeoutItem>` Component Individual swipeable item container. #### Props | Prop | Type | Default | Description | |------|------|---------|-------------| | `disabled` | `boolean` | `false` | Disable this item | | `autoCloseOnAction` | `boolean` | `true` | Close after action triggered | | `confirmDelete` | `boolean` | `false` | Require confirmation for delete | | `snapPoints` | `number[]` | `undefined` | Custom snap points for this item | | `maxSwipeDistance` | `number` | `undefined` | Maximum swipe distance in pixels | | `ariaLabel` | `string` | `undefined` | Accessibility label | ### `<SwipeoutAction>` Component Action button component with rich features. #### Props | Prop | Type | Default | Description | |------|------|---------|-------------| | `type` | `'default' \| 'destructive' \| 'primary' \| 'secondary'` | `'default'` | Action type for styling | | `icon` | `Component` | `undefined` | Icon component | | `label` | `string` | `undefined` | Text label | | `color` | `string` | `undefined` | Custom background color | | `overSwipe` | `boolean` | `false` | Enable overswipe trigger | | `requireConfirm` | `boolean` | `false` | Require confirmation | | `confirmMessage` | `string` | `'Are you sure?'` | Confirmation message | | `loading` | `boolean` | `false` | Show loading state | | `disabled` | `boolean` | `false` | Disable action | | `width` | `number` | `undefined` | Custom width in pixels | ## 🎨 Theming ### CSS Variables ```css :root { /* Colors */ --swipeout-delete-button-bg-color: #ff3b30; --swipeout-primary-button-bg-color: #007aff; --swipeout-secondary-button-bg-color: #8e8e93; --swipeout-button-text-color: #fff; /* Sizing */ --swipeout-button-padding-horizontal: 30px; --swipeout-button-min-width: 80px; /* Animation */ --swipeout-animation-duration: 300ms; --swipeout-animation-timing: cubic-bezier(0.4, 0, 0.2, 1); /* Interaction */ --swipeout-threshold: 40px; --swipeout-rubber-band-tension: 0.8; } ``` ### Dark Mode The component automatically adapts to dark mode: ```css @media (prefers-color-scheme: dark) { .swipeout-content { background: var(--background, #1a1a1a); } } ``` ## ⌨️ Keyboard Navigation | Key | Action | |-----|--------| | `Delete` / `Backspace` | Trigger delete action if available | | `ArrowLeft` | Open left actions | | `ArrowRight` | Open right actions | | `Escape` | Close all open items | | `Tab` | Navigate between items | | `Enter` / `Space` | Activate focused action | ## ♿ Accessibility - Full ARIA attributes support - Keyboard navigation - Screen reader announcements - Focus management - Minimum touch target sizes (44x44px on mobile) - Reduced motion support - High contrast mode support ## 🎯 Advanced Examples ### With Icon Actions ```svelte <SwipeoutAction icon={Archive} label="Archive" type="primary" onclick={handleArchive} /> ``` ### With Confirmation ```svelte <SwipeoutAction icon={Trash2} label="Delete" type="destructive" requireConfirm={true} confirmMessage="Delete this item?" onclick={handleDelete} /> ``` ### Overswipe Action ```svelte <SwipeoutAction icon={Check} label="Complete" overSwipe={true} onclick={handleComplete} /> ``` ### Custom Styled ```svelte <Swipeout style="--swipeout-primary-button-bg-color: #8b5cf6;" > <!-- content --> </Swipeout> ``` ## 🔧 TypeScript Support Full TypeScript support with exported types: ```typescript import type { SwipeoutProps, SwipeoutItemProps, SwipeoutActionProps, SwipeDirection, SwipeState, SwipeEventDetail } from '@your-org/svelte-swipeout'; ``` ## 📱 Browser Support - iOS Safari 12+ - Chrome 80+ - Firefox 75+ - Edge 80+ - Samsung Internet 12+ ## 🎭 Performance - Hardware-accelerated animations - Passive event listeners - RequestAnimationFrame for smooth updates - Efficient state management - Minimal re-renders - CSS containment for better paint performance ## 🤝 Contributing Contributions are welcome! Please read our contributing guidelines. ## 📄 License MIT © Your Organization ## 🙏 Credits Inspired by: - iOS Mail app - Gmail mobile app - Material Design swipe patterns Built with: - Svelte 5 - TypeScript - Tailwind CSS