Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 34 additions & 15 deletions src/dd-draggable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ import { isTouch, touchend, touchmove, touchstart, pointerdown, DDTouch } from '
import { GridHTMLElement } from './gridstack';

interface DragOffset {
left: number;
x: number;
top: number;
width: number;
height: number;
offsetLeft: number;
offsetX: number;
offsetTop: number;
}

Expand Down Expand Up @@ -51,7 +51,7 @@ export class DDDraggable extends DDBaseImplement implements HTMLElementExtendOpt
/** @internal */
protected helperContainment: HTMLElement;
/** @internal properties we change during dragging, and restore back */
protected static originStyleProp = ['width', 'height', 'transform', 'transform-origin', 'transition', 'pointerEvents', 'position', 'left', 'top', 'minWidth', 'willChange'];
protected static originStyleProp = ['width', 'height', 'transform', 'transform-origin', 'transition', 'pointerEvents', 'position', 'left', 'right', 'top', 'minWidth', 'willChange'];
/** @internal pause before we call the actual drag hit collision code */
protected dragTimeout: number;
/** @internal */
Expand Down Expand Up @@ -289,7 +289,10 @@ export class DDDraggable extends DDBaseImplement implements HTMLElementExtendOpt
n._origRotate = n._origRotate || { ...n._orig }; // store the real orig size in case we Esc after doing rotation
delete n._moving; // force rotate to happen (move waits for >50% coverage otherwise)
grid.setAnimation(false) // immediate rotate so _getDragOffset() gets the right dom size below
.rotate(n.el, { top: -this.dragOffset.offsetTop, left: -this.dragOffset.offsetLeft })
.rotate(n.el, {
top: -this.dragOffset.offsetTop,
left: -this.dragOffset.offsetX
})
.setAnimation();
n._moving = true;
this.dragOffset = this._getDragOffset(this.lastDrag, n.el, this.helperContainment);
Expand Down Expand Up @@ -326,7 +329,7 @@ export class DDDraggable extends DDBaseImplement implements HTMLElementExtendOpt
// style.cursor = 'move'; // TODO: can't set with pointerEvents=none ! (no longer in CSS either as no-op)
style.width = this.dragOffset.width + 'px';
style.height = this.dragOffset.height + 'px';
style.willChange = 'left, top';
style.willChange = 'left, right, top';
style.position = 'fixed'; // let us drag between grids by not clipping as parent .grid-stack is position: 'relative'
this._dragFollow(e); // now position it
style.transition = 'none'; // show up instantly
Expand Down Expand Up @@ -362,15 +365,18 @@ export class DDDraggable extends DDBaseImplement implements HTMLElementExtendOpt

/** @internal updates the top/left position to follow the mouse */
public _dragFollow(e: DragEvent): void {
const containmentRect = { left: 0, top: 0 };
// if (this.helper.style.position === 'absolute') { // we use 'fixed'
// const { left, top } = this.helperContainment.getBoundingClientRect();
// containmentRect = { left, top };
// }
const style = this.helper.style;
const offset = this.dragOffset;
style.left = (e.clientX + offset.offsetLeft - containmentRect.left) * this.dragTransform.xScale + 'px';
style.top = (e.clientY + offset.offsetTop - containmentRect.top) * this.dragTransform.yScale + 'px';
if (this.option.rtl) {
style.right = ((window.innerWidth - e.clientX) + offset.offsetX) * this.dragTransform.xScale + 'px';
if (style.left)
style.left = '';
} else {
style.left = (e.clientX + offset.offsetX) * this.dragTransform.xScale + 'px';
if (style.right)
style.right = '';
}
style.top = (e.clientY + offset.offsetTop) * this.dragTransform.yScale + 'px';
}

/** @internal */
Expand All @@ -397,10 +403,15 @@ export class DDDraggable extends DDBaseImplement implements HTMLElementExtendOpt
}

const targetOffset = el.getBoundingClientRect();
let x = this.option.rtl ? targetOffset.right : targetOffset.left;
let offsetX = this.option.rtl
? (event.clientX - targetOffset.right + xformOffsetX)
: (-event.clientX + targetOffset.left - xformOffsetX);

