Sortable icon indicating copy to clipboard operation
Sortable copied to clipboard

Keep initial position of item

Open petronellius opened this issue 6 years ago • 30 comments

Similar to Chrome or Firefox Bookmarks Toolbar. It keeps initial position of item during dragging, showing a line at the position when item is potentially dropped.

petronellius avatar Jul 16 '18 14:07 petronellius

Hey, also interested in a solution for this. I have the part when you transform the chosenClass: "sortable-chosen" and transform the element to a bar:

:host(.sortable-chosen) {
  visibility: hidden;
  height: 1px;
  position: relative;
  &::before {
    content: '';
    visibility: visible;
    border-bottom: 1px solid $primary;
    width: 100%;
    height: 0;
    position: absolute;
    top: 0;
    left: 0;
  }
}

But I can't achieve to let the item at its initial position and do the move only on dragEnd...

Thanks in advance for anyone helping on this!

Nightbr avatar Oct 31 '18 10:10 Nightbr

I achieve something which is quite hacky but seems to work so far. I create a clone of my element and reinsert it in the DOM at the same place of the dragged element. And remove it onDragEnd. I have some issue of DOM rendering but it's because I use webcomponent. For normal DOM element this should work perfectly:

import Sortable from 'sortablejs';
import { EventEmitter } from '@stencil/core';

export function initMenuSort(el: HTMLElement, emitter: EventEmitter) {
  let persistentClone: HTMLElement = null;
  const sort = Sortable.create(el, {
    draggable: '.my-draggable',
    handle: '.drag-handle',
    filter: 'my-menu-action',
    group: { name: 'menu' },
    ghostClass: 'sortable-ghost',
    chosenClass: 'my-sortable-chosen',
    sort: true,
    onStart: (e: any) => {
      const el = e.item as HTMLMyComponentElement;
      let index = e.oldIndex;
      if (index < 0) {
        index = 0;
      }
      // @TODO el.cloneNode(true) (deep=true)
      // this cause the component to rerender and display the content <slot> twice
      // when stenciljs will be bulletproof on cloneNode(deep=true) we will have the clone properly displaying
      persistentClone = e.from.insertBefore(
        el.cloneNode(),
        e.from.children[index]
      );
      persistentClone.classList.add('my-sortable-disable');
      persistentClone.classList.remove('my-draggable');
      console.log(persistentClone);
      console.log(e);
      el.dragStart();
    },
    onEnd: (e: any) => {
      const data = {
        oldIndex: e.oldIndex,
        newIndex: e.newIndex,
        item: e.item,
        newParent: e.to,
        oldParent: e.from
      };
      const el = e.item as HTMLMyComponentElement;
      el.dragEnd();
      persistentClone.remove();
      persistentClone = null;
      emitter.emit(data);
    },
    onMove: e => {
      // If it's not draggable, we cancel the move
      if (e.related.classList.contains('my-draggable')) {
        return true;
      }
      return false;
    }
  });
  return sort;
}

Adapte the code to your case ;)

Nightbr avatar Oct 31 '18 11:10 Nightbr

Perhaps a placeholder element option, to stay in the original position of the element?

owen-m1 avatar Dec 09 '18 01:12 owen-m1

We are having issues when sorting an item and then re-rendering a stencil component, this causes sortable to repeat the previous drag action - I am not sure why this is occurring. @Nightbr is this the issue you were trying to get around ?

miguelyoobic95 avatar Apr 18 '19 15:04 miguelyoobic95

Hey @miguelyoobic95 it's another problem you describe. But we manage to include sortableJS with stenciljs component, we use our fork -> "sortablejs": "https://github.com/saloonio/Sortable.git#dev-firefoxShadowRoot"

We use a stenciljs component container which instantiate sortablejs:

componentDidLoad() {
    this.sortableElement = Sortable.create(this.el.querySelector('.components') as HTMLElement, {
      handle: '.drag-icon',
      draggable: '.plop-draggable',
      filter: 'plop-new-component-container soon-button',
      animation: 150,
      onStart: e => {
        const el = e.item as HTMLPlopComponentElement;
        el.dragStart();
      },
      onEnd: e => {
        const el = e.item as HTMLPlopComponentElement;
        el.dragEnd();
        this.drop(e);
      },
      onMove: e => {
        // If it's not draggable, we cancel the move
        if (e.related.classList.contains('plop-draggable')) {
          return true;
        }
        return false;
      }
    });
  }

Nightbr avatar Apr 19 '19 07:04 Nightbr

I'm also interested in this feature. Another great example for this is Notion:

Jun-03-2019 13-43-46

philippkuehn avatar Jun 03 '19 13:06 philippkuehn

This would be a great idea for a plugin. I could try to make it myself, but if anyone else wants to give it a try - the docs for creating a plugin are here: https://github.com/SortableJS/Sortable/blob/master/plugins/README.md

