JSFiddle - React, Tailwind, and code Playground

by Upward

HTML

<div class="drop-list">
    <div class="line">1</div>
    <div class="line">2</div>
    <div class="line">3</div>
    <div class="line">4</div>
    <div class="line">5</div>
</div>
<br><br><br>
<div class="drop-list">
    <div class="line">1</div>
    <div class="line">2</div>
    <div class="line">3</div>
</div>
<br><br><br><br><br><br><br><br><br>

CSS

.line {
    background: red;
    margin: 2px;
    position: relative;
}
.line.active {
    -webkit-user-select: none;
    -moz-user-select: none;
    -ms-user-select: none;
    user-select: none;
    background: yellow;
    z-index: 10;
    opacity: 0.5;
}

.drop-list {
    width: 200px;
}
body.active {
    -webkit-user-select: none;    
}

JavaScript

var config = {
    wrapClass: '.drop-list',
    itemClass: '.line'
};

$(config.wrapClass).each(function() {
    var $parent = $(this);
    var $line;
    
    $(config.itemClass, $parent).on('mousedown.drop', function(e) {
        $line = $(this);
        var startY = e.clientY;
        var diffY;
        
        $line.addClass('active');
        $line.on('selectstart', function() {
            return false;
        });
        
        $('body').on('mousemove.drop', function(e) {
            diffY = startY - e.clientY;
            $line.css({ top: -diffY });
        });
    });

    $('body').on('mouseup.drop', function(e) {
        if (!$line) return;

        var length = $(config.itemClass, $parent).length;
        var diff = parseInt($line.css('top'));
        var height = $line.outerHeight();
        var index = $(config.itemClass, $parent).index($line);
        var newIndex = Math.round((diff / height));
        
        index = diff > 0 ? index - 1 : index;
        newIndex = index + newIndex;
        newIndex = newIndex > length - 1 ? length - 1 : newIndex;
        
        if (newIndex >= 0) {
            $(config.itemClass, $parent).eq(newIndex).after($line)
        } else {
            $(config.itemClass, $parent).eq(0).before($line)
        }
        $('body').off('mousemove.drop').removeClass('active');
        $(config.itemClass, $parent).removeClass('active').css({top: 0});
    });
});