diff --git a/src/dd-draggable.ts b/src/dd-draggable.ts index 2de0ff501..96455c5a7 100644 --- a/src/dd-draggable.ts +++ b/src/dd-draggable.ts @@ -163,7 +163,7 @@ export class DDDraggable extends DDBaseImplement implements HTMLElementExtendOpt } else if (Math.abs(e.x - s.x) + Math.abs(e.y - s.y) > 3) { /** * don't start unless we've moved at least 3 pixels - */ + */ this.dragging = true; DDManager.dragElement = this; // if we're dragging an actual grid item, set the current drop as the grid (to detect enter/leave) @@ -179,7 +179,6 @@ export class DDDraggable extends DDBaseImplement implements HTMLElementExtendOpt const ev = Utils.initEvent(e, { target: this.el, type: 'dragstart' }); this._setupHelperStyle(e); - this.helper.classList.add('ui-draggable-dragging'); if (this.option.start) { this.option.start(ev, this.ui()); } @@ -205,7 +204,6 @@ export class DDDraggable extends DDBaseImplement implements HTMLElementExtendOpt delete DDManager.dropElement; } - this.helper.classList.remove('ui-draggable-dragging'); this.helperContainment.style.position = this.parentOriginStylePosition || null; if (this.helper === this.el) { this._removeHelperStyle(); @@ -250,10 +248,11 @@ export class DDDraggable extends DDBaseImplement implements HTMLElementExtendOpt /** @internal set the fix position of the dragged item */ protected _setupHelperStyle(e: DragEvent): DDDraggable { + this.helper.classList.add('ui-draggable-dragging'); // TODO: set all at once with style.cssText += ... ? https://stackoverflow.com/questions/3968593 const style = this.helper.style; style.pointerEvents = 'none'; // needed for over items to get enter/leave - // style.cursor = 'move'; // TODO: can't set with pointerEvents=none ! + // style.cursor = 'move'; // TODO: can't set with pointerEvents=none ! (done in CSS as well) style['min-width'] = 0; // since we no longer relative to our parent and we don't resize anyway (normally 100/#column %) style.width = this.dragOffset.width + 'px'; style.height = this.dragOffset.height + 'px'; @@ -271,6 +270,7 @@ export class DDDraggable extends DDBaseImplement implements HTMLElementExtendOpt /** @internal restore back the original style before dragging */ protected _removeHelperStyle(): DDDraggable { + this.helper.classList.remove('ui-draggable-dragging'); let node = (this.helper as GridItemHTMLElement)?.gridstackNode; // don't bother restoring styles if we're gonna remove anyway... if (this.dragElementOriginStyle && (!node || !node._isAboutToRemove)) { diff --git a/src/dd-droppable.ts b/src/dd-droppable.ts index d02b6a5d2..637607248 100644 --- a/src/dd-droppable.ts +++ b/src/dd-droppable.ts @@ -9,6 +9,7 @@ import { DDBaseImplement, HTMLElementExtendOpt } from './dd-base-impl'; import { Utils } from './utils'; import { DDElementHost } from './dd-element'; import { isTouch, pointerenter, pointerleave } from './dd-touch'; +import { GridHTMLElement } from './gridstack'; export interface DDDroppableOpt { accept?: string | ((el: HTMLElement) => boolean); @@ -86,34 +87,34 @@ export class DDDroppable extends DDBaseImplement implements HTMLElementExtendOpt /** @internal called when the cursor enters our area - prepare for a possible drop and track leaving */ protected _mouseEnter(e: MouseEvent): void { // console.log(`${count++} Enter ${this.el.id || (this.el as GridHTMLElement).gridstack.opts.id}`); // TEST - if (!DDManager.dragElement /* || DDManager.dropElement === this*/) return; + if (!DDManager.dragElement) return; if (!this._canDrop()) return; e.preventDefault(); e.stopPropagation(); + // make sure when we enter this, that the last one gets a leave FIRST to correctly cleanup as we don't always do + if (DDManager.dropElement && DDManager.dropElement !== this) { + DDManager.dropElement._mouseLeave(e as DragEvent); + } + DDManager.dropElement = this; + const ev = Utils.initEvent(e, { target: this.el, type: 'dropover' }); if (this.option.over) { this.option.over(ev, this._ui(DDManager.dragElement)) } this.triggerEvent('dropover', ev); this.el.classList.add('ui-droppable-over'); - - // make sure when we enter this, that the last one gets a leave to correctly cleanup as we don't always do - if (DDManager.dropElement && DDManager.dropElement !== this) { - DDManager.dropElement._mouseLeave(e as DragEvent); - } - DDManager.dropElement = this; // console.log('tracking'); // TEST } /** @internal called when the item is leaving our area, stop tracking if we had moving item */ - protected _mouseLeave(event: DragEvent): void { + protected _mouseLeave(e: MouseEvent): void { // console.log(`${count++} Leave ${this.el.id || (this.el as GridHTMLElement).gridstack.opts.id}`); // TEST if (!DDManager.dragElement || DDManager.dropElement !== this) return; - event.preventDefault(); - event.stopPropagation(); + e.preventDefault(); + e.stopPropagation(); - const ev = Utils.initEvent(event, { target: this.el, type: 'dropout' }); + const ev = Utils.initEvent(e, { target: this.el, type: 'dropout' }); if (this.option.out) { this.option.out(ev, this._ui(DDManager.dragElement)) } @@ -131,7 +132,7 @@ export class DDDroppable extends DDBaseImplement implements HTMLElementExtendOpt parent = parent.parentElement; } if (parentDrop) { - parentDrop._mouseEnter(event); + parentDrop._mouseEnter(e); } } } diff --git a/src/dd-resizable-handle.ts b/src/dd-resizable-handle.ts index 4153014af..0bfe64fec 100644 --- a/src/dd-resizable-handle.ts +++ b/src/dd-resizable-handle.ts @@ -72,42 +72,48 @@ export class DDResizableHandle { } /** @internal called on mouse down on us: capture move on the entire document (mouse might not stay on us) until we release the mouse */ - protected _mouseDown(e: MouseEvent): void { - e.preventDefault(); + protected _mouseDown(e: MouseEvent) { this.mouseDownEvent = e; document.addEventListener('mousemove', this._mouseMove, true); // capture, not bubble - document.addEventListener('mouseup', this._mouseUp); + document.addEventListener('mouseup', this._mouseUp, true); if (isTouch) { this.el.addEventListener('touchmove', touchmove); this.el.addEventListener('touchend', touchend); } + e.stopPropagation(); + e.preventDefault(); } /** @internal */ - protected _mouseMove(e: MouseEvent): void { + protected _mouseMove(e: MouseEvent) { let s = this.mouseDownEvent; - // don't start unless we've moved at least 3 pixels - if (!this.moving && Math.abs(e.x - s.x) + Math.abs(e.y - s.y) > 2) { + if (this.moving) { + this._triggerEvent('move', e); + } else if (Math.abs(e.x - s.x) + Math.abs(e.y - s.y) > 2) { + // don't start unless we've moved at least 3 pixels this.moving = true; this._triggerEvent('start', this.mouseDownEvent); - } else if (this.moving) { this._triggerEvent('move', e); } + e.stopPropagation(); + e.preventDefault(); } /** @internal */ - protected _mouseUp(e: MouseEvent): void { + protected _mouseUp(e: MouseEvent) { if (this.moving) { - this._triggerEvent('resizestop', e); + this._triggerEvent('stop', e); } document.removeEventListener('mousemove', this._mouseMove, true); - document.removeEventListener('mouseup', this._mouseUp); + document.removeEventListener('mouseup', this._mouseUp, true); if (isTouch) { this.el.removeEventListener('touchmove', touchmove); this.el.removeEventListener('touchend', touchend); } delete this.moving; delete this.mouseDownEvent; + e.stopPropagation(); + e.preventDefault(); } /** @internal */ diff --git a/src/dd-resizable.ts b/src/dd-resizable.ts index 19143828a..c341d0433 100644 --- a/src/dd-resizable.ts +++ b/src/dd-resizable.ts @@ -52,8 +52,11 @@ export class DDResizable extends DDBaseImplement implements HTMLElementExtendOpt super(); this.el = el; this.option = opts; + // create var event binding so we can easily remove and still look like TS methods (unlike anonymous functions) + this._mouseOver = this._mouseOver.bind(this); + this._mouseOut = this._mouseOut.bind(this); this.enable(); - this._setupAutoHide(); + this._setupAutoHide(this.option.autoHide); this._setupHandlers(); } @@ -79,10 +82,7 @@ export class DDResizable extends DDBaseImplement implements HTMLElementExtendOpt public destroy(): void { this._removeHandlers(); - if (this.option.autoHide) { - this.el.removeEventListener('mouseover', this._showHandlers); - this.el.removeEventListener('mouseout', this._hideHandlers); - } + this._setupAutoHide(false); this.el.classList.remove('ui-resizable'); delete this.el; super.destroy(); @@ -97,34 +97,36 @@ export class DDResizable extends DDBaseImplement implements HTMLElementExtendOpt this._setupHandlers(); } if (updateAutoHide) { - this._setupAutoHide(); + this._setupAutoHide(this.option.autoHide); } return this; } - /** @internal */ - protected _setupAutoHide(): DDResizable { - if (this.option.autoHide) { + /** @internal turns auto hide on/off */ + protected _setupAutoHide(auto: boolean): DDResizable { + if (auto) { this.el.classList.add('ui-resizable-autohide'); // use mouseover/mouseout instead of mouseenter/mouseleave to get better performance; - this.el.addEventListener('mouseover', this._showHandlers); - this.el.addEventListener('mouseout', this._hideHandlers); + this.el.addEventListener('mouseover', this._mouseOver); + this.el.addEventListener('mouseout', this._mouseOut); } else { this.el.classList.remove('ui-resizable-autohide'); - this.el.removeEventListener('mouseover', this._showHandlers); - this.el.removeEventListener('mouseout', this._hideHandlers); + this.el.removeEventListener('mouseover', this._mouseOver); + this.el.removeEventListener('mouseout', this._mouseOut); } return this; } /** @internal */ - protected _showHandlers = () => { + protected _mouseOver(e: Event) { this.el.classList.remove('ui-resizable-autohide'); + e.stopPropagation(); } /** @internal */ - protected _hideHandlers = () => { + protected _mouseOut(e: Event) { this.el.classList.add('ui-resizable-autohide'); + e.stopPropagation(); } /** @internal */ diff --git a/src/gridstack.scss b/src/gridstack.scss index 1c5bdf0db..ab0a133ca 100644 --- a/src/gridstack.scss +++ b/src/gridstack.scss @@ -133,6 +133,7 @@ $animation_speed: .3s !default; } .ui-draggable-dragging { will-change: left, top; + cursor: move; } .ui-resizable-resizing { will-change: width, height;