diff --git a/src/gridstack-dd.ts b/src/gridstack-dd.ts index fafc6bfee..f51a2713c 100644 --- a/src/gridstack-dd.ts +++ b/src/gridstack-dd.ts @@ -422,6 +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; + // 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; diff --git a/src/h5/dd-resizable.ts b/src/h5/dd-resizable.ts index d97f697d7..026b08c15 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,12 @@ export class DDResizable extends DDBaseImplement implements HTMLElementExtendOpt /** @internal */ private temporalRect: Rect; /** @internal */ + private scrollY: number; + /** @internal */ + private scrolled: number; + /** @internal */ + private scrollEl: HTMLElement; + /** @internal */ private startEvent: MouseEvent; /** @internal value saved in the same order as _originStyleProp[] */ private elOriginStyleVal: string[]; @@ -152,6 +159,8 @@ export class DDResizable extends DDBaseImplement implements HTMLElementExtendOpt /** @internal */ private _resizeStart(event: MouseEvent): DDResizable { this.originalRect = this.el.getBoundingClientRect(); + this.scrollEl = Utils.getScrollParent(this.el); + this.scrollY = this.scrollEl.scrollTop; this.startEvent = event; this._setupHelper(); this._applyChange(); @@ -166,6 +175,7 @@ export class DDResizable extends DDBaseImplement implements HTMLElementExtendOpt /** @internal */ private _resizing(event: MouseEvent, dir: string): DDResizable { + 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 }); @@ -188,6 +198,8 @@ export class DDResizable extends DDBaseImplement implements HTMLElementExtendOpt delete this.startEvent; delete this.originalRect; delete this.temporalRect; + delete this.scrollY; + delete this.scrolled; return this; } @@ -218,10 +230,11 @@ export class DDResizable extends DDBaseImplement implements HTMLElementExtendOpt 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, + height: this.originalRect.height + this.scrolled, left: this.originalRect.left, - top: this.originalRect.top + top: this.originalRect.top - this.scrolled }; + const offsetH = event.clientX - oEvent.clientX; const offsetV = event.clientY - oEvent.clientY; @@ -293,7 +306,13 @@ export class DDResizable extends DDBaseImplement implements HTMLElementExtendOpt private _ui = (): DDUIData => { const containmentEl = this.el.parentElement; const containmentRect = containmentEl.getBoundingClientRect(); - const rect = this.temporalRect || this.originalRect; + 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 + }; + const rect = this.temporalRect || newRect; return { position: { left: rect.left - containmentRect.left, diff --git a/src/utils.ts b/src/utils.ts index 952336762..21a488c33 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 */ @@ -327,5 +327,31 @@ export class Utils { } } } + + /** + * @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 { + const scrollEl = this.getScrollParent(el); + 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}); + + } else if (bottom) { + scrollEl.scrollBy({ behavior: 'smooth', top: distance - (height - event.clientY)}); + } + } }