return {
left: targetOffset.left,
x,
top: targetOffset.top,
offsetLeft: - event.clientX + targetOffset.left - xformOffsetX,
offsetX,
offsetTop: - event.clientY + targetOffset.top - xformOffsetY,
width: targetOffset.width * this.dragTransform.xScale,
height: targetOffset.height * this.dragTransform.yScale
Expand Down Expand Up @@ -473,10 +484,18 @@ export class DDDraggable extends DDBaseImplement implements HTMLElementExtendOpt
const containmentEl = this.el.parentElement;
const containmentRect = containmentEl.getBoundingClientRect();
const offset = this.helper.getBoundingClientRect();

// RTL: GridStack measures column positions from the right side of the container,
// so we report `left` as the distance between the helper's right edge and the
// container's right edge (both in viewport-left coordinates via getBoundingClientRect).
const leftPos = this.option.rtl
? (containmentRect.right - offset.right) * this.dragTransform.xScale
: (offset.left - containmentRect.left) * this.dragTransform.xScale;

return {
position: { //Current CSS position of the helper as { top, left } object
top: (offset.top - containmentRect.top) * this.dragTransform.yScale,
left: (offset.left - containmentRect.left) * this.dragTransform.xScale
left: leftPos
}
/* not used by GridStack for now...
helper: [this.helper], //The object arr representing the helper that's being dragged.
Expand Down
7 changes: 5 additions & 2 deletions src/dd-gridstack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,8 @@ export class DDGridStack {
...{
start: opts.start,
stop: opts.stop,
resize: opts.resize
resize: opts.resize,
rtl: opts.rtl,
}
});
}
Expand All @@ -111,6 +112,7 @@ export class DDGridStack {
* @param opts - Drag options or command ('enable', 'disable', 'destroy', 'option', or config object)
* @param key - Option key when using 'option' command
* @param value - Option value when using 'option' command
* @param rtl - Are we in rtl mode?
* @returns this instance for chaining
*
* @example
Expand All @@ -133,7 +135,8 @@ export class DDGridStack {
// containment: (grid.parentGridNode && grid.opts.dragOut === false) ? grid.el.parentElement : (grid.opts.draggable.containment || null),
start: opts.start,
stop: opts.stop,
drag: opts.drag
drag: opts.drag,
rtl: opts.rtl,
}
});
}
Expand Down
75 changes: 58 additions & 17 deletions src/dd-resizable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import { DDResizableHandle } from './dd-resizable-handle';
import { DDBaseImplement, HTMLElementExtendOpt } from './dd-base-impl';
import { Utils } from './utils';
import { DDResizeOpt, DDUIData, GridItemHTMLElement, GridStackMouseEvent, Rect, Size } from './types';
import { DDResizeOpt, DDUIData, GridItemHTMLElement, GridStackMouseEvent, Size } from './types';
import { DDManager } from './dd-manager';

// import { GridItemHTMLElement } from './types'; let count = 0; // TEST
Expand All @@ -22,22 +22,31 @@ export interface DDResizableOpt extends DDResizeOpt {
start?: (event: Event, ui: DDUIData) => void;
stop?: (event: Event) => void;
resize?: (event: Event, ui: DDUIData) => void;
rtl?: boolean;
}

interface RectScaleReciprocal {
x: number;
y: number;
}

interface TemporalRect {
width: number;
height: number;
left: number;
right: number;
top: number;
}

export class DDResizable extends DDBaseImplement implements HTMLElementExtendOpt<DDResizableOpt> {
/** @internal */
protected handlers: DDResizableHandle[];
/** @internal */
protected originalRect: Rect;
protected originalRect: DOMRectReadOnly;
/** @internal */
protected rectScale: RectScaleReciprocal = { x: 1, y: 1 };
/** @internal */
protected temporalRect: Rect;
protected temporalRect: TemporalRect;
/** @internal */
protected scrollY: number;
/** @internal */
Expand All @@ -51,7 +60,7 @@ export class DDResizable extends DDBaseImplement implements HTMLElementExtendOpt
/** @internal */
protected parentOriginStylePosition: string;
/** @internal */
protected static _originStyleProp = ['width', 'height', 'position', 'left', 'top', 'opacity', 'zIndex'];
protected static _originStyleProp = ['width', 'height', 'position', 'left', 'right', 'top', 'opacity', 'zIndex'];
/** @internal */
protected sizeToContent: boolean;

Expand Down Expand Up @@ -185,6 +194,8 @@ export class DDResizable extends DDBaseImplement implements HTMLElementExtendOpt
this._applyChange();
const ev = Utils.initEvent<GridStackMouseEvent>(event, { type: 'resize', target: this.el });
ev.resizeDir = dir; // expose handle direction so _dragOrResize can avoid position drift
ev.hasMovedX = this.option.rtl ? dir.includes('e') : dir.includes('w');
ev.hasMovedY = dir.includes('n');
if (this.option.resize) {
this.option.resize(ev, this._ui());
}
Expand Down Expand Up @@ -240,38 +251,51 @@ export class DDResizable extends DDBaseImplement implements HTMLElementExtendOpt
}

/** @internal */
protected _getChange(event: MouseEvent, dir: string): Rect {
protected _getChange(event: MouseEvent, dir: string): TemporalRect {
const oEvent = this.startEvent;
const newRect = { // Note: originalRect is a complex object, not a simple Rect, so copy out.
width: this.originalRect.width,
height: this.originalRect.height + this.scrolled,
left: this.originalRect.left,
top: this.originalRect.top - this.scrolled
right: this.originalRect.right,
top: this.originalRect.top - this.scrolled,
};

const offsetX = event.clientX - oEvent.clientX;
const offsetY = this.sizeToContent ? 0 : event.clientY - oEvent.clientY; // prevent vert resize
let moveLeft: boolean;
let moveUp: boolean;

if (dir.indexOf('e') > -1) {
const isRtl = this.option.rtl;

if (!isRtl && dir.indexOf('e') > -1) {
newRect.width += offsetX;
} else if (dir.indexOf('w') > -1) {
} else if (isRtl && dir.indexOf('w') > -1) {
newRect.width -= offsetX;
} else if (!isRtl && dir.indexOf('w') > -1) {
newRect.width -= offsetX;
newRect.left += offsetX;
moveLeft = true;
} else if (isRtl && dir.indexOf('e') > -1) {
newRect.width += offsetX;
newRect.right += offsetX;
moveLeft = true;
}

if (dir.indexOf('s') > -1) {
newRect.height += offsetY;
} else if (dir.indexOf('n') > -1) {
newRect.height -= offsetY;
newRect.top += offsetY
moveUp = true;
}

const constrain = this._constrainSize(newRect.width, newRect.height, moveLeft, moveUp);
if (Math.round(newRect.width) !== Math.round(constrain.width)) { // round to ignore slight round-off errors
if (dir.indexOf('w') > -1) {
if (!isRtl && dir.indexOf('w') > -1) {
newRect.left += newRect.width - constrain.width;
} else if (isRtl && dir.indexOf('e') > -1) {
newRect.right -= newRect.width - constrain.width;
}
newRect.width = constrain.width;
}
Expand All @@ -298,17 +322,29 @@ export class DDResizable extends DDBaseImplement implements HTMLElementExtendOpt

/** @internal */
protected _applyChange(): DDResizable {
let containmentRect = { left: 0, top: 0, width: 0, height: 0 };
let containmentRect = { left: 0, right: 0, top: 0, width: 0, height: 0 };
if (this.el.style.position === 'absolute') {
const containmentEl = this.el.parentElement;
const { left, top } = containmentEl.getBoundingClientRect();
containmentRect = { left, top, width: 0, height: 0 };
const { left, right, top } = containmentEl.getBoundingClientRect();
containmentRect = { left, right, top, width: 0, height: 0 };
}
if (!this.temporalRect) return this;
Object.keys(this.temporalRect).forEach(key => {
const value = this.temporalRect[key];
const scaleReciprocal = key === 'width' || key === 'left' ? this.rectScale.x : key === 'height' || key === 'top' ? this.rectScale.y : 1;
this.el.style[key] = (value - containmentRect[key]) * scaleReciprocal + 'px';
Object.entries(this.temporalRect).forEach(([key, value]) => {
if (this.option.rtl ? key === 'left' : key === 'right')
return;

const scaleReciprocal = key === 'width' || key === 'left' || key === 'right'
? this.rectScale.x
: key === 'height' || key === 'top'
? this.rectScale.y
: 1;
let finalValue: string;
if (key === 'right') {
finalValue = (containmentRect.right - value) * this.rectScale.x + 'px';
} else {
finalValue = (value - containmentRect[key]) * scaleReciprocal + 'px';
}
this.el.style[key] = finalValue;
});
return this;
}
Expand All @@ -328,12 +364,17 @@ export class DDResizable extends DDBaseImplement implements HTMLElementExtendOpt
width: this.originalRect.width,
height: this.originalRect.height + this.scrolled,
left: this.originalRect.left,
right: this.originalRect.right,
top: this.originalRect.top - this.scrolled
};
const rect = this.temporalRect || newRect;

const leftPos = this.option.rtl
? (containmentRect.right - rect.right) * this.rectScale.x
: (rect.left - containmentRect.left) * this.rectScale.x;
return {
position: {
left: (rect.left - containmentRect.left) * this.rectScale.x,
left: leftPos,
top: (rect.top - containmentRect.top) * this.rectScale.y
},
size: {
Expand Down
Loading