diff --git a/gulpfile.js b/gulpfile.js index 385d901..15305ac 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -1,7 +1,7 @@ const path = require('path'); const utils = require('@gravity-ui/gulp-utils'); -const {task, src, dest, series, parallel} = require('gulp'); +const {task, src, dest, series, parallel, watch} = require('gulp'); const sass = require('gulp-dart-sass'); const sourcemaps = require('gulp-sourcemaps'); const rimraf = require('rimraf'); @@ -101,3 +101,21 @@ task( ); task('default', series(['build'])); + +task( + 'rebuild', + parallel([ + 'compile-to-esm', + 'compile-to-cjs', + 'copy-js-declarations', + 'copy-i18n', + 'styles-components', + ]), +); + +task( + 'watch', + series(['build'], () => { + return watch(['src/**/*.{js,jsx,ts,tsx,scss}'], series(['rebuild'])); + }), +); diff --git a/package.json b/package.json index 37603cc..0db370a 100644 --- a/package.json +++ b/package.json @@ -63,6 +63,7 @@ "test": "jest", "clean": "gulp clean", "build": "gulp", + "build:watch": "gulp watch", "start": "storybook dev -p 7120", "typecheck": "tsc --noEmit", "build-storybook": "sb build -c .storybook -o storybook-static", diff --git a/src/components/GridLayout/GridLayout.tsx b/src/components/GridLayout/GridLayout.tsx index 4d3e642..36f1517 100644 --- a/src/components/GridLayout/GridLayout.tsx +++ b/src/components/GridLayout/GridLayout.tsx @@ -54,6 +54,9 @@ export default class GridLayout extends React.PureComponent = {current: null}; + private _groupResetRegistryRef: React.MutableRefObject void>> = { + current: new Map(), + }; private _timeout?: NodeJS.Timeout; private _lastReloadAt?: number; @@ -478,7 +481,9 @@ export default class GridLayout extends React.PureComponent reset()); + } + renderTemporaryPlaceholder(gridLayout: Partial) { const {temporaryLayout, noOverlay, draggableHandleClassName} = this.context; @@ -820,6 +832,7 @@ export default class GridLayout extends React.PureComponent; sharedDragPositionRef: React.MutableRefObject<{offsetX: number; offsetY: number} | null>; + groupResetRegistryRef: React.MutableRefObject void>>; // Drag state scoped to this group only (always false/null for non-source groups) isAnyDragging: boolean; @@ -68,6 +69,7 @@ export const GroupLayout = React.memo(function GroupLayout({ temporaryPlaceholder, dragStateRef, sharedDragPositionRef, + groupResetRegistryRef, isDragCaptured, isAnyDragging, currentDraggingItemId, @@ -161,6 +163,7 @@ export const GroupLayout = React.memo(function GroupLayout({ onDrop={callbacks.onDrop} dragStateRef={dragStateRef} sharedDragPositionRef={sharedDragPositionRef} + groupResetRegistryRef={groupResetRegistryRef} group={group} isDragCaptured={isDragCaptured} isDroppable={Boolean(outerDnDEnable) && editMode} diff --git a/src/components/GridLayout/ReactGridLayout.tsx b/src/components/GridLayout/ReactGridLayout.tsx index d5fcacd..9cf4e8b 100644 --- a/src/components/GridLayout/ReactGridLayout.tsx +++ b/src/components/GridLayout/ReactGridLayout.tsx @@ -30,6 +30,7 @@ type DragOverLayoutProps = ReactGridLayout.ReactGridLayoutProps & { group?: string; onDragTargetRestore?: () => void; transformScaleRef?: React.MutableRefObject; + groupResetRegistryRef?: React.MutableRefObject void>>; }; type DragOverLayoutState = { @@ -73,6 +74,13 @@ class DragOverLayout extends ReactGridLayout { componentDidMount(): void { super.componentDidMount?.(); + if (this.props.group !== undefined) { + this.props.groupResetRegistryRef?.current.set( + this.props.group, + this.resetExternalPlaceholder, + ); + } + // If cursor is moved out of the window there is a bug // which leaves placeholder element in grid, this action needed to reset this state window.addEventListener('dragend', this.resetExternalPlaceholder); @@ -87,6 +95,10 @@ class DragOverLayout extends ReactGridLayout { } componentWillUnmount(): void { + if (this.props.group !== undefined) { + this.props.groupResetRegistryRef?.current.delete(this.props.group); + } + window.removeEventListener('dragend', this.resetExternalPlaceholder); const innerElement = this.getInnerElement(); @@ -221,11 +233,9 @@ class DragOverLayout extends ReactGridLayout { } }; - // Returns true when another group's item is being dragged over this grid. - // Reads from the ref directly — no re-render required to stay current. isSharedDragTarget = (): boolean => { const drag = this.props.dragStateRef?.current; - return Boolean(drag?.isDragging && drag?.sourceGroup !== this.props.group); + return Boolean(drag?.isDragging); }; // Proxy mouse events -> drag methods for dnd between groups @@ -247,7 +257,10 @@ class DragOverLayout extends ReactGridLayout { }; mouseMoveHandler = (e: MouseEvent): void => { - if (this.isSharedDragTarget()) { + if ( + this.isSharedDragTarget() && + this.props.group !== this.props.dragStateRef?.current.sourceGroup + ) { if (!(e as MouseEvent & {nativeEvent?: MouseEvent}).nativeEvent) { // Emulate nativeEvent for firefox const target = this.getInnerElement() || (e.target as HTMLElement); @@ -271,8 +284,8 @@ class DragOverLayout extends ReactGridLayout { const {layout} = this.state; const item = layout.find((l) => l.i === droppingItem?.i); - // reset dragEnter counter on drop - this.resetExternalPlaceholder(); + // reset dragEnter counter on drop for all registered groups + this.props.groupResetRegistryRef?.current.forEach((reset) => reset()); if (item) { this.props.onDrop?.(layout, item, e);