Slightly modified version of https://svelte.dev/repl/3bf15c868aa94743b5f1487369378cf3?version=3.21.0 :
- Used typescript & scss
- Updated some logic & styles
- Font awesome icon
Slightly modified version of https://svelte.dev/repl/3bf15c868aa94743b5f1487369378cf3?version=3.21.0 :
| <script lang="ts"> | |
| import { faGripLines } from '@fortawesome/free-solid-svg-icons'; | |
| import Fa from 'svelte-fa'; | |
| type List = $$Generic<Array<any>>; | |
| export let list: List; | |
| let hovering: number | null = null; | |
| const drop = (event: DragEvent, target: number) => { | |
| event.dataTransfer.dropEffect = 'move'; | |
| const start = parseInt(event.dataTransfer.getData('text/plain')); | |
| const newList = list; | |
| if (start < target) { | |
| newList.splice(target + 1, 0, newList[start]); | |
| newList.splice(start, 1); | |
| } else { | |
| newList.splice(target, 0, newList[start]); | |
| newList.splice(start + 1, 1); | |
| } | |
| list = newList; | |
| hovering = null; | |
| }; | |
| const dragstart = (event: DragEvent, start: number) => { | |
| event.dataTransfer.effectAllowed = 'move'; | |
| event.dataTransfer.dropEffect = 'move'; | |
| event.dataTransfer.setData('text/plain', start.toString()); | |
| }; | |
| </script> | |
| <div class="list"> | |
| {#each list as item, index (index)} | |
| <div | |
| class="item" | |
| draggable={true} | |
| on:dragstart={(event) => dragstart(event, index)} | |
| on:drop|preventDefault={(event) => drop(event, index)} | |
| on:dragover|preventDefault={() => {}} | |
| on:dragenter={() => (hovering = index)} | |
| class:active={hovering === index}> | |
| <Fa icon={faGripLines} color="rgba(var(--text-rgb), 0.25)" /> | |
| <slot {item} /> | |
| </div> | |
| {/each} | |
| </div> | |
| <style lang="scss"> | |
| .list { | |
| border: 2px solid var(--background-secondary); | |
| border-radius: 12px; | |
| overflow: hidden; | |
| margin: 4px 0px; | |
| .item { | |
| display: flex; | |
| align-items: center; | |
| gap: 8px; | |
| cursor: grab; | |
| padding: 10px 14px; | |
| font-size: 1.1rem; | |
| font-weight: 500; | |
| &:active, | |
| &:focus { | |
| cursor: grabbing; | |
| } | |
| &:not(:last-child) { | |
| border-bottom: 2px solid var(--background-secondary); | |
| } | |
| &.active { | |
| background-color: var(--primary); | |
| } | |
| } | |
| } | |
| </style> |