From 0dbeebde21e903f22973b7e6e1460902e070fa5a Mon Sep 17 00:00:00 2001 From: Serge Pavlyuk Date: Mon, 7 Oct 2024 16:05:01 +0300 Subject: [PATCH 1/3] fix: enable pointer-events: none only when widget is dragget out from group --- src/components/GridItem/GridItem.js | 3 +++ src/components/GridItem/GridItem.scss | 5 +++- src/components/GridLayout/GridLayout.js | 35 +++++++++++++++++++++++++ 3 files changed, 42 insertions(+), 1 deletion(-) diff --git a/src/components/GridItem/GridItem.js b/src/components/GridItem/GridItem.js index 2ae028b..4f0191f 100644 --- a/src/components/GridItem/GridItem.js +++ b/src/components/GridItem/GridItem.js @@ -51,6 +51,7 @@ class GridItem extends React.PureComponent { id: PropTypes.string, item: PropTypes.object, isDragging: PropTypes.bool, + draggedOut: PropTypes.bool, layout: PropTypes.array, forwardedRef: PropTypes.any, @@ -161,6 +162,7 @@ class GridItem extends React.PureComponent { children, className, isDragging, + draggedOut, noOverlay, focusable, withCustomHandle, @@ -188,6 +190,7 @@ class GridItem extends React.PureComponent { className={b( { 'is-dragging': isDragging, + 'is-dragged-out': draggedOut, 'is-focused': this.state.isFocused, 'with-custom-handle': withCustomHandle, }, diff --git a/src/components/GridItem/GridItem.scss b/src/components/GridItem/GridItem.scss index 5026a8c..6281464 100644 --- a/src/components/GridItem/GridItem.scss +++ b/src/components/GridItem/GridItem.scss @@ -42,6 +42,10 @@ pointer-events: none; } } + + &_is-dragged-out { + pointer-events: none; + } } .react-grid-layout { @@ -67,7 +71,6 @@ z-index: 3; will-change: transform; // needs for drag n drop between multiple groups - pointer-events: none; } .react-grid-item.dashkit-grid-item_is-focused { diff --git a/src/components/GridLayout/GridLayout.js b/src/components/GridLayout/GridLayout.js index 2b9eaef..f46b99a 100644 --- a/src/components/GridLayout/GridLayout.js +++ b/src/components/GridLayout/GridLayout.js @@ -21,6 +21,7 @@ export default class GridLayout extends React.PureComponent { isPageHidden: false, currentDraggingElement: null, draggedOverGroup: null, + draggedOut: false, }; } @@ -262,6 +263,32 @@ export default class GridLayout extends React.PureComponent { this.setState({currentDraggingElement, draggedOverGroup: group}); } + _initDragCoordinatesWatcher(element) { + if (!this.state.draggedOut) { + this._parendDragNode = element.parentElement; + this.setState({draggedOut: false}); + } + } + + _updateDragCoordinates(e) { + const parent = this._parendDragNode; + const parentRect = parent.getBoundingClientRect(); + + if ( + e.clientX < parentRect.left || + e.clientX > parentRect.right || + e.clientY < parentRect.top || + e.clientY > parentRect.bottom + ) { + this.setState({draggedOut: true}); + } + } + + _resetDragWatcher() { + this._parendDragNode = null; + this.setState({draggedOut: false}); + } + _onDragStart(group, _newLayout, layoutItem, _newItem, _placeholder, e, element) { this.context.onDragStart?.call( this, @@ -276,6 +303,8 @@ export default class GridLayout extends React.PureComponent { ), ); + this._initDragCoordinatesWatcher(element); + if (this.context.dragOverPlugin) { this.setState({isDragging: true}); } else { @@ -285,6 +314,8 @@ export default class GridLayout extends React.PureComponent { } _onDrag(group, layout, oldItem, newItem, placeholder, e, element) { + this._updateDragCoordinates(e); + this.context.onDrag?.call( this, this.prepareDefaultArguments(group, layout, oldItem, newItem, placeholder, e, element), @@ -292,6 +323,8 @@ export default class GridLayout extends React.PureComponent { } _onDragStop(group, layout, oldItem, newItem, placeholder, e, element) { + this._resetDragWatcher(); + this._onStop(group, layout); this.context.onDragStop?.call( @@ -332,6 +365,7 @@ export default class GridLayout extends React.PureComponent { return; } + console.log('_onTargetRestore'); const {currentDraggingElement} = this.state; if (currentDraggingElement) { @@ -570,6 +604,7 @@ export default class GridLayout extends React.PureComponent { layout={layout} adjustWidgetLayout={this.adjustWidgetLayout} isDragging={this.state.isDragging} + draggedOut={this.state.draggedOut} noOverlay={noOverlay} focusable={focusable} withCustomHandle={Boolean(draggableHandleClassName)} From 30894e30137c15605942adcc0a4ec58114ee91ce Mon Sep 17 00:00:00 2001 From: Serge Pavlyuk Date: Wed, 9 Oct 2024 15:47:19 +0300 Subject: [PATCH 2/3] fix: force cursor capture when group changed --- src/components/GridItem/GridItem.scss | 2 +- src/components/GridLayout/GridLayout.js | 46 +++++++++++++++++++++---- 2 files changed, 40 insertions(+), 8 deletions(-) diff --git a/src/components/GridItem/GridItem.scss b/src/components/GridItem/GridItem.scss index 6281464..b3e0217 100644 --- a/src/components/GridItem/GridItem.scss +++ b/src/components/GridItem/GridItem.scss @@ -43,6 +43,7 @@ } } + // needs for drag n drop between multiple groups &_is-dragged-out { pointer-events: none; } @@ -70,7 +71,6 @@ transition: none; z-index: 3; will-change: transform; - // needs for drag n drop between multiple groups } .react-grid-item.dashkit-grid-item_is-focused { diff --git a/src/components/GridLayout/GridLayout.js b/src/components/GridLayout/GridLayout.js index f46b99a..075cc60 100644 --- a/src/components/GridLayout/GridLayout.js +++ b/src/components/GridLayout/GridLayout.js @@ -264,24 +264,56 @@ export default class GridLayout extends React.PureComponent { } _initDragCoordinatesWatcher(element) { - if (!this.state.draggedOut) { + if (!this._parendDragNode) { this._parendDragNode = element.parentElement; this.setState({draggedOut: false}); } } + _forceCursorCapture(parentElement, position) { + const div = document.createElement('div'); + const blockSize = 50; + const offset = blockSize / 2; + + div.style.position = 'absolute'; + div.style.display = 'block'; + div.style.zIndex = 10; + + div.style.width = `${blockSize}px`; + div.style.height = `${blockSize}px`; + div.style.top = `${position.top - offset}px`; + div.style.left = `${position.left - offset}px`; + + parentElement.appendChild(div); + setTimeout(() => { + div.remove(); + }, 100); + } + _updateDragCoordinates(e) { const parent = this._parendDragNode; const parentRect = parent.getBoundingClientRect(); + const {clientX, clientY} = e; + let draggedOut = this.state.draggedOut; if ( - e.clientX < parentRect.left || - e.clientX > parentRect.right || - e.clientY < parentRect.top || - e.clientY > parentRect.bottom + clientX < parentRect.left || + clientX > parentRect.right || + clientY < parentRect.top || + clientY > parentRect.bottom ) { - this.setState({draggedOut: true}); + draggedOut = true; + } else { + draggedOut = false; + } + + if (draggedOut !== this.state.draggedOut) { + this._forceCursorCapture(parent, { + top: clientY - parentRect.top, + left: clientX - parentRect.left, + }); } + this.setState({draggedOut}); } _resetDragWatcher() { @@ -314,7 +346,7 @@ export default class GridLayout extends React.PureComponent { } _onDrag(group, layout, oldItem, newItem, placeholder, e, element) { - this._updateDragCoordinates(e); + this._updateDragCoordinates(e, element); this.context.onDrag?.call( this, From 54b32bb5854992e20139428d9ebb1228905931e0 Mon Sep 17 00:00:00 2001 From: Serge Pavlyuk Date: Thu, 10 Oct 2024 19:04:46 +0300 Subject: [PATCH 3/3] fix: added top and left limits for focus block and used class for base css properties --- src/components/GridItem/GridItem.js | 6 +-- src/components/GridItem/GridItem.scss | 9 ++++ src/components/GridLayout/GridLayout.js | 58 +++++++++++++++---------- 3 files changed, 48 insertions(+), 25 deletions(-) diff --git a/src/components/GridItem/GridItem.js b/src/components/GridItem/GridItem.js index 4f0191f..ae7bf24 100644 --- a/src/components/GridItem/GridItem.js +++ b/src/components/GridItem/GridItem.js @@ -51,7 +51,7 @@ class GridItem extends React.PureComponent { id: PropTypes.string, item: PropTypes.object, isDragging: PropTypes.bool, - draggedOut: PropTypes.bool, + isDraggedOut: PropTypes.bool, layout: PropTypes.array, forwardedRef: PropTypes.any, @@ -162,7 +162,7 @@ class GridItem extends React.PureComponent { children, className, isDragging, - draggedOut, + isDraggedOut, noOverlay, focusable, withCustomHandle, @@ -190,7 +190,7 @@ class GridItem extends React.PureComponent { className={b( { 'is-dragging': isDragging, - 'is-dragged-out': draggedOut, + 'is-dragged-out': isDraggedOut, 'is-focused': this.state.isFocused, 'with-custom-handle': withCustomHandle, }, diff --git a/src/components/GridItem/GridItem.scss b/src/components/GridItem/GridItem.scss index b3e0217..71898e0 100644 --- a/src/components/GridItem/GridItem.scss +++ b/src/components/GridItem/GridItem.scss @@ -88,6 +88,15 @@ user-select: none; } +.react-grid-focus-capture { + position: absolute; + display: block; + // Should be the highest between all grid item states + z-index: 6; + max-width: 100%; + max-height: 100%; +} + .react-grid-item .react-resizable-handle { position: absolute; width: 20px; diff --git a/src/components/GridLayout/GridLayout.js b/src/components/GridLayout/GridLayout.js index 075cc60..57b8d3c 100644 --- a/src/components/GridLayout/GridLayout.js +++ b/src/components/GridLayout/GridLayout.js @@ -240,7 +240,7 @@ export default class GridLayout extends React.PureComponent { }; } - updateeDraggingElementState(group, layoutItem, e) { + updateDraggingElementState(group, layoutItem, e) { let currentDraggingElement = this.state.currentDraggingElement; if (!currentDraggingElement) { @@ -270,23 +270,28 @@ export default class GridLayout extends React.PureComponent { } } - _forceCursorCapture(parentElement, position) { - const div = document.createElement('div'); - const blockSize = 50; + // When element is going back and prointer-event: none is removed mouse enter event is not fired + // So to trigger it we are forcing this event by adding transparent block under the mouse + _forceCursorCapture(parentElement, position, parentRect) { + const block = document.createElement('div'); + block.classList.add('react-grid-focus-capture'); + + const blockSize = 44; const offset = blockSize / 2; - div.style.position = 'absolute'; - div.style.display = 'block'; - div.style.zIndex = 10; + // Keeping elemnt inside current grid + const top = Math.min(Math.max(position.top - offset, 0), parentRect.height - blockSize); + const left = Math.min(Math.max(position.left - offset, 0), parentRect.width - blockSize); + + block.style.width = `${blockSize}px`; + block.style.height = `${blockSize}px`; + block.style.top = `${top}px`; + block.style.left = `${left}px`; - div.style.width = `${blockSize}px`; - div.style.height = `${blockSize}px`; - div.style.top = `${position.top - offset}px`; - div.style.left = `${position.left - offset}px`; + parentElement.appendChild(block); - parentElement.appendChild(div); setTimeout(() => { - div.remove(); + block.remove(); }, 100); } @@ -308,12 +313,19 @@ export default class GridLayout extends React.PureComponent { } if (draggedOut !== this.state.draggedOut) { - this._forceCursorCapture(parent, { - top: clientY - parentRect.top, - left: clientX - parentRect.left, - }); + this.setState({draggedOut}); + + if (!draggedOut) { + this._forceCursorCapture( + parent, + { + top: clientY - parentRect.top, + left: clientX - parentRect.left, + }, + parentRect, + ); + } } - this.setState({draggedOut}); } _resetDragWatcher() { @@ -340,13 +352,13 @@ export default class GridLayout extends React.PureComponent { if (this.context.dragOverPlugin) { this.setState({isDragging: true}); } else { - this.updateeDraggingElementState(group, layoutItem, e); + this.updateDraggingElementState(group, layoutItem, e); this.setState({isDragging: true}); } } _onDrag(group, layout, oldItem, newItem, placeholder, e, element) { - this._updateDragCoordinates(e, element); + this._updateDragCoordinates(e); this.context.onDrag?.call( this, @@ -397,7 +409,6 @@ export default class GridLayout extends React.PureComponent { return; } - console.log('_onTargetRestore'); const {currentDraggingElement} = this.state; if (currentDraggingElement) { @@ -627,6 +638,9 @@ export default class GridLayout extends React.PureComponent { : null)} > {renderItems.map((item, i) => { + const isCurrentItem = currentDraggingElement?.item.id === item.id; + const isDraggedOut = isCurrentItem && this.state.draggedOut; + return (