Skip to content

Commit bd4e993

Browse files
author
Mohamed Amdoun
committed
moved scale from ref element functions to utils
adjusted position and scale of helper that is dragged from outside
1 parent c0abba2 commit bd4e993

File tree

4 files changed

+107
-66
lines changed

4 files changed

+107
-66
lines changed

src/dd-draggable.ts

Lines changed: 23 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
*/
55

66
import { DDManager } from './dd-manager';
7-
import { Utils } from './utils';
7+
import { DragTransform, Utils } from './utils';
88
import { DDBaseImplement, HTMLElementExtendOpt } from './dd-base-impl';
99
import { GridItemHTMLElement, DDUIData } from './types';
1010
import { DDElementHost } from './dd-element';
@@ -33,11 +33,6 @@ interface DragOffset {
3333
offsetTop: number;
3434
}
3535

36-
interface DragScaleReciprocal {
37-
x: number;
38-
y: number;
39-
}
40-
4136
type DDDragEvent = 'drag' | 'dragstart' | 'dragstop';
4237

4338
// make sure we are not clicking on known object that handles mouseDown
@@ -55,8 +50,6 @@ export class DDDraggable extends DDBaseImplement implements HTMLElementExtendOpt
5550
/** @internal */
5651
protected dragOffset: DragOffset;
5752
/** @internal */
58-
protected dragScale: DragScaleReciprocal = { x: 1, y: 1 };
59-
/** @internal */
6053
protected dragElementOriginStyle: Array<string>;
6154
/** @internal */
6255
protected dragEl: HTMLElement;
@@ -70,6 +63,15 @@ export class DDDraggable extends DDBaseImplement implements HTMLElementExtendOpt
7063
protected static originStyleProp = ['transition', 'pointerEvents', 'position', 'left', 'top', 'minWidth', 'willChange'];
7164
/** @internal pause before we call the actual drag hit collision code */
7265
protected dragTimeout: number;
66+
/** @internal */
67+
protected transformReference: HTMLElement;
68+
/** @internal */
69+
protected dragTransform: DragTransform = {
70+
xScale: 1,
71+
yScale: 1,
72+
xOffset: 0,
73+
yOffset: 0
74+
};
7375

7476
constructor(el: HTMLElement, option: DDDraggableOpt = {}) {
7577
super();
@@ -79,6 +81,7 @@ export class DDDraggable extends DDBaseImplement implements HTMLElementExtendOpt
7981
// get the element that is actually supposed to be dragged by
8082
let handleName = option.handle.substring(1);
8183
this.dragEl = el.classList.contains(handleName) ? el : el.querySelector(option.handle) || el;
84+
this.transformReference = Utils.createTransformReferenceElement();
8285
// create var event binding so we can easily remove and still look like TS methods (unlike anonymous functions)
8386
this._mouseDown = this._mouseDown.bind(this);
8487
this._mouseMove = this._mouseMove.bind(this);
@@ -214,6 +217,10 @@ export class DDDraggable extends DDBaseImplement implements HTMLElementExtendOpt
214217
}
215218
this.helper = this._createHelper(e);
216219
this._setupHelperContainmentStyle();
220+
this.dragTransform = Utils.getValuesFromTransformedElement(
221+
this.transformReference,
222+
this.helperContainment
223+
);
217224
this.dragOffset = this._getDragOffset(e, this.el, this.helperContainment);
218225
const ev = Utils.initEvent<DragEvent>(e, { target: this.el, type: 'dragstart' });
219226

@@ -336,8 +343,8 @@ export class DDDraggable extends DDBaseImplement implements HTMLElementExtendOpt
336343
// }
337344
const style = this.helper.style;
338345
const offset = this.dragOffset;
339-
style.left = (e.clientX + offset.offsetLeft - containmentRect.left) * this.dragScale.x + 'px';
340-
style.top = (e.clientY + offset.offsetTop - containmentRect.top) * this.dragScale.y + 'px';
346+
style.left = (e.clientX + offset.offsetLeft - containmentRect.left) * this.dragTransform.xScale + 'px';
347+
style.top = (e.clientY + offset.offsetTop - containmentRect.top) * this.dragTransform.yScale + 'px';
341348
}
342349

343350
/** @internal */
@@ -359,25 +366,8 @@ export class DDDraggable extends DDBaseImplement implements HTMLElementExtendOpt
359366
let xformOffsetX = 0;
360367
let xformOffsetY = 0;
361368
if (parent) {
362-
const testEl = document.createElement('div');
363-
Utils.addElStyles(testEl, {
364-
opacity: '0',
365-
position: 'fixed',
366-
top: 0 + 'px',
367-
left: 0 + 'px',
368-
width: '1px',
369-
height: '1px',
370-
zIndex: '-999999',
371-
});
372-
parent.appendChild(testEl);
373-
const testElPosition = testEl.getBoundingClientRect();
374-
parent.removeChild(testEl);
375-
xformOffsetX = testElPosition.left;
376-
xformOffsetY = testElPosition.top;
377-
this.dragScale = {
378-
x: 1 / testElPosition.width,
379-
y: 1 / testElPosition.height
380-
};
369+
xformOffsetX = this.dragTransform.xOffset;
370+
xformOffsetY = this.dragTransform.yOffset;
381371
}
382372

