Skip to content

fix(drag): always put element under the mouse when dragging an element#2263

Merged
adumesny merged 9 commits intogridstack:masterfrom
ForestAdmin:fix/scroll-and-scale
Sep 30, 2023
Merged

fix(drag): always put element under the mouse when dragging an element#2263
adumesny merged 9 commits intogridstack:masterfrom
ForestAdmin:fix/scroll-and-scale

Conversation

@VincentMolinie
Copy link
Copy Markdown

@VincentMolinie VincentMolinie commented Apr 13, 2023

Description

The position of elements dragged was badly calculated and resulted in weird behaviour in a lot of cases like:

  • Disable automatic scroll, scroll page while dragging
  • Zoom out/in using the scale css property and try to drag a component

Checklist

  • Created tests which fail without the change (if possible)
  • All tests passing (yarn test)
  • Extended the README / documentation, if necessary

Issues fixed

#1275

@VincentMolinie
Copy link
Copy Markdown
Author

@adumesny could you take a look 🙏

@adumesny
Copy link
Copy Markdown
Member

thanks, will take a look. the title sounds scary, if all you wanted to fix was scrolling and scaling divs.

Copy link
Copy Markdown
Member

@adumesny adumesny left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so looks like you are handling parent div scale (what about general transform including translate ?) for dragging only, what about for sizing do we need the same ?

Comment thread src/dd-draggable.ts Outdated
Comment thread src/gridstack.ts Outdated
Comment thread src/utils.ts Outdated
Comment thread src/utils.ts
@adumesny adumesny changed the title fix(drag): place the element dragged always below the mouse fix(drag): handle div scale during dragging Apr 15, 2023
Comment thread src/dd-draggable.ts Outdated
@VincentMolinie
Copy link
Copy Markdown
Author

I'm still working on it, I'll let you know as soon as I fixed all the issues

@hosnyDev
Copy link
Copy Markdown

@VincentMolinie @adumesny please can u fix this issue ASAP 😊

@VincentMolinie
Copy link
Copy Markdown
Author

Everything should be fixed now :)

@VincentMolinie
Copy link
Copy Markdown
Author

@VincentMolinie @adumesny please can u fix this issue ASAP 😊

Yes don't worry that was my top priority 😄

@VincentMolinie
Copy link
Copy Markdown
Author

@VincentMolinie @adumesny please can u fix this issue ASAP 😊

Yes don't worry that was my top priority 😄
It's just not that easy to fix ^^

@hosnyDev
Copy link
Copy Markdown

@VincentMolinie appreciated TYT "D

@VincentMolinie
Copy link
Copy Markdown
Author

VincentMolinie commented Apr 19, 2023

Well I don't need to take my time, as I say I did fix every issues I could find 👼

@VincentMolinie VincentMolinie changed the title fix(drag): handle div scale during dragging fix(drag): handle element scale during dragging / resizing Apr 19, 2023
@VincentMolinie
Copy link
Copy Markdown
Author

@adumesny it's back to you 😄

@adumesny
Copy link
Copy Markdown
Member

adumesny commented May 1, 2023

I pulled your changes locally, and demo/offset.html no longer works... can't trade scaling for offset.

Also I even tried just scale and it's badly broken too ! so not sure what you tested this on...

    <div style="transform: scale(0.5, 0.5)">
      <div class="grid-stack"></div>
    </div>

at this point I'm not sure what you are trying to fix (the code change looks much more complicated than I would expect) and didn't plan on fixing scale transform - I don't understand the use case for it. Browser scaling (for accessibility and others) works just fine, so why woudl you scale a div like above ???

@VincentMolinie VincentMolinie changed the title fix(drag): handle element scale during dragging / resizing fix(drag): always put element under the mouse when dragging an element May 4, 2023
@VincentMolinie
Copy link
Copy Markdown
Author

Good catch @adumesny , sorry about that. I fixed those issue and I added a demo for the scale so we can test it easily

@VincentMolinie
Copy link
Copy Markdown
Author

VincentMolinie commented May 23, 2023

if some people would like to use this fix I published a temporary version here until it's officially released 😄 : git+https://github.com/VincentMolinie/gridstack.js.git#public/scroll-and-scale

