Sortable
more-shadcn-noair/sortableFreeComponent
A sortable list for vertical, horizontal, and grid layouts.
Live preview
live · rendered by the registry
Rendered by more-shadcn-noair on their own page — this is the component running, not a screenshot of it.Install it
npx shadcn@latest add https://more-shadcn.noair.fun/r/sortable.jsonSource
1<script lang="ts">2 import { cn } from '$UTILS$';3 import { setSortableContext } from './ctx';4 import { tick, type Snippet } from 'svelte';56 let {7 items = $bindable([]),8 class: className,9 children,10 onSort,11 animation = 20012 }: {13 items: any[];14 class?: string;15 children: Snippet;16 onSort?: (items: any[]) => void;17 animation?: number;18 } = $props();1920 let draggedId = $state<string | null>(null);21 let itemNodes = new Map<string, HTMLElement>();2223 async function move(fromIdx: number, toIdx: number) {24 const rects = new Map<string, DOMRect>();25 itemNodes.forEach((node, id) => rects.set(id, node.getBoundingClientRect()));2627 const newItems = [...items];28 const [item] = newItems.splice(fromIdx, 1);29 newItems.splice(toIdx, 0, item);30 items = newItems;31 onSort?.(newItems);3233 await tick();3435 itemNodes.forEach((node, id) => {36 const prev = rects.get(id);37 if (!prev) return;38 const curr = node.getBoundingClientRect();39 const dx = prev.left - curr.left;40 const dy = prev.top - curr.top;41 if (dx || dy) {42 node.style.transition = 'none';43 node.style.transform = `translate(${dx}px, ${dy}px)`;44 node.offsetHeight;45 node.style.transition = `transform ${animation}ms cubic-bezier(0.2, 0, 0, 1)`;46 node.style.transform = '';47 }48 });49 }5051 setSortableContext({52 getDraggedId: () => draggedId,53 registerItem: (id, node) => itemNodes.set(id, node),54 unregisterItem: (id) => itemNodes.delete(id),55 onDragStart: (e, id) => {56 draggedId = id;57 if (e.dataTransfer) {58 e.dataTransfer.effectAllowed = 'move';59 const ghost = document.createElement('div');60 e.dataTransfer.setDragImage(ghost, 0, 0);61 }62 },63 onDragOver: (e, targetId) => {64 e.preventDefault();65 if (!draggedId || draggedId === targetId) return;6667 const fromIdx = items.findIndex((i) => i.id === draggedId);68 const toIdx = items.findIndex((i) => i.id === targetId);6970 if (fromIdx !== -1 && toIdx !== -1) {71 move(fromIdx, toIdx);72 }73 },74 onDragEnd: () => {75 draggedId = null;76 }77 });78</script>7980<div class={cn(className)}>81 {@render children()}82</div>
More from more-shadcn-noair
All 43 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| Audioaudio | more-shadcn-noair | Components | Free | no deps | |
| Audio Waveaudio-wave | more-shadcn-noair | Components | Free | no deps | |
| Autocompleteautocomplete | more-shadcn-noair | Components | Free | no deps | |
| Bannerbanner | more-shadcn-noair | Components | Free | no deps | |
| Big Calendarbig-calendar | more-shadcn-noair | Components | Free | no deps | |
| Bottom Navigationbottom-nav | more-shadcn-noair | Components | Free | no deps | |
| Canvascanvas | more-shadcn-noair | Components | Free | no deps | |
| Chipchip | more-shadcn-noair | Components | Free | no deps |
