Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@vizzly/dnd-backend

Package Overview
Dependencies
Maintainers
2
Versions
21
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@vizzly/dnd-backend - npm Package Compare versions

Comparing version 15.0.1-a to 15.0.1-b

2

package.json
{
"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();
};
}
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc