bcakmakoglu / revue-draggable

A Vue component that makes anything draggable 🤏 Easy to use and control. Supports Vue3 and Vue2 🦾

Home Page:https://draggable-docs.vueflow.dev

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Method to "reset" this component?

jfrueh opened this issue · comments

I'm needing a way to reset this component back to it's original state (i.e. never dragged, no transforms set, etc.). I tried doing this by manipulating the transform property and class names but those hacks didn't work for me.

Is there any way to just reset this component back to it's original, undragged state. I know I could force the component to re-render with a :key that dynamically changes but I have a lot of HTML inside of my draggable component so it's not very performant.

I'm hoping that I'm just overlooking a simple way to do this. Sorry if I missed it in the docs.

Great library - I've tried a few drag libraries for Vue and this is a clear winner for my needs.

Thanks.

commented

Hi @jfrueh!
First off: Thanks for opening an issue and the kind words for the library, it means a lot 💝

Regarding your issue
It depends on how you use the library, if you're using the composables you can basically overwrite the state at any point (a state ref object is returned by the useDraggable/useDraggableCore functions).

      const myInitialState = () => { allowAnyClick: false, ... }; // here you could create a function to create a base state to revert to
      const { state } = useDraggable(target, props);

      onUpdated(() => {
        state.value = { ...myInitialState() }; // here you could overwrite the state and use a state initializer to reset your state
      });

Using the component could work in a similar fashion but depending on how you use the component this might get more complicated, i.e. if you use a for-loop to create multiple elements that are draggable and might have a different base state, you'd have to store these states somewhere and pass them back as props to the component, which will then update the internal state.

I can add a slot scope that exposes the state from the component though so you could access it in your template if necessary.
Something like this

<Draggable v-bind="draggableOptions" @start="start" @move="move" @stop="stop">
    <template v-slot:default="props">
        <div>
          {{ props }} <!-- here you'd get the state ref which you can modify however you want -->
          <slot></slot>
        </div>
    </template>
  </Draggable>

If that doesn't cover your case I could add some functionality to reset an element.
Let me know your thoughts on this 😋

Thanks for the quick reply. I'm not using composables (and using Vue2 instructions), so I have an element like this:

<div id="floatingToolbar" v-draggable @start="start" @move="move" @stop="stop">...</div>

Can you tell me how I access the state ref in this case? Just to clarify my specific use case... have a single draggable element on my page. Once it's done being moved (and after receiving input from the user) I just need to be able to reset the draggable to exactly how it was before it was ever moved (i.e. it's initial state).

commented

Ah, that should be easy to solve without accessing internal states.
What you want to do is basically just use a controlled position, if I'm understanding you correctly, i.e. this:

     ...
     <div v-draggable="state" @start="onStart" @stop="onStop" @move="onMove">I am draggable</div>
</template>
<script>
export default {
  data() {
    return { 
       state: { position: { x: 0, y: 0 } }
     }
  },
  methods: {
     reset() {
        this.state = { position: { x: 0, y: 0 } };
     },
    ...
  }
}
</script>

So when you call reset, you'll basically just tell the state to put the element back to it's original position (x = 0, y = 0).

Let me know if this works for you.

We are getting closer. When I set the state property of the data object to default to this (as suggested):

state: { position: { x: 0, y: 0 } }

The item that is dragged automatically snaps back to it's original position as soon as the dragging is complete instead of waiting for me to call the reset function to set it back.

If I initialize state to be null, then it allows me to drag the item around without issue. When I call the suggested reset method that works as well (the item goes back to it's original position), however:

  1. When you click to drag the object, it jumps down to it's last dragged to position
  2. Now that the state variable is set, it behaves like the first issue and it just snaps back to it's original position.

Thanks for the continued help.

commented

Ah well, that seems to be an issue I'll have to look at.

But there is a solution that should work, I tried it out just now and it didn't seem to cause any problems.

The directive stores the draggable state on the element so you can access it like this

   <div ref="foo" v-draggable></div>
...
reset() {
      this.$refs.foo['revue-draggable'].value = { ...this.$refs.title['revue-draggable'].value, x: 0, y: 0 };
    },

You should always replace the whole draggable state value like in the example above, otherwise the transformation will not be triggered immediately and you'll notice some jumping on the next drag.

So don't do this if you want to avoid weird behavior.

reset() {
      this.$refs.foo['revue-draggable'].value.x = 0;
      this.$refs.foo['revue-draggable'].value.y = 0;
    },

This solution will work.

You can close the issue if you plan to keep it always working this way or keep it open if you plan to build simple "reset" functionality.

Thank you!

commented

I'll close this issue; Reset functionality should be handled as controlled position imo.
Controlled positions already work correctly and cover this case adequately without too much overhead I'd say.

<div v-draggable="{ x: 0, y: 0 }" @move="onControlledDrag">Draggable</div>
<button @click="reset">Reset position</button>
...
<script>
export default {
   data() {
    return {
      controlledPosition: {
        x: 0,
        y: 0
      }
    }
  },
  methods: {
     onControlledDrag(e) {
      const { x, y } = e.detail.data;
      this.controlledPosition.x = x;
      this.controlledPosition.y = y;
    },
    reset() {
      this.controlledPosition = { x: 0, y: 0 };
    }
  }
}

Docs will be extended to elaborate more on how to accomplish something like this though.
I'll also add more explanations to the options that can be passed to Draggable/DraggableCore.