383373
const targetOffset = el.getBoundingClientRect();
@@ -386,8 +376,8 @@ export class DDDraggable extends DDBaseImplement implements HTMLElementExtendOpt
386376
top: targetOffset.top,
387377
offsetLeft: - event.clientX + targetOffset.left - xformOffsetX,
388378
offsetTop: - event.clientY + targetOffset.top - xformOffsetY,
389-
width: targetOffset.width * this.dragScale.x,
390-
height: targetOffset.height * this.dragScale.y
379+
width: targetOffset.width * this.dragTransform.xScale,
380+
height: targetOffset.height * this.dragTransform.yScale
391381
};
392382
}
393383

@@ -398,8 +388,8 @@ export class DDDraggable extends DDBaseImplement implements HTMLElementExtendOpt
398388
const offset = this.helper.getBoundingClientRect();
399389
return {
400390
position: { //Current CSS position of the helper as { top, left } object
401-
top: (offset.top - containmentRect.top) * this.dragScale.y,
402-
left: (offset.left - containmentRect.left) * this.dragScale.x
391+
top: (offset.top - containmentRect.top) * this.dragTransform.yScale,
392+
left: (offset.left - containmentRect.left) * this.dragTransform.xScale
403393
}
404394
/* not used by GridStack for now...
405395
helper: [this.helper], //The object arr representing the helper that's being dragged.

src/dd-resizable.ts

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -57,11 +57,14 @@ export class DDResizable extends DDBaseImplement implements HTMLElementExtendOpt
5757
protected parentOriginStylePosition: string;
5858
/** @internal */
5959
protected static _originStyleProp = ['width', 'height', 'position', 'left', 'top', 'opacity', 'zIndex'];
60+
/** @internal */
61+
protected transformReference: HTMLElement;
6062

6163
constructor(el: HTMLElement, opts: DDResizableOpt = {}) {
6264
super();
6365
this.el = el;
6466
this.option = opts;
67+
this.transformReference = Utils.createTransformReferenceElement();
6568
// create var event binding so we can easily remove and still look like TS methods (unlike anonymous functions)
6669
this._mouseOver = this._mouseOver.bind(this);
6770
this._mouseOut = this._mouseOut.bind(this);
@@ -226,22 +229,10 @@ export class DDResizable extends DDBaseImplement implements HTMLElementExtendOpt
226229
this.parentOriginStylePosition = this.el.parentElement.style.position;
227230

228231
const parent = this.el.parentElement;
229-
const testEl = document.createElement('div');
230-
Utils.addElStyles(testEl, {
231-
opacity: '0',
232-
position: 'fixed',
233-
top: 0 + 'px',
234-
left: 0 + 'px',
235-
width: '1px',
236-
height: '1px',
237-
zIndex: '-999999',
238-
});
239-
parent.appendChild(testEl);
240-
const testElPosition = testEl.getBoundingClientRect();
241-
parent.removeChild(testEl);
232+
const transformValues = Utils.getValuesFromTransformedElement(this.transformReference, parent);
242233
this.rectScale = {
243-
x: 1 / testElPosition.width,
244-
y: 1 / testElPosition.height
234+
x: transformValues.xScale,
235+
y: transformValues.yScale
245236
};
246237

247238
if (getComputedStyle(this.el.parentElement).position.match(/static/)) {

src/gridstack.ts

Lines changed: 37 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
* see root license https://github.com/gridstack/gridstack.js/tree/master/LICENSE
77
*/
88
import { GridStackEngine } from './gridstack-engine';
9-
import { Utils, HeightData, obsolete } from './utils';
9+
import { Utils, HeightData, obsolete, DragTransform } from './utils';
1010
import { gridDefaults, ColumnOptions, GridItemHTMLElement, GridStackElement, GridStackEventHandlerCallback,
1111
GridStackNode, GridStackWidget, numberOrString, DDUIData, DDDragInOpt, GridStackPosition, GridStackOptions,
1212
dragInDefaultOptions, GridStackEventHandler, GridStackNodesHandler, AddRemoveFcn, SaveFcn, CompactOptions, GridStackMoveOpts, ResizeToContentFcn } from './types';
@@ -2031,34 +2031,50 @@ export class GridStack {
20312031
let cellHeight: number, cellWidth: number;
20322032

20332033
// creates a reference element for tracking the right position after scaling
2034-
const testEl = document.createElement('div');
2035-
Utils.addElStyles(testEl, {
2036-
opacity: '0',
2037-
position: 'fixed',
2038-
top: 0 + 'px',
2039-
left: 0 + 'px',
2040-
width: '1px',
2041-
height: '1px',
2042-
zIndex: '-999999',
2043-
});
2034+
const transformReference = Utils.createTransformReferenceElement();
20442035

20452036
let onDrag = (event: DragEvent, el: GridItemHTMLElement, helper: GridItemHTMLElement) => {
20462037
let node = el.gridstackNode;
20472038
if (!node) return;
20482039

20492040
helper = helper || el;
2050-
helper.appendChild(testEl);
2051-
const testElPosition = testEl.getBoundingClientRect();
2052-
helper.removeChild(testEl);
2053-
const dragScale = {
2054-
x: 1 / testElPosition.width,
2055-
y: 1 / testElPosition.height,
2041+
let transformValues: DragTransform;
2042+
// if we are dragging an element in and out that is coming from a grid
2043+
// we get the transform values by using the helper attached to the grid
2044+
if (node.grid?.el) {
2045+
transformValues = Utils.getValuesFromTransformedElement(transformReference, helper)
2046+
}
2047+
// if the element is being dragged from outside (not from any grid)
2048+
// we use the grid as the transformation reference, since the helper is not subject to transformation
2049+
else if (this._placeholder && this._placeholder.closest('.grid-stack')) {
2050+
const gridEl = this._placeholder.closest('.grid-stack') as HTMLElement;
2051+
transformValues = Utils.getValuesFromTransformedElement(transformReference, gridEl);
2052+
// if the element is being dragged from outside, scale it down to match the grid's scale
2053+
helper.style.transform = `scale(${1 / transformValues.xScale},${1 / transformValues.yScale})`;
2054+
// this makes it so that the helper is well positioned relative to the mouse after scaling
2055+
const helperRect = helper.getBoundingClientRect();
2056+
helper.style.left = helperRect.x + (transformValues.xScale - 1) * (event.clientX - helperRect.x) / transformValues.xScale + 'px';
2057+
helper.style.top = helperRect.y + (transformValues.yScale - 1) * (event.clientY - helperRect.y) / transformValues.yScale + 'px';
2058+
helper.style.transformOrigin = `0px 0px`
2059+
} // if all else fails, we might want to use the default transform value
2060+
else {
2061+
transformValues = {
2062+
xScale: 1,
2063+
xOffset: 0,
2064+
yScale: 1,
2065+
yOffset: 0,
2066+
}
20562067
}
20572068
let parent = this.el.getBoundingClientRect();
20582069
let {top, left} = helper.getBoundingClientRect();
20592070
left -= parent.left;
20602071
top -= parent.top;
2061-
let ui: DDUIData = {position: {top: top * dragScale.y, left: left * dragScale.x}};
2072+
let ui: DDUIData = {
2073+
position: {
2074+
top: top * transformValues.xScale,
2075+
left: left * transformValues.yScale
2076+
}
2077+
};
20622078

20632079
if (node._temporaryRemoved) {
20642080
node.x = Math.max(0, Math.round(left / cellWidth));
@@ -2534,6 +2550,9 @@ export class GridStack {
25342550
let node = el.gridstackNode;
25352551
if (!node) return;
25362552

2553+
helper = helper || el;
2554+
// restore the scale of the helper on leave
2555+
helper.style.transform = 'scale(1)';
25372556
dd.off(el, 'drag'); // no need to track while being outside
25382557

25392558
// this gets called when cursor leaves and shape is outside, so only do this once

src/utils.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,13 @@ export interface HeightData {
1010
unit: string;
1111
}
1212

13+
export interface DragTransform {
14+
xScale: number;
15+
yScale: number;
16+
xOffset: number;
17+
yOffset: number;
18+
}
19+
1320
/** checks for obsolete method names */
1421
// eslint-disable-next-line
1522
export function obsolete(self, f, oldName: string, newName: string, rev: string): (...args: any[]) => any {
@@ -554,6 +561,40 @@ export class Utils {
554561
(target || e.target).dispatchEvent(simulatedEvent);
555562
}
556563

564+
/**
565+
* defines an element that is used to get the offset and scale from grid transforms
566+
* has to be hooked to a helper
567+
* should be called once
568+
*/
569+
public static createTransformReferenceElement(): HTMLElement {
570+
const transformReference = document.createElement('div');
571+
Utils.addElStyles(transformReference, {
572+
opacity: '0',
573+
position: 'fixed',
574+
top: 0 + 'px',
575+
left: 0 + 'px',
576+
width: '1px',
577+
height: '1px',
578+
zIndex: '-999999',
579+
});
580+
return transformReference;
581+
}
582+
583+
/**
584+
* can be used after setting the reference element from createTransformReferenceElement
585+
*/
586+
public static getValuesFromTransformedElement(transformReference: HTMLElement, helper: HTMLElement): DragTransform {
587+
helper.appendChild(transformReference);
588+
const transformValues = transformReference.getBoundingClientRect();
589+
helper.removeChild(transformReference);
590+
return {
591+
xScale: 1 / transformValues.width,
592+
yScale: 1 / transformValues.height,
593+
xOffset: transformValues.left,
594+
yOffset: transformValues.top,
595+
}
596+
}
597+
557598
/** returns true if event is inside the given element rectangle */
558599
// Note: Safari Mac has null event.relatedTarget which causes #1684 so check if DragEvent is inside the coordinates instead
559600
// this.el.contains(event.relatedTarget as HTMLElement)

0 commit comments

Comments
 (0)