@vizzly/dnd-backend
Advanced tools
Comparing version 15.0.1-a to 15.0.1-b
{ | ||
"name": "@vizzly/dnd-backend", | ||
"type": "module", | ||
"version": "15.0.1-a", | ||
"version": "15.0.1-b", | ||
"description": "HTML5 backend for React DnD", | ||
@@ -6,0 +6,0 @@ "main": "./dist/bundle.cjs.js", |
import type { | ||
Backend, | ||
DragDropActions, | ||
DragDropManager, | ||
DragDropMonitor, | ||
HandlerRegistry, | ||
Identifier, | ||
Unsubscribe, | ||
XYCoord, | ||
} from 'dnd-core' | ||
Backend, | ||
DragDropActions, | ||
DragDropManager, | ||
DragDropMonitor, | ||
HandlerRegistry, | ||
Identifier, | ||
Unsubscribe, | ||
XYCoord, | ||
} from "dnd-core"; | ||
import { EnterLeaveCounter } from './EnterLeaveCounter.js' | ||
import { EnterLeaveCounter } from "./EnterLeaveCounter.js"; | ||
import { | ||
createNativeDragSource, | ||
matchNativeItemType, | ||
} from './NativeDragSources/index.js' | ||
import type { NativeDragSource } from './NativeDragSources/NativeDragSource.js' | ||
import * as NativeTypes from './NativeTypes.js' | ||
createNativeDragSource, | ||
matchNativeItemType, | ||
} from "./NativeDragSources/index.js"; | ||
import type { NativeDragSource } from "./NativeDragSources/NativeDragSource.js"; | ||
import * as NativeTypes from "./NativeTypes.js"; | ||
import { | ||
getDragPreviewOffset, | ||
getEventClientOffset, | ||
getNodeClientOffset, | ||
} from './OffsetUtils.js' | ||
import { OptionsReader } from './OptionsReader.js' | ||
import type { HTML5BackendContext, HTML5BackendOptions } from './types.js' | ||
getDragPreviewOffset, | ||
getEventClientOffset, | ||
getNodeClientOffset, | ||
} from "./OffsetUtils.js"; | ||
import { OptionsReader } from "./OptionsReader.js"; | ||
import type { HTML5BackendContext, HTML5BackendOptions } from "./types.js"; | ||
type RootNode = Node & { __isReactDndBackendSetUp: boolean | undefined } | ||
type RootNode = Node & { __isReactDndBackendSetUp: boolean | undefined }; | ||
export class HTML5BackendImpl implements Backend { | ||
private options: OptionsReader | ||
private options: OptionsReader; | ||
// React-Dnd Components | ||
private actions: DragDropActions | ||
private monitor: DragDropMonitor | ||
private registry: HandlerRegistry | ||
// React-Dnd Components | ||
private actions: DragDropActions; | ||
private monitor: DragDropMonitor; | ||
private registry: HandlerRegistry; | ||
// Internal State | ||
private enterLeaveCounter: EnterLeaveCounter | ||
// Internal State | ||
private enterLeaveCounter: EnterLeaveCounter; | ||
private sourcePreviewNodes: Map<string, Element> = new Map() | ||
private sourcePreviewNodeOptions: Map<string, any> = new Map() | ||
private sourceNodes: Map<string, Element> = new Map() | ||
private sourceNodeOptions: Map<string, any> = new Map() | ||
private sourcePreviewNodes: Map<string, Element> = new Map(); | ||
private sourcePreviewNodeOptions: Map<string, any> = new Map(); | ||
private sourceNodes: Map<string, Element> = new Map(); | ||
private sourceNodeOptions: Map<string, any> = new Map(); | ||
private dragStartSourceIds: string[] | null = null | ||
private dropTargetIds: string[] = [] | ||
private dragEnterTargetIds: string[] = [] | ||
private currentNativeSource: NativeDragSource | null = null | ||
private currentNativeHandle: Identifier | null = null | ||
private currentDragSourceNode: Element | null = null | ||
private altKeyPressed = false | ||
private mouseMoveTimeoutTimer: number | null = null | ||
private asyncEndDragFrameId: number | null = null | ||
private dragOverTargetIds: string[] | null = null | ||
private dragStartSourceIds: string[] | null = null; | ||
private dropTargetIds: string[] = []; | ||
private dragEnterTargetIds: string[] = []; | ||
private currentNativeSource: NativeDragSource | null = null; | ||
private currentNativeHandle: Identifier | null = null; | ||
private currentDragSourceNode: Element | null = null; | ||
private altKeyPressed = false; | ||
private mouseMoveTimeoutTimer: number | null = null; | ||
private asyncEndDragFrameId: number | null = null; | ||
private dragOverTargetIds: string[] | null = null; | ||
private lastClientOffset: XYCoord | null = null | ||
private hoverRafId: number | null = null | ||
private lastClientOffset: XYCoord | null = null; | ||
private hoverRafId: number | null = null; | ||
public constructor( | ||
manager: DragDropManager, | ||
globalContext?: HTML5BackendContext, | ||
options?: HTML5BackendOptions, | ||
) { | ||
this.options = new OptionsReader(globalContext, options) | ||
this.actions = manager.getActions() | ||
this.monitor = manager.getMonitor() | ||
this.registry = manager.getRegistry() | ||
this.enterLeaveCounter = new EnterLeaveCounter(this.isNodeInDocument) | ||
} | ||
public constructor( | ||
manager: DragDropManager, | ||
globalContext?: HTML5BackendContext, | ||
options?: HTML5BackendOptions | ||
) { | ||
this.options = new OptionsReader(globalContext, options); | ||
this.actions = manager.getActions(); | ||
this.monitor = manager.getMonitor(); | ||
this.registry = manager.getRegistry(); | ||
this.enterLeaveCounter = new EnterLeaveCounter(this.isNodeInDocument); | ||
} | ||
/** | ||
* Generate profiling statistics for the HTML5Backend. | ||
*/ | ||
public profile(): Record<string, number> { | ||
return { | ||
sourcePreviewNodes: this.sourcePreviewNodes.size, | ||
sourcePreviewNodeOptions: this.sourcePreviewNodeOptions.size, | ||
sourceNodeOptions: this.sourceNodeOptions.size, | ||
sourceNodes: this.sourceNodes.size, | ||
dragStartSourceIds: this.dragStartSourceIds?.length || 0, | ||
dropTargetIds: this.dropTargetIds.length, | ||
dragEnterTargetIds: this.dragEnterTargetIds.length, | ||
dragOverTargetIds: this.dragOverTargetIds?.length || 0, | ||
} | ||
} | ||
/** | ||
* Generate profiling statistics for the HTML5Backend. | ||
*/ | ||
public profile(): Record<string, number> { | ||
return { | ||
sourcePreviewNodes: this.sourcePreviewNodes.size, | ||
sourcePreviewNodeOptions: this.sourcePreviewNodeOptions.size, | ||
sourceNodeOptions: this.sourceNodeOptions.size, | ||
sourceNodes: this.sourceNodes.size, | ||
dragStartSourceIds: this.dragStartSourceIds?.length || 0, | ||
dropTargetIds: this.dropTargetIds.length, | ||
dragEnterTargetIds: this.dragEnterTargetIds.length, | ||
dragOverTargetIds: this.dragOverTargetIds?.length || 0, | ||
}; | ||
} | ||
// public for test | ||
public get window(): Window | undefined { | ||
return this.options.window | ||
} | ||
public get document(): Document | undefined { | ||
return this.options.document | ||
} | ||
/** | ||
* Get the root element to use for event subscriptions | ||
*/ | ||
private get rootElement(): Node | undefined { | ||
return this.options.rootElement as Node | ||
} | ||
// public for test | ||
public get window(): Window | undefined { | ||
return this.options.window; | ||
} | ||
public get document(): Document | undefined { | ||
return this.options.document; | ||
} | ||
/** | ||
* Get the root element to use for event subscriptions | ||
*/ | ||
private get rootElement(): Node | undefined { | ||
return this.options.rootElement as Node; | ||
} | ||
public setup(): void { | ||
const root = this.rootElement as RootNode | undefined | ||
if (root === undefined) { | ||
return | ||
} | ||
public setup(): void { | ||
const root = this.rootElement as RootNode | undefined; | ||
if (root === undefined) { | ||
return; | ||
} | ||
if (root.__isReactDndBackendSetUp) { | ||
throw new Error('Cannot have two HTML5 backends at the same time.') | ||
} | ||
root.__isReactDndBackendSetUp = true | ||
this.addEventListeners(root) | ||
} | ||
if (root.__isReactDndBackendSetUp) { | ||
throw new Error("Cannot have two HTML5 backends at the same time."); | ||
} | ||
root.__isReactDndBackendSetUp = true; | ||
this.addEventListeners(root); | ||
} | ||
public teardown(): void { | ||
const root = this.rootElement as RootNode | ||
if (root === undefined) { | ||
return | ||
} | ||
public teardown(): void { | ||
const root = this.rootElement as RootNode; | ||
if (root === undefined) { | ||
return; | ||
} | ||
root.__isReactDndBackendSetUp = false | ||
this.removeEventListeners(this.rootElement as Element) | ||
this.clearCurrentDragSourceNode() | ||
if (this.asyncEndDragFrameId) { | ||
this.window?.cancelAnimationFrame(this.asyncEndDragFrameId) | ||
} | ||
} | ||
root.__isReactDndBackendSetUp = false; | ||
this.removeEventListeners(this.rootElement as Element); | ||
this.clearCurrentDragSourceNode(); | ||
if (this.asyncEndDragFrameId) { | ||
this.window?.cancelAnimationFrame(this.asyncEndDragFrameId); | ||
} | ||
} | ||
public connectDragPreview( | ||
sourceId: string, | ||
node: Element, | ||
options: any, | ||
): Unsubscribe { | ||
this.sourcePreviewNodeOptions.set(sourceId, options) | ||
this.sourcePreviewNodes.set(sourceId, node) | ||
public connectDragPreview( | ||
sourceId: string, | ||
node: Element, | ||
options: any | ||
): Unsubscribe { | ||
this.sourcePreviewNodeOptions.set(sourceId, options); | ||
this.sourcePreviewNodes.set(sourceId, node); | ||
return (): void => { | ||
this.sourcePreviewNodes.delete(sourceId) | ||
this.sourcePreviewNodeOptions.delete(sourceId) | ||
} | ||
} | ||
return (): void => { | ||
this.sourcePreviewNodes.delete(sourceId); | ||
this.sourcePreviewNodeOptions.delete(sourceId); | ||
}; | ||
} | ||
public connectDragSource( | ||
sourceId: string, | ||
node: Element, | ||
options: any, | ||
): Unsubscribe { | ||
this.sourceNodes.set(sourceId, node) | ||
this.sourceNodeOptions.set(sourceId, options) | ||
public connectDragSource( | ||
sourceId: string, | ||
node: Element, | ||
options: any | ||
): Unsubscribe { | ||
this.sourceNodes.set(sourceId, node); | ||
this.sourceNodeOptions.set(sourceId, options); | ||
const handleDragStart = (e: any) => this.handleDragStart(e, sourceId) | ||
const handleSelectStart = (e: any) => this.handleSelectStart(e) | ||
const handleDragStart = (e: any) => this.handleDragStart(e, sourceId); | ||
const handleSelectStart = (e: any) => this.handleSelectStart(e); | ||
node.setAttribute('draggable', 'true') | ||
node.addEventListener('dragstart', handleDragStart) | ||
node.addEventListener('selectstart', handleSelectStart) | ||
node.setAttribute("draggable", "true"); | ||
node.addEventListener("dragstart", handleDragStart); | ||
node.addEventListener("selectstart", handleSelectStart); | ||
return (): void => { | ||
this.sourceNodes.delete(sourceId) | ||
this.sourceNodeOptions.delete(sourceId) | ||
return (): void => { | ||
this.sourceNodes.delete(sourceId); | ||
this.sourceNodeOptions.delete(sourceId); | ||
node.removeEventListener('dragstart', handleDragStart) | ||
node.removeEventListener('selectstart', handleSelectStart) | ||
node.setAttribute('draggable', 'false') | ||
} | ||
} | ||
node.removeEventListener("dragstart", handleDragStart); | ||
node.removeEventListener("selectstart", handleSelectStart); | ||
node.setAttribute("draggable", "false"); | ||
}; | ||
} | ||
public connectDropTarget(targetId: string, node: HTMLElement): Unsubscribe { | ||
const handleDragEnter = (e: DragEvent) => this.handleDragEnter(e, targetId) | ||
const handleDragOver = (e: DragEvent) => this.handleDragOver(e, targetId) | ||
const handleDrop = (e: DragEvent) => this.handleDrop(e, targetId) | ||
public connectDropTarget(targetId: string, node: HTMLElement): Unsubscribe { | ||
const handleDragEnter = (e: DragEvent) => this.handleDragEnter(e, targetId); | ||
const handleDragOver = (e: DragEvent) => this.handleDragOver(e, targetId); | ||
const handleDrop = (e: DragEvent) => this.handleDrop(e, targetId); | ||
node.addEventListener('dragenter', handleDragEnter) | ||
node.addEventListener('dragover', handleDragOver) | ||
node.addEventListener('drop', handleDrop) | ||
node.addEventListener("dragenter", handleDragEnter); | ||
node.addEventListener("dragover", handleDragOver); | ||
node.addEventListener("drop", handleDrop); | ||
return (): void => { | ||
node.removeEventListener('dragenter', handleDragEnter) | ||
node.removeEventListener('dragover', handleDragOver) | ||
node.removeEventListener('drop', handleDrop) | ||
} | ||
} | ||
return (): void => { | ||
node.removeEventListener("dragenter", handleDragEnter); | ||
node.removeEventListener("dragover", handleDragOver); | ||
node.removeEventListener("drop", handleDrop); | ||
}; | ||
} | ||
private addEventListeners(target: Node) { | ||
// SSR Fix (https://github.com/react-dnd/react-dnd/pull/813 | ||
if (!target.addEventListener) { | ||
return | ||
} | ||
target.addEventListener( | ||
'dragstart', | ||
this.handleTopDragStart as EventListener, | ||
) | ||
target.addEventListener('dragstart', this.handleTopDragStartCapture, true) | ||
target.addEventListener('dragend', this.handleTopDragEndCapture, true) | ||
target.addEventListener( | ||
'dragenter', | ||
this.handleTopDragEnter as EventListener, | ||
) | ||
target.addEventListener( | ||
'dragenter', | ||
this.handleTopDragEnterCapture as EventListener, | ||
true, | ||
) | ||
target.addEventListener( | ||
'dragleave', | ||
this.handleTopDragLeaveCapture as EventListener, | ||
true, | ||
) | ||
target.addEventListener('dragover', this.handleTopDragOver as EventListener) | ||
target.addEventListener( | ||
'dragover', | ||
this.handleTopDragOverCapture as EventListener, | ||
true, | ||
) | ||
target.addEventListener('drop', this.handleTopDrop as EventListener) | ||
target.addEventListener( | ||
'drop', | ||
this.handleTopDropCapture as EventListener, | ||
true, | ||
) | ||
} | ||
public addEventListeners(target: Node) { | ||
// SSR Fix (https://github.com/react-dnd/react-dnd/pull/813 | ||
if (!target.addEventListener) { | ||
return; | ||
} | ||
target.addEventListener( | ||
"dragstart", | ||
this.handleTopDragStart as EventListener | ||
); | ||
target.addEventListener("dragstart", this.handleTopDragStartCapture, true); | ||
target.addEventListener("dragend", this.handleTopDragEndCapture, true); | ||
target.addEventListener( | ||
"dragenter", | ||
this.handleTopDragEnter as EventListener | ||
); | ||
target.addEventListener( | ||
"dragenter", | ||
this.handleTopDragEnterCapture as EventListener, | ||
true | ||
); | ||
target.addEventListener( | ||
"dragleave", | ||
this.handleTopDragLeaveCapture as EventListener, | ||
true | ||
); | ||
target.addEventListener( | ||
"dragover", | ||
this.handleTopDragOver as EventListener | ||
); | ||
target.addEventListener( | ||
"dragover", | ||
this.handleTopDragOverCapture as EventListener, | ||
true | ||
); | ||
target.addEventListener("drop", this.handleTopDrop as EventListener); | ||
target.addEventListener( | ||
"drop", | ||
this.handleTopDropCapture as EventListener, | ||
true | ||
); | ||
} | ||
private removeEventListeners(target: Node) { | ||
// SSR Fix (https://github.com/react-dnd/react-dnd/pull/813 | ||
if (!target.removeEventListener) { | ||
return | ||
} | ||
target.removeEventListener('dragstart', this.handleTopDragStart as any) | ||
target.removeEventListener( | ||
'dragstart', | ||
this.handleTopDragStartCapture, | ||
true, | ||
) | ||
target.removeEventListener('dragend', this.handleTopDragEndCapture, true) | ||
target.removeEventListener( | ||
'dragenter', | ||
this.handleTopDragEnter as EventListener, | ||
) | ||
target.removeEventListener( | ||
'dragenter', | ||
this.handleTopDragEnterCapture as EventListener, | ||
true, | ||
) | ||
target.removeEventListener( | ||
'dragleave', | ||
this.handleTopDragLeaveCapture as EventListener, | ||
true, | ||
) | ||
target.removeEventListener( | ||
'dragover', | ||
this.handleTopDragOver as EventListener, | ||
) | ||
target.removeEventListener( | ||
'dragover', | ||
this.handleTopDragOverCapture as EventListener, | ||
true, | ||
) | ||
target.removeEventListener('drop', this.handleTopDrop as EventListener) | ||
target.removeEventListener( | ||
'drop', | ||
this.handleTopDropCapture as EventListener, | ||
true, | ||
) | ||
} | ||
public removeEventListeners(target: Node) { | ||
// SSR Fix (https://github.com/react-dnd/react-dnd/pull/813 | ||
if (!target.removeEventListener) { | ||
return; | ||
} | ||
target.removeEventListener("dragstart", this.handleTopDragStart as any); | ||
target.removeEventListener( | ||
"dragstart", | ||
this.handleTopDragStartCapture, | ||
true | ||
); | ||
target.removeEventListener("dragend", this.handleTopDragEndCapture, true); | ||
target.removeEventListener( | ||
"dragenter", | ||
this.handleTopDragEnter as EventListener | ||
); | ||
target.removeEventListener( | ||
"dragenter", | ||
this.handleTopDragEnterCapture as EventListener, | ||
true | ||
); | ||
target.removeEventListener( | ||
"dragleave", | ||
this.handleTopDragLeaveCapture as EventListener, | ||
true | ||
); | ||
target.removeEventListener( | ||
"dragover", | ||
this.handleTopDragOver as EventListener | ||
); | ||
target.removeEventListener( | ||
"dragover", | ||
this.handleTopDragOverCapture as EventListener, | ||
true | ||
); | ||
target.removeEventListener("drop", this.handleTopDrop as EventListener); | ||
target.removeEventListener( | ||
"drop", | ||
this.handleTopDropCapture as EventListener, | ||
true | ||
); | ||
} | ||
private getCurrentSourceNodeOptions() { | ||
const sourceId = this.monitor.getSourceId() as string | ||
const sourceNodeOptions = this.sourceNodeOptions.get(sourceId) | ||
private getCurrentSourceNodeOptions() { | ||
const sourceId = this.monitor.getSourceId() as string; | ||
const sourceNodeOptions = this.sourceNodeOptions.get(sourceId); | ||
return { | ||
dropEffect: this.altKeyPressed ? 'copy' : 'move', | ||
...(sourceNodeOptions || {}), | ||
} | ||
} | ||
return { | ||
dropEffect: this.altKeyPressed ? "copy" : "move", | ||
...(sourceNodeOptions || {}), | ||
}; | ||
} | ||
private getCurrentDropEffect() { | ||
if (this.isDraggingNativeItem()) { | ||
// It makes more sense to default to 'copy' for native resources | ||
return 'copy' | ||
} | ||
private getCurrentDropEffect() { | ||
if (this.isDraggingNativeItem()) { | ||
// It makes more sense to default to 'copy' for native resources | ||
return "copy"; | ||
} | ||
return this.getCurrentSourceNodeOptions().dropEffect | ||
} | ||
return this.getCurrentSourceNodeOptions().dropEffect; | ||
} | ||
private getCurrentSourcePreviewNodeOptions() { | ||
const sourceId = this.monitor.getSourceId() as string | ||
const sourcePreviewNodeOptions = this.sourcePreviewNodeOptions.get(sourceId) | ||
private getCurrentSourcePreviewNodeOptions() { | ||
const sourceId = this.monitor.getSourceId() as string; | ||
const sourcePreviewNodeOptions = | ||
this.sourcePreviewNodeOptions.get(sourceId); | ||
return { | ||
anchorX: 0.5, | ||
anchorY: 0.5, | ||
captureDraggingState: false, | ||
...(sourcePreviewNodeOptions || {}), | ||
} | ||
} | ||
return { | ||
anchorX: 0.5, | ||
anchorY: 0.5, | ||
captureDraggingState: false, | ||
...(sourcePreviewNodeOptions || {}), | ||
}; | ||
} | ||
private getSourceClientOffset = (sourceId: string): XYCoord | null => { | ||
const source = this.sourceNodes.get(sourceId) | ||
return (source && getNodeClientOffset(source as HTMLElement)) || null | ||
} | ||
private getSourceClientOffset = (sourceId: string): XYCoord | null => { | ||
const source = this.sourceNodes.get(sourceId); | ||
return (source && getNodeClientOffset(source as HTMLElement)) || null; | ||
}; | ||
private isDraggingNativeItem() { | ||
const itemType = this.monitor.getItemType() | ||
return Object.keys(NativeTypes).some( | ||
(key: string) => (NativeTypes as any)[key] === itemType, | ||
) | ||
} | ||
private isDraggingNativeItem() { | ||
const itemType = this.monitor.getItemType(); | ||
return Object.keys(NativeTypes).some( | ||
(key: string) => (NativeTypes as any)[key] === itemType | ||
); | ||
} | ||
private beginDragNativeItem(type: string, dataTransfer?: DataTransfer) { | ||
this.clearCurrentDragSourceNode() | ||
private beginDragNativeItem(type: string, dataTransfer?: DataTransfer) { | ||
this.clearCurrentDragSourceNode(); | ||
this.currentNativeSource = createNativeDragSource(type, dataTransfer) | ||
this.currentNativeHandle = this.registry.addSource( | ||
type, | ||
this.currentNativeSource, | ||
) | ||
this.actions.beginDrag([this.currentNativeHandle]) | ||
} | ||
this.currentNativeSource = createNativeDragSource(type, dataTransfer); | ||
this.currentNativeHandle = this.registry.addSource( | ||
type, | ||
this.currentNativeSource | ||
); | ||
this.actions.beginDrag([this.currentNativeHandle]); | ||
} | ||
private endDragNativeItem = (): void => { | ||
if (!this.isDraggingNativeItem()) { | ||
return | ||
} | ||
private endDragNativeItem = (): void => { | ||
if (!this.isDraggingNativeItem()) { | ||
return; | ||
} | ||
this.actions.endDrag() | ||
if (this.currentNativeHandle) { | ||
this.registry.removeSource(this.currentNativeHandle) | ||
} | ||
this.currentNativeHandle = null | ||
this.currentNativeSource = null | ||
} | ||
this.actions.endDrag(); | ||
if (this.currentNativeHandle) { | ||
this.registry.removeSource(this.currentNativeHandle); | ||
} | ||
this.currentNativeHandle = null; | ||
this.currentNativeSource = null; | ||
}; | ||
private isNodeInDocument = (node: Node | null | undefined): boolean => { | ||
// Check the node either in the main document or in the current context | ||
return Boolean( | ||
node && | ||
this.document && | ||
this.document.body && | ||
this.document.body.contains(node), | ||
) | ||
} | ||
private isNodeInDocument = (node: Node | null | undefined): boolean => { | ||
// Check the node either in the main document or in the current context | ||
return Boolean( | ||
node && | ||
this.document && | ||
this.document.body && | ||
this.document.body.contains(node) | ||
); | ||
}; | ||
private endDragIfSourceWasRemovedFromDOM = (): void => { | ||
const node = this.currentDragSourceNode | ||
if (node == null || this.isNodeInDocument(node)) { | ||
return | ||
} | ||
private endDragIfSourceWasRemovedFromDOM = (): void => { | ||
const node = this.currentDragSourceNode; | ||
if (node == null || this.isNodeInDocument(node)) { | ||
return; | ||
} | ||
if (this.clearCurrentDragSourceNode() && this.monitor.isDragging()) { | ||
this.actions.endDrag() | ||
} | ||
this.cancelHover() | ||
} | ||
if (this.clearCurrentDragSourceNode() && this.monitor.isDragging()) { | ||
this.actions.endDrag(); | ||
} | ||
this.cancelHover(); | ||
}; | ||
private setCurrentDragSourceNode(node: Element | null) { | ||
this.clearCurrentDragSourceNode() | ||
this.currentDragSourceNode = node | ||
private setCurrentDragSourceNode(node: Element | null) { | ||
this.clearCurrentDragSourceNode(); | ||
this.currentDragSourceNode = node; | ||
// A timeout of > 0 is necessary to resolve Firefox issue referenced | ||
// See: | ||
// * https://github.com/react-dnd/react-dnd/pull/928 | ||
// * https://github.com/react-dnd/react-dnd/issues/869 | ||
const MOUSE_MOVE_TIMEOUT = 1000 | ||
// A timeout of > 0 is necessary to resolve Firefox issue referenced | ||
// See: | ||
// * https://github.com/react-dnd/react-dnd/pull/928 | ||
// * https://github.com/react-dnd/react-dnd/issues/869 | ||
const MOUSE_MOVE_TIMEOUT = 1000; | ||
// Receiving a mouse event in the middle of a dragging operation | ||
// means it has ended and the drag source node disappeared from DOM, | ||
// so the browser didn't dispatch the dragend event. | ||
// | ||
// We need to wait before we start listening for mousemove events. | ||
// This is needed because the drag preview needs to be drawn or else it fires an 'mousemove' event | ||
// immediately in some browsers. | ||
// | ||
// See: | ||
// * https://github.com/react-dnd/react-dnd/pull/928 | ||
// * https://github.com/react-dnd/react-dnd/issues/869 | ||
// | ||
this.mouseMoveTimeoutTimer = setTimeout(() => { | ||
return this.rootElement?.addEventListener( | ||
'mousemove', | ||
this.endDragIfSourceWasRemovedFromDOM, | ||
true, | ||
) | ||
}, MOUSE_MOVE_TIMEOUT) as any as number | ||
} | ||
// Receiving a mouse event in the middle of a dragging operation | ||
// means it has ended and the drag source node disappeared from DOM, | ||
// so the browser didn't dispatch the dragend event. | ||
// | ||
// We need to wait before we start listening for mousemove events. | ||
// This is needed because the drag preview needs to be drawn or else it fires an 'mousemove' event | ||
// immediately in some browsers. | ||
// | ||
// See: | ||
// * https://github.com/react-dnd/react-dnd/pull/928 | ||
// * https://github.com/react-dnd/react-dnd/issues/869 | ||
// | ||
this.mouseMoveTimeoutTimer = setTimeout(() => { | ||
return this.rootElement?.addEventListener( | ||
"mousemove", | ||
this.endDragIfSourceWasRemovedFromDOM, | ||
true | ||
); | ||
}, MOUSE_MOVE_TIMEOUT) as any as number; | ||
} | ||
private clearCurrentDragSourceNode() { | ||
if (this.currentDragSourceNode) { | ||
this.currentDragSourceNode = null | ||
private clearCurrentDragSourceNode() { | ||
if (this.currentDragSourceNode) { | ||
this.currentDragSourceNode = null; | ||
if (this.rootElement) { | ||
this.window?.clearTimeout(this.mouseMoveTimeoutTimer || undefined) | ||
this.rootElement.removeEventListener( | ||
'mousemove', | ||
this.endDragIfSourceWasRemovedFromDOM, | ||
true, | ||
) | ||
} | ||
if (this.rootElement && this.window) { | ||
if (this.mouseMoveTimeoutTimer !== null) { | ||
if (typeof this.window.clearTimeout === "function") { | ||
this.window.clearTimeout(this.mouseMoveTimeoutTimer); | ||
} else if (typeof clearTimeout === "function") { | ||
clearTimeout(this.mouseMoveTimeoutTimer); | ||
} | ||
this.mouseMoveTimeoutTimer = null; | ||
} | ||
this.rootElement.removeEventListener( | ||
"mousemove", | ||
this.endDragIfSourceWasRemovedFromDOM, | ||
true | ||
); | ||
} | ||
this.mouseMoveTimeoutTimer = null | ||
return true | ||
} | ||
return true; | ||
} | ||
return false | ||
} | ||
return false; | ||
} | ||
private scheduleHover = (dragOverTargetIds: string[] | null) => { | ||
if ( | ||
this.hoverRafId === null && | ||
typeof requestAnimationFrame !== 'undefined' | ||
) { | ||
this.hoverRafId = requestAnimationFrame(() => { | ||
if (this.monitor.isDragging()) { | ||
this.actions.hover(dragOverTargetIds || [], { | ||
clientOffset: this.lastClientOffset, | ||
}) | ||
} | ||
private scheduleHover = (dragOverTargetIds: string[] | null) => { | ||
if ( | ||
this.hoverRafId === null && | ||
typeof requestAnimationFrame !== "undefined" | ||
) { | ||
this.hoverRafId = requestAnimationFrame(() => { | ||
if (this.monitor.isDragging()) { | ||
this.actions.hover(dragOverTargetIds || [], { | ||
clientOffset: this.lastClientOffset, | ||
}); | ||
} | ||
this.hoverRafId = null | ||
}) | ||
} | ||
} | ||
this.hoverRafId = null; | ||
}); | ||
} | ||
}; | ||
private cancelHover = () => { | ||
if ( | ||
this.hoverRafId !== null && | ||
typeof cancelAnimationFrame !== 'undefined' | ||
) { | ||
cancelAnimationFrame(this.hoverRafId) | ||
this.hoverRafId = null | ||
} | ||
} | ||
private cancelHover = () => { | ||
if ( | ||
this.hoverRafId !== null && | ||
typeof cancelAnimationFrame !== "undefined" | ||
) { | ||
cancelAnimationFrame(this.hoverRafId); | ||
this.hoverRafId = null; | ||
} | ||
}; | ||
public handleTopDragStartCapture = (): void => { | ||
this.clearCurrentDragSourceNode() | ||
this.dragStartSourceIds = [] | ||
} | ||
public handleTopDragStartCapture = (): void => { | ||
this.clearCurrentDragSourceNode(); | ||
this.dragStartSourceIds = []; | ||
}; | ||
public handleDragStart(e: DragEvent, sourceId: string): void { | ||
if (e.defaultPrevented) { | ||
return | ||
} | ||
public handleDragStart(e: DragEvent, sourceId: string): void { | ||
if (e.defaultPrevented) { | ||
return; | ||
} | ||
if (!this.dragStartSourceIds) { | ||
this.dragStartSourceIds = [] | ||
} | ||
this.dragStartSourceIds.unshift(sourceId) | ||
} | ||
if (!this.dragStartSourceIds) { | ||
this.dragStartSourceIds = []; | ||
} | ||
this.dragStartSourceIds.unshift(sourceId); | ||
} | ||
public handleTopDragStart = (e: DragEvent): void => { | ||
if (e.defaultPrevented) { | ||
return | ||
} | ||
public handleTopDragStart = (e: DragEvent): void => { | ||
if (e.defaultPrevented) { | ||
return; | ||
} | ||
const { dragStartSourceIds } = this | ||
this.dragStartSourceIds = null | ||
const { dragStartSourceIds } = this; | ||
this.dragStartSourceIds = null; | ||
const clientOffset = getEventClientOffset(e) | ||
const clientOffset = getEventClientOffset(e); | ||
// Avoid crashing if we missed a drop event or our previous drag died | ||
if (this.monitor.isDragging()) { | ||
this.actions.endDrag() | ||
this.cancelHover() | ||
} | ||
// Avoid crashing if we missed a drop event or our previous drag died | ||
if (this.monitor.isDragging()) { | ||
this.actions.endDrag(); | ||
this.cancelHover(); | ||
} | ||
// Don't publish the source just yet (see why below) | ||
this.actions.beginDrag(dragStartSourceIds || [], { | ||
publishSource: false, | ||
getSourceClientOffset: this.getSourceClientOffset, | ||
clientOffset, | ||
}) | ||
// Don't publish the source just yet (see why below) | ||
this.actions.beginDrag(dragStartSourceIds || [], { | ||
publishSource: false, | ||
getSourceClientOffset: this.getSourceClientOffset, | ||
clientOffset, | ||
}); | ||
const { dataTransfer } = e | ||
const nativeType = matchNativeItemType(dataTransfer) | ||
const { dataTransfer } = e; | ||
const nativeType = matchNativeItemType(dataTransfer); | ||
if (this.monitor.isDragging()) { | ||
if (dataTransfer && typeof dataTransfer.setDragImage === 'function') { | ||
// Use custom drag image if user specifies it. | ||
// If child drag source refuses drag but parent agrees, | ||
// use parent's node as drag image. Neither works in IE though. | ||
const sourceId: string = this.monitor.getSourceId() as string | ||
const sourceNode = this.sourceNodes.get(sourceId) | ||
const dragPreview = this.sourcePreviewNodes.get(sourceId) || sourceNode | ||
if (this.monitor.isDragging()) { | ||
if (dataTransfer && typeof dataTransfer.setDragImage === "function") { | ||
// Use custom drag image if user specifies it. | ||
// If child drag source refuses drag but parent agrees, | ||
// use parent's node as drag image. Neither works in IE though. | ||
const sourceId: string = this.monitor.getSourceId() as string; | ||
const sourceNode = this.sourceNodes.get(sourceId); | ||
const dragPreview = this.sourcePreviewNodes.get(sourceId) || sourceNode; | ||
if (dragPreview) { | ||
const { anchorX, anchorY, offsetX, offsetY } = | ||
this.getCurrentSourcePreviewNodeOptions() | ||
const anchorPoint = { anchorX, anchorY } | ||
const offsetPoint = { offsetX, offsetY } | ||
const dragPreviewOffset = getDragPreviewOffset( | ||
sourceNode as HTMLElement, | ||
dragPreview as HTMLElement, | ||
clientOffset, | ||
anchorPoint, | ||
offsetPoint, | ||
) | ||
if (dragPreview) { | ||
const { anchorX, anchorY, offsetX, offsetY } = | ||
this.getCurrentSourcePreviewNodeOptions(); | ||
const anchorPoint = { anchorX, anchorY }; | ||
const offsetPoint = { offsetX, offsetY }; | ||
const dragPreviewOffset = getDragPreviewOffset( | ||
sourceNode as HTMLElement, | ||
dragPreview as HTMLElement, | ||
clientOffset, | ||
anchorPoint, | ||
offsetPoint | ||
); | ||
dataTransfer.setDragImage( | ||
dragPreview, | ||
dragPreviewOffset.x, | ||
dragPreviewOffset.y, | ||
) | ||
} | ||
} | ||
dataTransfer.setDragImage( | ||
dragPreview, | ||
dragPreviewOffset.x, | ||
dragPreviewOffset.y | ||
); | ||
} | ||
} | ||
try { | ||
// Firefox won't drag without setting data | ||
dataTransfer?.setData('application/json', {} as any) | ||
} catch (err) { | ||
// IE doesn't support MIME types in setData | ||
} | ||
try { | ||
// Firefox won't drag without setting data | ||
dataTransfer?.setData("application/json", {} as any); | ||
} catch (err) { | ||
// IE doesn't support MIME types in setData | ||
} | ||
// Store drag source node so we can check whether | ||
// it is removed from DOM and trigger endDrag manually. | ||
this.setCurrentDragSourceNode(e.target as Element) | ||
// Store drag source node so we can check whether | ||
// it is removed from DOM and trigger endDrag manually. | ||
this.setCurrentDragSourceNode(e.target as Element); | ||
// Now we are ready to publish the drag source.. or are we not? | ||
const { captureDraggingState } = this.getCurrentSourcePreviewNodeOptions() | ||
if (!captureDraggingState) { | ||
// Usually we want to publish it in the next tick so that browser | ||
// is able to screenshot the current (not yet dragging) state. | ||
// | ||
// It also neatly avoids a situation where render() returns null | ||
// in the same tick for the source element, and browser freaks out. | ||
setTimeout(() => this.actions.publishDragSource(), 0) | ||
} else { | ||
// In some cases the user may want to override this behavior, e.g. | ||
// to work around IE not supporting custom drag previews. | ||
// | ||
// When using a custom drag layer, the only way to prevent | ||
// the default drag preview from drawing in IE is to screenshot | ||
// the dragging state in which the node itself has zero opacity | ||
// and height. In this case, though, returning null from render() | ||
// will abruptly end the dragging, which is not obvious. | ||
// | ||
// This is the reason such behavior is strictly opt-in. | ||
this.actions.publishDragSource() | ||
} | ||
} else if (nativeType) { | ||
// A native item (such as URL) dragged from inside the document | ||
this.beginDragNativeItem(nativeType) | ||
} else if ( | ||
dataTransfer && | ||
!dataTransfer.types && | ||
((e.target && !(e.target as Element).hasAttribute) || | ||
!(e.target as Element).hasAttribute('draggable')) | ||
) { | ||
// Looks like a Safari bug: dataTransfer.types is null, but there was no draggable. | ||
// Just let it drag. It's a native type (URL or text) and will be picked up in | ||
// dragenter handler. | ||
return | ||
} else { | ||
// If by this time no drag source reacted, tell browser not to drag. | ||
e.preventDefault() | ||
} | ||
} | ||
// Now we are ready to publish the drag source.. or are we not? | ||
const { captureDraggingState } = | ||
this.getCurrentSourcePreviewNodeOptions(); | ||
if (!captureDraggingState) { | ||
// Usually we want to publish it in the next tick so that browser | ||
// is able to screenshot the current (not yet dragging) state. | ||
// | ||
// It also neatly avoids a situation where render() returns null | ||
// in the same tick for the source element, and browser freaks out. | ||
setTimeout(() => this.actions.publishDragSource(), 0); | ||
} else { | ||
// In some cases the user may want to override this behavior, e.g. | ||
// to work around IE not supporting custom drag previews. | ||
// | ||
// When using a custom drag layer, the only way to prevent | ||
// the default drag preview from drawing in IE is to screenshot | ||
// the dragging state in which the node itself has zero opacity | ||
// and height. In this case, though, returning null from render() | ||
// will abruptly end the dragging, which is not obvious. | ||
// | ||
// This is the reason such behavior is strictly opt-in. | ||
this.actions.publishDragSource(); | ||
} | ||
} else if (nativeType) { | ||
// A native item (such as URL) dragged from inside the document | ||
this.beginDragNativeItem(nativeType); | ||
} else if ( | ||
dataTransfer && | ||
!dataTransfer.types && | ||
((e.target && !(e.target as Element).hasAttribute) || | ||
!(e.target as Element).hasAttribute("draggable")) | ||
) { | ||
// Looks like a Safari bug: dataTransfer.types is null, but there was no draggable. | ||
// Just let it drag. It's a native type (URL or text) and will be picked up in | ||
// dragenter handler. | ||
return; | ||
} else { | ||
// If by this time no drag source reacted, tell browser not to drag. | ||
e.preventDefault(); | ||
} | ||
}; | ||
public handleTopDragEndCapture = (): void => { | ||
if (this.clearCurrentDragSourceNode() && this.monitor.isDragging()) { | ||
// Firefox can dispatch this event in an infinite loop | ||
// if dragend handler does something like showing an alert. | ||
// Only proceed if we have not handled it already. | ||
this.actions.endDrag() | ||
} | ||
this.cancelHover() | ||
} | ||
public handleTopDragEndCapture = (): void => { | ||
if (this.clearCurrentDragSourceNode() && this.monitor.isDragging()) { | ||
// Firefox can dispatch this event in an infinite loop | ||
// if dragend handler does something like showing an alert. | ||
// Only proceed if we have not handled it already. | ||
this.actions.endDrag(); | ||
} | ||
this.cancelHover(); | ||
}; | ||
public handleTopDragEnterCapture = (e: DragEvent): void => { | ||
this.dragEnterTargetIds = [] | ||
public handleTopDragEnterCapture = (e: DragEvent): void => { | ||
this.dragEnterTargetIds = []; | ||
if (this.isDraggingNativeItem()) { | ||
this.currentNativeSource?.loadDataTransfer(e.dataTransfer) | ||
} | ||
if (this.isDraggingNativeItem()) { | ||
this.currentNativeSource?.loadDataTransfer(e.dataTransfer); | ||
} | ||
const isFirstEnter = this.enterLeaveCounter.enter(e.target) | ||
if (!isFirstEnter || this.monitor.isDragging()) { | ||
return | ||
} | ||
const isFirstEnter = this.enterLeaveCounter.enter(e.target); | ||
if (!isFirstEnter || this.monitor.isDragging()) { | ||
return; | ||
} | ||
const { dataTransfer } = e | ||
const nativeType = matchNativeItemType(dataTransfer) | ||
const { dataTransfer } = e; | ||
const nativeType = matchNativeItemType(dataTransfer); | ||
if (nativeType) { | ||
// A native item (such as file or URL) dragged from outside the document | ||
this.beginDragNativeItem(nativeType, dataTransfer as DataTransfer) | ||
} | ||
} | ||
if (nativeType) { | ||
// A native item (such as file or URL) dragged from outside the document | ||
this.beginDragNativeItem(nativeType, dataTransfer as DataTransfer); | ||
} | ||
}; | ||
public handleDragEnter(_e: DragEvent, targetId: string): void { | ||
this.dragEnterTargetIds.unshift(targetId) | ||
} | ||
public handleDragEnter(_e: DragEvent, targetId: string): void { | ||
this.dragEnterTargetIds.unshift(targetId); | ||
} | ||
public handleTopDragEnter = (e: DragEvent): void => { | ||
const { dragEnterTargetIds } = this | ||
this.dragEnterTargetIds = [] | ||
public handleTopDragEnter = (e: DragEvent): void => { | ||
const { dragEnterTargetIds } = this; | ||
this.dragEnterTargetIds = []; | ||
if (!this.monitor.isDragging()) { | ||
// This is probably a native item type we don't understand. | ||
return | ||
} | ||
if (!this.monitor.isDragging()) { | ||
// This is probably a native item type we don't understand. | ||
return; | ||
} | ||
this.altKeyPressed = e.altKey | ||
this.altKeyPressed = e.altKey; | ||
// If the target changes position as the result of `dragenter`, `dragover` might still | ||
// get dispatched despite target being no longer there. The easy solution is to check | ||
// whether there actually is a target before firing `hover`. | ||
if (dragEnterTargetIds.length > 0) { | ||
this.actions.hover(dragEnterTargetIds, { | ||
clientOffset: getEventClientOffset(e), | ||
}) | ||
} | ||
// If the target changes position as the result of `dragenter`, `dragover` might still | ||
// get dispatched despite target being no longer there. The easy solution is to check | ||
// whether there actually is a target before firing `hover`. | ||
if (dragEnterTargetIds.length > 0) { | ||
this.actions.hover(dragEnterTargetIds, { | ||
clientOffset: getEventClientOffset(e), | ||
}); | ||
} | ||
const canDrop = dragEnterTargetIds.some((targetId) => | ||
this.monitor.canDropOnTarget(targetId), | ||
) | ||
const canDrop = dragEnterTargetIds.some((targetId) => | ||
this.monitor.canDropOnTarget(targetId) | ||
); | ||
if (canDrop) { | ||
// IE requires this to fire dragover events | ||
e.preventDefault() | ||
if (e.dataTransfer) { | ||
e.dataTransfer.dropEffect = this.getCurrentDropEffect() | ||
} | ||
} | ||
} | ||
if (canDrop) { | ||
// IE requires this to fire dragover events | ||
e.preventDefault(); | ||
if (e.dataTransfer) { | ||
e.dataTransfer.dropEffect = this.getCurrentDropEffect(); | ||
} | ||
} | ||
}; | ||
public handleTopDragOverCapture = (e: DragEvent): void => { | ||
this.dragOverTargetIds = [] | ||
public handleTopDragOverCapture = (e: DragEvent): void => { | ||
this.dragOverTargetIds = []; | ||
if (this.isDraggingNativeItem()) { | ||
this.currentNativeSource?.loadDataTransfer(e.dataTransfer) | ||
} | ||
} | ||
if (this.isDraggingNativeItem()) { | ||
this.currentNativeSource?.loadDataTransfer(e.dataTransfer); | ||
} | ||
}; | ||
public handleDragOver(_e: DragEvent, targetId: string): void { | ||
if (this.dragOverTargetIds === null) { | ||
this.dragOverTargetIds = [] | ||
} | ||
this.dragOverTargetIds.unshift(targetId) | ||
} | ||
public handleDragOver(_e: DragEvent, targetId: string): void { | ||
if (this.dragOverTargetIds === null) { | ||
this.dragOverTargetIds = []; | ||
} | ||
this.dragOverTargetIds.unshift(targetId); | ||
} | ||
public handleTopDragOver = (e: DragEvent): void => { | ||
const { dragOverTargetIds } = this | ||
this.dragOverTargetIds = [] | ||
public handleTopDragOver = (e: DragEvent): void => { | ||
const { dragOverTargetIds } = this; | ||
this.dragOverTargetIds = []; | ||
if (!this.monitor.isDragging()) { | ||
// This is probably a native item type we don't understand. | ||
// Prevent default "drop and blow away the whole document" action. | ||
e.preventDefault() | ||
if (e.dataTransfer) { | ||
e.dataTransfer.dropEffect = 'none' | ||
} | ||
return | ||
} | ||
if (!this.monitor.isDragging()) { | ||
// This is probably a native item type we don't understand. | ||
// Prevent default "drop and blow away the whole document" action. | ||
e.preventDefault(); | ||
if (e.dataTransfer) { | ||
e.dataTransfer.dropEffect = "none"; | ||
} | ||
return; | ||
} | ||
this.altKeyPressed = e.altKey | ||
this.lastClientOffset = getEventClientOffset(e) | ||
this.altKeyPressed = e.altKey; | ||
this.lastClientOffset = getEventClientOffset(e); | ||
this.scheduleHover(dragOverTargetIds) | ||
this.scheduleHover(dragOverTargetIds); | ||
const canDrop = (dragOverTargetIds || []).some((targetId) => | ||
this.monitor.canDropOnTarget(targetId), | ||
) | ||
const canDrop = (dragOverTargetIds || []).some((targetId) => | ||
this.monitor.canDropOnTarget(targetId) | ||
); | ||
if (canDrop) { | ||
// Show user-specified drop effect. | ||
e.preventDefault() | ||
if (e.dataTransfer) { | ||
e.dataTransfer.dropEffect = this.getCurrentDropEffect() | ||
} | ||
} else if (this.isDraggingNativeItem()) { | ||
// Don't show a nice cursor but still prevent default | ||
// "drop and blow away the whole document" action. | ||
e.preventDefault() | ||
} else { | ||
e.preventDefault() | ||
if (e.dataTransfer) { | ||
e.dataTransfer.dropEffect = 'none' | ||
} | ||
} | ||
} | ||
if (canDrop) { | ||
// Show user-specified drop effect. | ||
e.preventDefault(); | ||
if (e.dataTransfer) { | ||
e.dataTransfer.dropEffect = this.getCurrentDropEffect(); | ||
} | ||
} else if (this.isDraggingNativeItem()) { | ||
// Don't show a nice cursor but still prevent default | ||
// "drop and blow away the whole document" action. | ||
e.preventDefault(); | ||
} else { | ||
e.preventDefault(); | ||
if (e.dataTransfer) { | ||
e.dataTransfer.dropEffect = "none"; | ||
} | ||
} | ||
}; | ||
public handleTopDragLeaveCapture = (e: DragEvent): void => { | ||
if (this.isDraggingNativeItem()) { | ||
e.preventDefault() | ||
} | ||
public handleTopDragLeaveCapture = (e: DragEvent): void => { | ||
if (this.isDraggingNativeItem()) { | ||
e.preventDefault(); | ||
} | ||
const isLastLeave = this.enterLeaveCounter.leave(e.target) | ||
if (!isLastLeave) { | ||
return | ||
} | ||
const isLastLeave = this.enterLeaveCounter.leave(e.target); | ||
if (!isLastLeave) { | ||
return; | ||
} | ||
if (this.isDraggingNativeItem()) { | ||
setTimeout(() => this.endDragNativeItem(), 0) | ||
} | ||
this.cancelHover() | ||
} | ||
if (this.isDraggingNativeItem()) { | ||
setTimeout(() => this.endDragNativeItem(), 0); | ||
} | ||
this.cancelHover(); | ||
}; | ||
public handleTopDropCapture = (e: DragEvent): void => { | ||
this.dropTargetIds = [] | ||
public handleTopDropCapture = (e: DragEvent): void => { | ||
this.dropTargetIds = []; | ||
if (this.isDraggingNativeItem()) { | ||
e.preventDefault() | ||
this.currentNativeSource?.loadDataTransfer(e.dataTransfer) | ||
} else if (matchNativeItemType(e.dataTransfer)) { | ||
// Dragging some elements, like <a> and <img> may still behave like a native drag event, | ||
// even if the current drag event matches a user-defined type. | ||
// Stop the default behavior when we're not expecting a native item to be dropped. | ||
if (this.isDraggingNativeItem()) { | ||
e.preventDefault(); | ||
this.currentNativeSource?.loadDataTransfer(e.dataTransfer); | ||
} else if (matchNativeItemType(e.dataTransfer)) { | ||
// Dragging some elements, like <a> and <img> may still behave like a native drag event, | ||
// even if the current drag event matches a user-defined type. | ||
// Stop the default behavior when we're not expecting a native item to be dropped. | ||
e.preventDefault() | ||
} | ||
e.preventDefault(); | ||
} | ||
this.enterLeaveCounter.reset() | ||
} | ||
this.enterLeaveCounter.reset(); | ||
}; | ||
public handleDrop(_e: DragEvent, targetId: string): void { | ||
this.dropTargetIds.unshift(targetId) | ||
} | ||
public handleDrop(_e: DragEvent, targetId: string): void { | ||
this.dropTargetIds.unshift(targetId); | ||
} | ||
public handleTopDrop = (e: DragEvent): void => { | ||
const { dropTargetIds } = this | ||
this.dropTargetIds = [] | ||
public handleTopDrop = (e: DragEvent): void => { | ||
const { dropTargetIds } = this; | ||
this.dropTargetIds = []; | ||
this.actions.hover(dropTargetIds, { | ||
clientOffset: getEventClientOffset(e), | ||
}) | ||
this.actions.drop({ dropEffect: this.getCurrentDropEffect() }) | ||
this.actions.hover(dropTargetIds, { | ||
clientOffset: getEventClientOffset(e), | ||
}); | ||
this.actions.drop({ dropEffect: this.getCurrentDropEffect() }); | ||
if (this.isDraggingNativeItem()) { | ||
this.endDragNativeItem() | ||
} else if (this.monitor.isDragging()) { | ||
this.actions.endDrag() | ||
} | ||
this.cancelHover() | ||
} | ||
if (this.isDraggingNativeItem()) { | ||
this.endDragNativeItem(); | ||
} else if (this.monitor.isDragging()) { | ||
this.actions.endDrag(); | ||
} | ||
this.cancelHover(); | ||
}; | ||
public handleSelectStart = (e: DragEvent): void => { | ||
const target = e.target as HTMLElement & { dragDrop: () => void } | ||
public handleSelectStart = (e: DragEvent): void => { | ||
const target = e.target as HTMLElement & { dragDrop: () => void }; | ||
// Only IE requires us to explicitly say | ||
// we want drag drop operation to start | ||
if (typeof target.dragDrop !== 'function') { | ||
return | ||
} | ||
// Only IE requires us to explicitly say | ||
// we want drag drop operation to start | ||
if (typeof target.dragDrop !== "function") { | ||
return; | ||
} | ||
// Inputs and textareas should be selectable | ||
if ( | ||
target.tagName === 'INPUT' || | ||
target.tagName === 'SELECT' || | ||
target.tagName === 'TEXTAREA' || | ||
target.isContentEditable | ||
) { | ||
return | ||
} | ||
// Inputs and textareas should be selectable | ||
if ( | ||
target.tagName === "INPUT" || | ||
target.tagName === "SELECT" || | ||
target.tagName === "TEXTAREA" || | ||
target.isContentEditable | ||
) { | ||
return; | ||
} | ||
// For other targets, ask IE | ||
// to enable drag and drop | ||
e.preventDefault() | ||
target.dragDrop() | ||
} | ||
// For other targets, ask IE | ||
// to enable drag and drop | ||
e.preventDefault(); | ||
target.dragDrop(); | ||
}; | ||
} |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
1378719
5860