owen-m1 avatar Jun 03 '19 13:06 owen-m1

Kapture 2019-06-03 at 15 49 41

using our code snippet https://github.com/SortableJS/Sortable/issues/1345#issuecomment-434630156 & https://github.com/SortableJS/Sortable/issues/1345#issuecomment-434655435

Nightbr avatar Jun 03 '19 13:06 Nightbr

@Nightbr Would you be interested in making your implementation into a plugin?

owen-m1 avatar Jun 03 '19 13:06 owen-m1

I don't think it could be easy because I'm wrapping SortableJS into a Stenciljs webcomponent. But I could try if I have spare time ;)

Nightbr avatar Jun 03 '19 15:06 Nightbr

Is there any plugin or API available to achieve this using just SortableJS?

I am also stuck with the same problem.

MihirGH avatar Dec 23 '20 13:12 MihirGH

Is there any plugin or API available to achieve this using just SortableJS?

raind33 avatar Feb 20 '21 05:02 raind33

+1 We need this :D

l2aelba avatar Apr 22 '21 13:04 l2aelba

This would be a great idea for a plugin. I could try to make it myself, but if anyone else wants to give it a try - the docs for creating a plugin are here: https://github.com/SortableJS/Sortable/blob/master/plugins/README.md

Are there still any plans for this?

pryley avatar Aug 20 '21 15:08 pryley

Just leaving a +1. This is the same functionality we would like to achieve. Thanks!

joaobarcia avatar Sep 02 '21 11:09 joaobarcia

+1

lbmir avatar Nov 16 '21 18:11 lbmir

+1

esoteloferry avatar Nov 26 '21 21:11 esoteloferry

+1

xiaoyudesu avatar Jan 13 '22 07:01 xiaoyudesu

+1

TradeFuse avatar Mar 12 '22 17:03 TradeFuse

+1

danielablum avatar Apr 12 '22 08:04 danielablum

+1

songispm avatar Oct 28 '22 17:10 songispm

Any new?

songispm avatar Nov 11 '22 13:11 songispm

This would be a great idea for a plugin. I could try to make it myself, but if anyone else wants to give it a try - the docs for creating a plugin are here: https://github.com/SortableJS/Sortable/blob/master/plugins/README.md

Any news?

Klapik avatar Jan 23 '23 11:01 Klapik

+1

kotoyama avatar Jul 31 '23 00:07 kotoyama

+1

9brada6 avatar Oct 02 '23 10:10 9brada6

+1

miroshnichenkoYaroslav avatar Nov 23 '23 20:11 miroshnichenkoYaroslav

+1

semivori avatar Jan 16 '24 15:01 semivori

@songispm @Klapik @kotoyama @9brada6 @miroshnichenkoYaroslav @semivori @joaobarcia

This can implemented without a plugin using @Nightbr's method. Here's a more minimal version:

Keep an original copy in place as you drag:

onStart(e) {
	// Add a clone in the original location
	const persistentClone = e.from.insertBefore(
		e.item.cloneNode(true),
		e.from.children[e.oldIndex as number]
	) as HTMLElement

	// give it a class so we can remove it later
	persistentClone.classList.add('sortable-clone') 

	// remove irrelevant classes
	persistentClone.classList.remove('sortable-chosen', 'sortable-ghost') 
},
onEnd(e){
	// remove persisted clones
	e.from.querySelectorAll('.sortable-clone').forEach(child => child.remove())
}

.sortable-ghost ends up being the blue line you want - but you'll need to do some styling to hide is contents, make it blue, etc.

My elements are table rows so I had to do some css trickery to get the line to work consistently between rows:

tbody {
	// ensure the absolutely positioned table row stays contained within the tbody
	position: relative;
}

// Position the table row line and hide its contents
.sortable-ghost {
	// eliminate goofy layout shifting
	position: absolute; 
	
	// make it full width
	display: block; 
	width: 100%;
	
	// Overly robust hack to hide text contents within the line element
	text-indent: -9999px;
	font-size: 0.001px;
	line-height: 0;
	color: transparent;
}

// Create the actual line
.sortable-ghost::before{
	content: '';
	position: absolute;
	top: 0;
	right: 0;
	left: 0;
	height: 3px;
	background: blue;
}

JeffJassky avatar Mar 15 '24 16:03 JeffJassky

Use Swap plugin:

https://sortablejs.github.io/Sortable/#swap https://github.com/SortableJS/Sortable/tree/master/plugins/Swap

SuroZaqaryan avatar Apr 04 '24 19:04 SuroZaqaryan

Use Swap plugin:

https://sortablejs.github.io/Sortable/#swap https://github.com/SortableJS/Sortable/tree/master/plugins/Swap

No, the Swap plugin swaps the dragged element with the destination element. Totally different feature.

pryley avatar Apr 12 '24 21:04 pryley