vue-draggable-resizable icon indicating copy to clipboard operation
vue-draggable-resizable copied to clipboard

Forcing width when resizing

Open graker opened this issue 4 years ago • 7 comments

Hi!

I looked over similar questions but none of the answers work for me, so. My problem is simple: I want to enforce specific width in @resizing event. The point is for the object being resized to be constrained by its parent, but setting :parent prop doesn't work for me. Maybe because the parent is a table cell, so my resizable blocks appear somewhere else but not in cells when I set parent restriction. But anyway, I also support dragging between cells, so restricting it to parent won't work for me.

So I wanted to implement the constraint manually, but changing :width in @resizing event doesn't do anything. Could you give me some advice on how to change object's width so that when user is resizing it, the width won't go over the specific value (will be forced to stay at some max value that is dynamically calculated).

Thanks!

P.S. Setting maxwidth won't do as well because my object may be placed for example in a middle of a cell. So then user would resize to the right and maxwidth will go over the cell's border.

graker avatar Feb 25 '20 14:02 graker

At the moment I solved it by setting this.$el.style.width to the maximum allowed value when user tries to go over borders. Though it seems like a hack and I really would love to find a more component-related solution, like changing :width reactively (which I expected to work but it doesn't).

It also have some downsides:

  1. While the width set up this way works visually (user sees that the resizeable component won't grow further), component's width value still increases in the background. That could lead to misunderstanding if user would try to decrease the width that was set to a maximum value, because inside the component the width is greater.

  2. To fix 1) I had to reset component's width by changing the prop from parent component. Eventually it resulted in the component being recreated which is all right by itself. But when the component is recreated it looses resize handles and user may still want to resize it more.

  3. To fix 2) I used active prop for components re-created after resizing, so that newly created components would "keep" the handles. But it resulted in some weird behaviour: while setting this.$el.style.width works the way I described above, it doesn't work if the draggable component has it's active prop initially set to true. Somehow in this case user's resizing overlaps my way of width enforcing and user can visually pass the borders. Though if user would deactivate the component by clicking on the outside and then make it active manually, the width enforcing works again.

So it raises an additional question: what is different in the component when the active prop is initially set to true in comparison to the component made active later by user clicking on it?

graker avatar Feb 26 '20 08:02 graker

Hi!

First of all, thank you for reporting the issue with this level of details. Much appreciated!

changing :width in @resizing event doesn't do anything

w and h are reactive props, but they are disabled during dragging or resizing because otherwise they would interfere with the internals of the component. I know that this is not the best idea and often it causes confusion, but I'm afraid that solving this problem would require a huge rewrite of the component.

Back to your problem: there's no way right now to force a specific width during resizing or dragging, but you can use the resizestop and dragstop events to revert the size of the component to the desired values.

mauricius avatar Mar 01 '20 10:03 mauricius

Hello!

Thank you for the reply!

Reverting width on resizestop and dragstop events is what I did and it eventually resulted in the small issue I described in my second comment. In a nutshell, to process new width and position in the parent component I had to recreate the draggable-resizable component with new width and position. That's okay but the new component appears without resize handle which is not good if user was resizing the component. So I'm using active prop to keep handles visible. But when active prop is initially set to true, my $el.style.width hack behaves differently than when user manually clicks on the component to make it active. Basically, it doesn't work when the component is initially active.

So maybe you know of some difference in the component behavior when it is active from start comparing to made active by the user?

graker avatar Mar 02 '20 10:03 graker

w and h are reactive props, but they are disabled during dragging or resizing

Please specify this point in the documentation, I spent an hour before I found this thread.

libid0nes avatar May 04 '20 09:05 libid0nes

Sure, but it's an open source project which means that PRs are always welcome.

mauricius avatar May 04 '20 11:05 mauricius

@graker I have the same issue with re-setting width as you describe, but also when I want to reset x,y of the component, not active as in your case. But I think the source of the issue is the same.

While playing around with await nextTick(); I have found out that if I reset w,h first, then wait for the next tick, and then reset x,y in a separate operation - sometimes it works as I expected. And sometimes not. My issue is that if I did drag and resize, so I changed x,y, and w,h - when I reset them back, the component processes w,h change first and somehow internally calculates new x,y based on changed w,h. This calculation might be async, therefore sometimes after w,h change when I apply my stored x,y it does place the component in the right previous position, and sometimes it places it based in the previous top-left corner coordinates, where the new resized coordinates were.

I think the same happens in your case. You change width, it updates something internally and asynchronously, and then you change active, and it causes a second update cycle, but the behavior is not predictable, as the first async update might have ended already or not yet.

Because of this update issue, I find this component not usable, as I simply cannot reset w,h,x,y to initial values by setting them back in a rectangle without experiencing weird async rendering issues.

@mauricius FYI

seems like related to https://github.com/mauricius/vue-draggable-resizable/issues/183#issuecomment-529098451

seyfer avatar Jun 19 '23 10:06 seyfer

this is the solution that works for me. I do reset rectangles to an empty array, removing them, then wait until they are removed from being rendered with nextTick and then add them back from saved state.

  const resetToInitialState = async () => {
    state.rectangles = [];

    await nextTick();

    state.rectangles = [
      ...state.initialRectangles.map((initial) => reactive({ ...initial })),
    ];
  };

this is the only way it was possible to render dragged and resized rectangle back to the initial state and with the proper visual position.

seyfer avatar Jun 19 '23 11:06 seyfer