Comment thread src/dd-draggable.ts Outdated
Comment thread src/dd-draggable.ts Outdated
Comment thread src/dd-resizable.ts
Comment thread src/utils.ts Outdated
if (style.position === 'relative' || style.position === 'absolute' || style.position === 'fixed') {
return el;
} else {
return this.getPositionContainerElement(el.parentElement);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there is no 'this' in static methods. use return Utils.getPositionContainerElement(...)
also no need to have else {} (a bit more compact, although less readable)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

check for el.parentElement being undefined

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the check for el.parentElement being undefined, it's already checked by the first line, we do a !this.el so it's actually !el.parentElement

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you still have this.xyz in a static method!

Comment thread src/utils.ts Outdated
Comment thread src/utils.ts Outdated
Comment thread src/utils.ts Outdated
@adumesny
Copy link
Copy Markdown
Member

adumesny commented May 27, 2023

your code is still completely breaking the main example demo/two.html (that and nested.html are the main 2 I always test against) at a minimum.

when you say you have fixed all issue, I'm not sure what you are using.
Again, why do we need to suport div scaling ? at this point it is more risk breaking things than is worth... Also you NEED to test mobile devices as I don't want to have to test+fix mobile touch all over as well...

start drag make object jump to (0,0)
image

@adumesny
Copy link
Copy Markdown
Member

I would strongly suggest you use the existing code but add scaling to the mix, rather than a complete re-write which has proven to be unreliable so far... twice, it broke very basic flows...

@VincentMolinie VincentMolinie force-pushed the fix/scroll-and-scale branch 2 times, most recently from 830b8d3 to c73ac4b Compare June 1, 2023 18:35
@VincentMolinie
Copy link
Copy Markdown
Author

I retested all the demo. now it should be good sorry about that 😥

@DigitagDev
Copy link
Copy Markdown
Contributor

@adumesny I work on a project where I drag items from a toolbar and drop them onto a grid. I even tried using the scale() function on the parent div to include a zoom effect on the area of my grid. Considering that several grids in the project need to have real scale, where a grid could occupy almost the entire screen or be a 200x200px grid with very small items, a zoom tool would be almost essential in my project. I'm mentioning this to clarify why I'm also trying to use the scale zoom. Unfortunately, browser support for this type of situation is only available for the entire page, not specifically for a div.

@leemoria
Copy link
Copy Markdown

leemoria commented Jun 7, 2023

I also need scale,Have you some methods to resolve?@adumesny

@adumesny
Copy link
Copy Markdown
Member

adumesny commented Jun 8, 2023

I'll take a look this weekend...

@adumesny
Copy link
Copy Markdown
Member

ran out of time over the weekend (had bugs to fix) and I will be on PTO soon, so this will have to wait till I get back the weekend in ~2 weeks

@VincentMolinie
Copy link
Copy Markdown
Author

I found some issues, so you can delay your review 😞

@VincentMolinie
Copy link
Copy Markdown
Author

Okey I finally found the issue, and the reason why I had such a difference between the demo and my own project.
The Helper is positioned in fixed when dragging. When having a position fixed it's positioned according to the viewport OR the first parent that has a transform. Which explain the weird position issue I had

So everything should be good now 🤞

Comment thread src/dd-draggable.ts Outdated
Comment thread src/dd-draggable.ts Outdated
}
if (node) {
const rect = this.el.getBoundingClientRect();
this._originalMousePositionInsideElement = { x: s.clientX - rect.left, y: s.clientY - rect.top };
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

'_' is reserved for private vars, not protected. also rename to 'origRelativeMouse' and create based on this.mouseDownEvent otherwise you are off by 3+ pixels. ALSO don't really need that since we have this.mouseDownEvent (less vars to track)

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm using s which is the downEvent.el can move so no, we need to keep that variable

Comment thread src/dd-draggable.ts Outdated
Comment thread src/dd-resizable.ts
position: {
left: rect.left - containmentRect.left,
top: rect.top - containmentRect.top
left: rect.left / scaleX,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so this has a very different meaning - was relative position, and now it's not (and scaled). Thsi will affect user of those values...

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was private API. Everyone knows that using private API might break even when just patching a version 🤔. I can't really see the issue here 😅

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's acutally protected (though it does have the _ which is usually reserved for private) so any subclass would break. but I have it @internal so no exported types for TS subclassing. need to clean and make private again.
but DDUIData is NOT private last time I checked and you changed the meaning...

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't changed the meaning. This is the real position of the element. temporalRect has been updated to be at the right position

Comment thread src/types.ts Outdated
Comment thread src/utils.ts Outdated
if (style.position === 'relative' || style.position === 'absolute' || style.position === 'fixed') {
return el;
} else {
return this.getPositionContainerElement(el.parentElement);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you still have this.xyz in a static method!

Comment thread src/utils.ts Outdated
Comment thread src/utils.ts
@adumesny
Copy link
Copy Markdown
Member

adumesny commented Aug 6, 2023

still doens't behave the right way.

two.html the item jumps to cursor (0,0) when starting a new (helper) drag say from the middle instead of being under the mouse
image
instead of https://gridstackjs.com/demo/two.html
image

also whe scaling (with zoom IN is much more optvious) you can see some weird wiggle animation where you must be changing the top & left position even though only scale bottom right happens. Didn't look at your code but it's not right.

20230806_154047.mp4

@VincentMolinie
Copy link
Copy Markdown
Author

Everything is fixed @adumesny, thanks again for your review and sorry for the delay, I was in holidays ^^

Comment thread src/dd-resizable.ts
position: {
left: rect.left - containmentRect.left,
top: rect.top - containmentRect.top
left: rect.left / scaleX,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's acutally protected (though it does have the _ which is usually reserved for private) so any subclass would break. but I have it @internal so no exported types for TS subclassing. need to clean and make private again.
but DDUIData is NOT private last time I checked and you changed the meaning...

Comment thread src/utils.ts
Comment thread src/utils.ts
Comment thread src/utils.ts Outdated
Comment thread src/utils.ts Outdated
let el = element;

// Check if element is visible, otherwise the width/height will be of 0
while (el && !el.offsetParent) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"element is visible" and offsetParent are the same ???? comment is off

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

offsetParent is null when an element is invisible 😉

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know clientHeight\Width are 0 when invisible (makes sense), but didn't know about offsetParent (less obvious). I would sue that (which matches your comment)

Comment thread src/dd-draggable.ts
};
}

/** @internal TODO: set to public as called by DDDroppable! */
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you've ppossibly changed the meaning of DDUIData here, yet this is used by DDDroppable according to comment, and I don't see changes in this review.... did you follow through and see how it is used and if this might be a breaking change ?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's still the position of the helper 🤔

Comment thread src/dd-resizable.ts
const containerElement = Utils.getPositionContainerElement(this.el.parentElement);
const containerRect = containerElement.getBoundingClientRect();

const newRect = { // Note: originalRect is a complex object, not a simple Rect, so copy out.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to be honest I don't know _getChange() is used anymore...

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes it is. It is actually all the logic of the resize. It's called every time we resize

Comment thread src/dd-resizable.ts
const minHeight = this.option.minHeight || oHeight;
const { scaleX, scaleY } = Utils.getScaleForElement(this.el);
const o = this.option;
const maxWidth = o.maxWidth ? o.maxWidth * scaleX : Number.MAX_SAFE_INTEGER;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if I recall this.options.maxWidth = n.maxW * cellWidth, so they still need to be scalled ?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes because the scale apply to the grid so if the cell are 10px and you scale to 2 you will have actual cell of 20px

Comment thread src/dd-resizable.ts Outdated
this.el.style[key] = value - containmentRect[key] + 'px';
});
const { scaleX, scaleY } = Utils.getScaleForElement(this.el);
this.el.style.width = `${Math.floor(this.temporalRect.width / scaleX)}px`;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why floor() and not round() ? if I recall you didn't have anyhting before and claimed all was working. what changed ?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nothing floor was working that's it. I can do round to

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I replaced it by a round

Comment thread src/utils.ts Outdated
@VincentMolinie
Copy link
Copy Markdown
Author

Back to you @adumesny 😉

@adumesny
Copy link
Copy Markdown
Member

adumesny commented Sep 5, 2023

still broken... while main demos seem fine (two.html and nested.html) offset.html doesn't work now.

20230904_175826.mp4

@VincentMolinie
Copy link
Copy Markdown
Author

The refacto did introduce a bug on the getContainerForPositionFixedElement

@VincentMolinie
Copy link
Copy Markdown
Author

@adumesny can you take a look at it 👼 ?

@adumesny
Copy link
Copy Markdown
Member

swamped at work and can't this weekend... I'll try to find time.

@adumesny
Copy link
Copy Markdown
Member

great, I tested a bunch of examples and mobile and it's working now. Will commit and go over the code again. thanks.

@adumesny adumesny merged commit b559a6f into gridstack:master Sep 30, 2023
@VincentMolinie VincentMolinie deleted the fix/scroll-and-scale branch October 9, 2023 14:10
adumesny added a commit to adumesny/gridstack.js that referenced this pull request Oct 15, 2023
adumesny added a commit to adumesny/gridstack.js that referenced this pull request Oct 15, 2023
VincentMolinie pushed a commit to ForestAdmin/gridstack.js that referenced this pull request Oct 16, 2023
gridstack#2263)

* fix(drag): place the element dragged always below the mouse

* fix: review fixes

* fix: fix data transfer demo

* fix: review fixes

* fix: fix position according to first transform parent

* review fixes

* review fixes

* review fixes

* fix: fix the refacto

---------

Co-authored-by: Vincent Molinié <vincent.m@forestadmin.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants