From 565b31a9a6f33960c24d64c13213cb05f061a781 Mon Sep 17 00:00:00 2001 From: Carlos Herrero Date: Wed, 9 Dec 2020 14:20:37 +0100 Subject: [PATCH 1/3] Resizing issue --- src/gridstack-dd.ts | 12 ++++++++++++ src/h5/dd-resizable.ts | 26 +++++++++++++++++++++----- src/utils.ts | 22 ++++++++++++++++++++++ 3 files changed, 55 insertions(+), 5 deletions(-) diff --git a/src/gridstack-dd.ts b/src/gridstack-dd.ts index 9d583924d..9dac317c1 100644 --- a/src/gridstack-dd.ts +++ b/src/gridstack-dd.ts @@ -342,6 +342,7 @@ GridStack.prototype._prepareDragDropByNode = function(node: GridStackNode): Grid // variables used/cashed between the 3 start/move/end methods, in addition to node passed above let cellWidth: number; let cellHeight: number; + let minRow: number; /** called when item starts moving/resizing */ let onStartMoving = (event: Event, ui: DDUIData): void => { @@ -352,6 +353,11 @@ GridStack.prototype._prepareDragDropByNode = function(node: GridStackNode): Grid this._gsEventHandler[event.type](event, target); } + // Saving `minRow` and adding new ones for resizing + minRow = this.opts.minRow; + this.opts.minRow = this.getRow() + 100; + this._updateContainerHeight(); + this.engine.cleanNodes(); this.engine.beginUpdate(node); cellWidth = this.cellWidth(); @@ -422,6 +428,8 @@ GridStack.prototype._prepareDragDropByNode = function(node: GridStackNode): Grid if (node._lastTriedX === x && node._lastTriedY === y) return; } else if (event.type === 'resize') { if (x < 0) return; + // Scrolling page if needed + Utils.updateScrollResize(event as MouseEvent, el, cellHeight); w = Math.round(ui.size.width / cellWidth); h = Math.round(ui.size.height / cellHeight); if (w === node.w && h === node.h) return; @@ -483,6 +491,10 @@ GridStack.prototype._prepareDragDropByNode = function(node: GridStackNode): Grid this.engine.endUpdate(); + // Removing lines rows added on `resizestart` + this.opts.minRow = minRow; + this._updateContainerHeight(); + // if we re-sized a nested grid item, let the children resize as well if (event.type === 'resizestop') { this._resizeNestedGrids(target); diff --git a/src/h5/dd-resizable.ts b/src/h5/dd-resizable.ts index d9b348051..959f75db4 100644 --- a/src/h5/dd-resizable.ts +++ b/src/h5/dd-resizable.ts @@ -8,6 +8,7 @@ import { DDResizableHandle } from './dd-resizable-handle'; import { DDBaseImplement, HTMLElementExtendOpt } from './dd-base-impl'; import { DDUtils } from './dd-utils'; +import { Utils } from '../utils'; import { DDUIData, Rect, Size } from '../types'; // TODO: merge with DDDragOpt @@ -37,6 +38,8 @@ export class DDResizable extends DDBaseImplement implements HTMLElementExtendOpt /** @internal */ private temporalRect: Rect; /** @internal */ + private scrollY: number; + /** @internal */ private startEvent: MouseEvent; /** @internal value saved in the same order as _originStyleProp[] */ private elOriginStyleVal: string[]; @@ -152,6 +155,8 @@ export class DDResizable extends DDBaseImplement implements HTMLElementExtendOpt /** @internal */ private _resizeStart(event: MouseEvent): DDResizable { this.originalRect = this.el.getBoundingClientRect(); + const scrollEl = Utils.getScrollParent(this.el); + this.scrollY = scrollEl === null ? 0 : scrollEl.scrollTop; this.startEvent = event; this._setupHelper(); this._applyChange(); @@ -188,6 +193,7 @@ export class DDResizable extends DDBaseImplement implements HTMLElementExtendOpt delete this.startEvent; delete this.originalRect; delete this.temporalRect; + delete this.scrollY; return this; } @@ -216,12 +222,15 @@ export class DDResizable extends DDBaseImplement implements HTMLElementExtendOpt /** @internal */ private _getChange(event: MouseEvent, dir: string): Rect { const oEvent = this.startEvent; + const scrollEl = Utils.getScrollParent(this.el); + const scrolled = scrollEl === null ? 0 : (scrollEl.scrollTop - this.scrollY); const newRect = { // Note: originalRect is a complex object, not a simple Rect, so copy out. width: this.originalRect.width, - height: this.originalRect.height, + height: this.originalRect.height + scrolled, left: this.originalRect.left, - top: this.originalRect.top + top: this.originalRect.top - scrolled }; + const offsetH = event.clientX - oEvent.clientX; const offsetV = event.clientY - oEvent.clientY; @@ -291,9 +300,16 @@ export class DDResizable extends DDBaseImplement implements HTMLElementExtendOpt /** @internal */ private _ui = (): DDUIData => { - const containmentEl = this.el.parentElement; - const containmentRect = containmentEl.getBoundingClientRect(); - const rect = this.temporalRect || this.originalRect; + const scrollEl = Utils.getScrollParent(this.el); + const scrolled = scrollEl === null ? 0 : (scrollEl.scrollTop - this.scrollY); + const containmentRect = this.el.parentElement.getBoundingClientRect(); + const newRect = { // Note: originalRect is a complex object, not a simple Rect, so copy out. + width: this.originalRect.width, + height: this.originalRect.height + scrolled, + left: this.originalRect.left, + top: this.originalRect.top - scrolled + }; + const rect = this.temporalRect || newRect; return { position: { left: rect.left - containmentRect.left, diff --git a/src/utils.ts b/src/utils.ts index d2f6d81db..8b464364d 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -327,5 +327,27 @@ export class Utils { } } } + + /** @internal */ + static updateScrollResize(event: MouseEvent, el: HTMLElement, distance: number): void { + let scrollEl = this.getScrollParent(el); + if (scrollEl === null) return; + + //const width = scrollEl.clientWidth; + const height = scrollEl.clientHeight; + + const top = event.clientY < distance; + const bottom = event.clientY > height - distance; + + if (top) { + // This also can be done with a timeout to keep scrolling while the mouse is + // in the scrolling zone. (will have smoother behavior) + scrollEl.scrollBy({ behavior: 'smooth', top: event.clientY - distance}); + } + + if (bottom) { + scrollEl.scrollBy({ behavior: 'smooth', top: distance - (height - event.clientY)}); + } + } } From 5ba15dcb417d02ede4839edcebc95a35d8c23cd5 Mon Sep 17 00:00:00 2001 From: Carlos Herrero Date: Mon, 14 Dec 2020 11:52:24 +0100 Subject: [PATCH 2/3] review changes --- src/gridstack-dd.ts | 17 ++++++++++------- src/h5/dd-resizable.ts | 19 ++++++++++--------- src/utils.ts | 15 +++++++++++---- 3 files changed, 31 insertions(+), 20 deletions(-) diff --git a/src/gridstack-dd.ts b/src/gridstack-dd.ts index 7432049c8..56bb8fb6e 100644 --- a/src/gridstack-dd.ts +++ b/src/gridstack-dd.ts @@ -353,10 +353,8 @@ GridStack.prototype._prepareDragDropByNode = function(node: GridStackNode): Grid this._gsEventHandler[event.type](event, target); } - // Saving `minRow` and adding new ones for resizing + // Saving `minRow` minRow = this.opts.minRow; - this.opts.minRow = this.getRow() + 100; - this._updateContainerHeight(); this.engine.cleanNodes(); this.engine.beginUpdate(node); @@ -428,8 +426,17 @@ GridStack.prototype._prepareDragDropByNode = function(node: GridStackNode): Grid if (node._lastTriedX === x && node._lastTriedY === y) return; } else if (event.type === 'resize') { if (x < 0) return; + // Addin an extra row if the item it's at the bottom of the layout + if (node.y+node.h >= this.getRow()-1) { + this.opts.minRow = this.getRow() + 1; + this._updateContainerHeight(); + } + // Scrolling page if needed Utils.updateScrollResize(event as MouseEvent, el, cellHeight); + // Restore minRow + this.opts.minRow = minRow; + w = Math.round(ui.size.width / cellWidth); h = Math.round(ui.size.height / cellHeight); if (w === node.w && h === node.h) return; @@ -491,10 +498,6 @@ GridStack.prototype._prepareDragDropByNode = function(node: GridStackNode): Grid this.engine.endUpdate(); - // Removing lines rows added on `resizestart` - this.opts.minRow = minRow; - this._updateContainerHeight(); - // if we re-sized a nested grid item, let the children resize as well if (event.type === 'resizestop') { this._resizeNestedGrids(target); diff --git a/src/h5/dd-resizable.ts b/src/h5/dd-resizable.ts index 7f274bbbc..8a817e429 100644 --- a/src/h5/dd-resizable.ts +++ b/src/h5/dd-resizable.ts @@ -40,6 +40,8 @@ export class DDResizable extends DDBaseImplement implements HTMLElementExtendOpt /** @internal */ private scrollY: number; /** @internal */ + private scrolled: number; + /** @internal */ private startEvent: MouseEvent; /** @internal value saved in the same order as _originStyleProp[] */ private elOriginStyleVal: string[]; @@ -156,7 +158,7 @@ export class DDResizable extends DDBaseImplement implements HTMLElementExtendOpt private _resizeStart(event: MouseEvent): DDResizable { this.originalRect = this.el.getBoundingClientRect(); const scrollEl = Utils.getScrollParent(this.el); - this.scrollY = scrollEl === null ? 0 : scrollEl.scrollTop; + this.scrollY = scrollEl ? scrollEl.scrollTop : 0; this.startEvent = event; this._setupHelper(); this._applyChange(); @@ -171,6 +173,8 @@ export class DDResizable extends DDBaseImplement implements HTMLElementExtendOpt /** @internal */ private _resizing(event: MouseEvent, dir: string): DDResizable { + const scrollEl = Utils.getScrollParent(this.el); + this.scrolled = scrollEl ? (scrollEl.scrollTop - this.scrollY) : this.scrollY; this.temporalRect = this._getChange(event, dir); this._applyChange(); const ev = DDUtils.initEvent(event, { type: 'resize', target: this.el }); @@ -194,6 +198,7 @@ export class DDResizable extends DDBaseImplement implements HTMLElementExtendOpt delete this.originalRect; delete this.temporalRect; delete this.scrollY; + delete this.scrolled; return this; } @@ -222,13 +227,11 @@ export class DDResizable extends DDBaseImplement implements HTMLElementExtendOpt /** @internal */ private _getChange(event: MouseEvent, dir: string): Rect { const oEvent = this.startEvent; - const scrollEl = Utils.getScrollParent(this.el); - const scrolled = scrollEl === null ? 0 : (scrollEl.scrollTop - this.scrollY); const newRect = { // Note: originalRect is a complex object, not a simple Rect, so copy out. width: this.originalRect.width, - height: this.originalRect.height + scrolled, + height: this.originalRect.height + this.scrolled, left: this.originalRect.left, - top: this.originalRect.top - scrolled + top: this.originalRect.top - this.scrolled }; const offsetH = event.clientX - oEvent.clientX; @@ -300,14 +303,12 @@ export class DDResizable extends DDBaseImplement implements HTMLElementExtendOpt /** @internal */ private _ui = (): DDUIData => { - const scrollEl = Utils.getScrollParent(this.el); - const scrolled = scrollEl === null ? 0 : (scrollEl.scrollTop - this.scrollY); const containmentRect = this.el.parentElement.getBoundingClientRect(); const newRect = { // Note: originalRect is a complex object, not a simple Rect, so copy out. width: this.originalRect.width, - height: this.originalRect.height + scrolled, + height: this.originalRect.height + this.scrolled, left: this.originalRect.left, - top: this.originalRect.top - scrolled + top: this.originalRect.top - this.scrolled }; const rect = this.temporalRect || newRect; return { diff --git a/src/utils.ts b/src/utils.ts index 83922f44c..6d8bab0c4 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -328,10 +328,18 @@ export class Utils { } } - /** @internal */ + /** + * @internal + * + * Function used to scroll the page. + * + * @param event `MouseEvent` that triggers the resize + * @param el `HTMLElement` that's being resized + * @param distance Distance to scroll + */ static updateScrollResize(event: MouseEvent, el: HTMLElement, distance: number): void { let scrollEl = this.getScrollParent(el); - if (scrollEl === null) return; + if (!scrollEl ) return; //const width = scrollEl.clientWidth; const height = scrollEl.clientHeight; @@ -343,9 +351,8 @@ export class Utils { // This also can be done with a timeout to keep scrolling while the mouse is // in the scrolling zone. (will have smoother behavior) scrollEl.scrollBy({ behavior: 'smooth', top: event.clientY - distance}); - } - if (bottom) { + } else if (bottom) { scrollEl.scrollBy({ behavior: 'smooth', top: distance - (height - event.clientY)}); } } From 84cec8da5fc0cc7675c96041baad8a951a21983c Mon Sep 17 00:00:00 2001 From: Carlos Herrero Date: Mon, 28 Dec 2020 18:51:19 +0100 Subject: [PATCH 3/3] Select scrollable parent --- src/gridstack-dd.ts | 13 ------------- src/h5/dd-resizable.ts | 12 +++++++----- src/utils.ts | 19 ++++++++----------- 3 files changed, 15 insertions(+), 29 deletions(-) diff --git a/src/gridstack-dd.ts b/src/gridstack-dd.ts index 56bb8fb6e..1137bab39 100644 --- a/src/gridstack-dd.ts +++ b/src/gridstack-dd.ts @@ -342,7 +342,6 @@ GridStack.prototype._prepareDragDropByNode = function(node: GridStackNode): Grid // variables used/cashed between the 3 start/move/end methods, in addition to node passed above let cellWidth: number; let cellHeight: number; - let minRow: number; /** called when item starts moving/resizing */ let onStartMoving = (event: Event, ui: DDUIData): void => { @@ -353,9 +352,6 @@ GridStack.prototype._prepareDragDropByNode = function(node: GridStackNode): Grid this._gsEventHandler[event.type](event, target); } - // Saving `minRow` - minRow = this.opts.minRow; - this.engine.cleanNodes(); this.engine.beginUpdate(node); cellWidth = this.cellWidth(); @@ -426,17 +422,8 @@ GridStack.prototype._prepareDragDropByNode = function(node: GridStackNode): Grid if (node._lastTriedX === x && node._lastTriedY === y) return; } else if (event.type === 'resize') { if (x < 0) return; - // Addin an extra row if the item it's at the bottom of the layout - if (node.y+node.h >= this.getRow()-1) { - this.opts.minRow = this.getRow() + 1; - this._updateContainerHeight(); - } - // Scrolling page if needed Utils.updateScrollResize(event as MouseEvent, el, cellHeight); - // Restore minRow - this.opts.minRow = minRow; - w = Math.round(ui.size.width / cellWidth); h = Math.round(ui.size.height / cellHeight); if (w === node.w && h === node.h) return; diff --git a/src/h5/dd-resizable.ts b/src/h5/dd-resizable.ts index 8a817e429..d92accabb 100644 --- a/src/h5/dd-resizable.ts +++ b/src/h5/dd-resizable.ts @@ -42,6 +42,8 @@ export class DDResizable extends DDBaseImplement implements HTMLElementExtendOpt /** @internal */ private scrolled: number; /** @internal */ + private scrollEl: HTMLElement; + /** @internal */ private startEvent: MouseEvent; /** @internal value saved in the same order as _originStyleProp[] */ private elOriginStyleVal: string[]; @@ -157,8 +159,8 @@ export class DDResizable extends DDBaseImplement implements HTMLElementExtendOpt /** @internal */ private _resizeStart(event: MouseEvent): DDResizable { this.originalRect = this.el.getBoundingClientRect(); - const scrollEl = Utils.getScrollParent(this.el); - this.scrollY = scrollEl ? scrollEl.scrollTop : 0; + this.scrollEl = Utils.getScrollParent(this.el); + this.scrollY = this.scrollEl.scrollTop; this.startEvent = event; this._setupHelper(); this._applyChange(); @@ -173,8 +175,7 @@ export class DDResizable extends DDBaseImplement implements HTMLElementExtendOpt /** @internal */ private _resizing(event: MouseEvent, dir: string): DDResizable { - const scrollEl = Utils.getScrollParent(this.el); - this.scrolled = scrollEl ? (scrollEl.scrollTop - this.scrollY) : this.scrollY; + this.scrolled = this.scrollEl.scrollTop - this.scrollY; this.temporalRect = this._getChange(event, dir); this._applyChange(); const ev = DDUtils.initEvent(event, { type: 'resize', target: this.el }); @@ -303,7 +304,8 @@ export class DDResizable extends DDBaseImplement implements HTMLElementExtendOpt /** @internal */ private _ui = (): DDUIData => { - const containmentRect = this.el.parentElement.getBoundingClientRect(); + const containmentEl = this.el.parentElement; + const containmentRect = containmentEl.getBoundingClientRect(); 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, diff --git a/src/utils.ts b/src/utils.ts index 6d8bab0c4..9a5087e83 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -280,15 +280,15 @@ export class Utils { /** @internal */ static getScrollParent(el: HTMLElement): HTMLElement { - let returnEl; - if (el === null) { - returnEl = null; - } else if (el.scrollHeight > el.clientHeight) { - returnEl = el; + if (el === null) return document.documentElement; + const style = getComputedStyle(el); + const overflowRegex = /(auto|scroll)/; + + if (overflowRegex.test(style.overflow + style.overflowY)) { + return el; } else { - returnEl = this.getScrollParent(el.parentElement); + return this.getScrollParent(el.parentElement); } - return returnEl; } /** @internal */ @@ -338,10 +338,7 @@ export class Utils { * @param distance Distance to scroll */ static updateScrollResize(event: MouseEvent, el: HTMLElement, distance: number): void { - let scrollEl = this.getScrollParent(el); - if (!scrollEl ) return; - - //const width = scrollEl.clientWidth; + const scrollEl = this.getScrollParent(el); const height = scrollEl.clientHeight; const top = event.clientY < distance;