dnd-manager 1.1.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -4
- package/dist/index.d.mts +16 -8
- package/dist/index.mjs +24 -10
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -3
package/README.md
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
# dnd-manager
|
|
2
2
|
|
|
3
|
-
Simple, performant, and data-driven Drag-and-Drop library for vanilla JS,
|
|
4
|
-
framework
|
|
3
|
+
Simple, performant, and data-driven Drag-and-Drop library for vanilla JS, compatible with any
|
|
4
|
+
framework, and designed for developers who want full control over drag-and-drop UX without being
|
|
5
|
+
locked into specific component patterns or opinionated libraries.
|
|
5
6
|
|
|
6
|
-
This document provides practical examples of using the library in both vanilla JavaScript and
|
|
7
|
-
|
|
7
|
+
This document provides practical examples of using the library in both vanilla JavaScript and React
|
|
8
|
+
applications.
|
|
8
9
|
|
|
9
10
|
## Table of Contents
|
|
10
11
|
|
|
@@ -832,6 +833,8 @@ function DragPreview({ item, position, width, height }: DragPreviewProps) {
|
|
|
832
833
|
|
|
833
834
|
### Configuration:
|
|
834
835
|
|
|
836
|
+
- `draggableKind` / `droppableKind` - Accept `string` or `string[]` to match one or many kinds. Each
|
|
837
|
+
element should still expose a single `data-kind` value.
|
|
835
838
|
- `dragThreshold: 10` - Prevents accidental drags
|
|
836
839
|
- `clickThreshold: 10` - Distinguishes clicks from drags
|
|
837
840
|
- `scrollThreshold: 100` - Auto-scroll near viewport edges
|
package/dist/index.d.mts
CHANGED
|
@@ -59,23 +59,28 @@ type DragDropConfig = {
|
|
|
59
59
|
*/
|
|
60
60
|
cancelOnPointerLeave?: boolean;
|
|
61
61
|
/**
|
|
62
|
-
* The `data-kind`
|
|
63
|
-
*
|
|
62
|
+
* The `data-kind` value(s) for draggable elements.
|
|
63
|
+
* Can be a single kind or multiple kinds.
|
|
64
|
+
* Each draggable element should still expose a single `data-kind` value.
|
|
64
65
|
* @example
|
|
65
66
|
* draggableKind: 'cell' // <div data-kind="cell">
|
|
67
|
+
* draggableKind: ['quest', 'bounty'] // <div data-kind="quest">
|
|
66
68
|
* draggableKind: 'card' // <div data-kind="card">
|
|
67
69
|
* draggableKind: 'list-item' // <div data-kind="list-item">
|
|
68
70
|
*/
|
|
69
|
-
draggableKind: string;
|
|
71
|
+
draggableKind: string | string[];
|
|
70
72
|
/**
|
|
71
|
-
* The `data-kind`
|
|
72
|
-
*
|
|
73
|
+
* The `data-kind` value(s) for droppable target elements.
|
|
74
|
+
* Can be a single kind or multiple kinds.
|
|
75
|
+
* Elements with matching kinds can receive dropped items.
|
|
76
|
+
* Each droppable element should still expose a single `data-kind` value.
|
|
73
77
|
* Often the same as `draggableKind` for reorderable lists.
|
|
74
78
|
* @example
|
|
75
79
|
* droppableKind: 'cell' // Can drop on cells
|
|
80
|
+
* droppableKind: ['quest', 'bounty'] // Can drop on either kind
|
|
76
81
|
* droppableKind: 'zone' // Can drop on zones
|
|
77
82
|
*/
|
|
78
|
-
droppableKind: string;
|
|
83
|
+
droppableKind: string | string[];
|
|
79
84
|
};
|
|
80
85
|
type PointerPosition = {
|
|
81
86
|
x: number;
|
|
@@ -103,6 +108,8 @@ type DragState<TItem, TPosition> = {
|
|
|
103
108
|
declare class DragDropManager<TItem = unknown, TPosition = unknown> {
|
|
104
109
|
private container;
|
|
105
110
|
private config;
|
|
111
|
+
private draggableKinds;
|
|
112
|
+
private droppableKinds;
|
|
106
113
|
private callbacks;
|
|
107
114
|
private state;
|
|
108
115
|
private rafId;
|
|
@@ -122,10 +129,11 @@ declare class DragDropManager<TItem = unknown, TPosition = unknown> {
|
|
|
122
129
|
private cleanup;
|
|
123
130
|
private getPointerId;
|
|
124
131
|
private isActivePointer;
|
|
132
|
+
private normalizeKindList;
|
|
125
133
|
/**
|
|
126
|
-
* Find closest ancestor element with
|
|
134
|
+
* Find closest ancestor element with a `data-kind` matching any expected kind.
|
|
127
135
|
*/
|
|
128
|
-
private
|
|
136
|
+
private findElementByKinds;
|
|
129
137
|
/**
|
|
130
138
|
* Calculate distance between two points
|
|
131
139
|
*/
|
package/dist/index.mjs
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
var DragDropManager = class {
|
|
3
3
|
container;
|
|
4
4
|
config;
|
|
5
|
+
draggableKinds;
|
|
6
|
+
droppableKinds;
|
|
5
7
|
callbacks;
|
|
6
8
|
state = {
|
|
7
9
|
startPosition: null,
|
|
@@ -34,6 +36,8 @@ var DragDropManager = class {
|
|
|
34
36
|
draggableKind: config.draggableKind,
|
|
35
37
|
droppableKind: config.droppableKind
|
|
36
38
|
};
|
|
39
|
+
this.draggableKinds = this.normalizeKindList(this.config.draggableKind);
|
|
40
|
+
this.droppableKinds = this.normalizeKindList(this.config.droppableKind);
|
|
37
41
|
this.callbacks = callbacks;
|
|
38
42
|
this.boundHandlers = {
|
|
39
43
|
pointerDown: this.handlePointerDown.bind(this),
|
|
@@ -58,9 +62,10 @@ var DragDropManager = class {
|
|
|
58
62
|
handlePointerDown(e) {
|
|
59
63
|
if (this.state.startPosition) return;
|
|
60
64
|
const target = e.target;
|
|
61
|
-
const
|
|
62
|
-
if (!
|
|
63
|
-
const
|
|
65
|
+
const draggableMatch = this.findElementByKinds(target, this.draggableKinds);
|
|
66
|
+
if (!draggableMatch) return;
|
|
67
|
+
const { element: draggableElement, kind: sourceKind } = draggableMatch;
|
|
68
|
+
const position = this.callbacks.getItemPosition(draggableElement, sourceKind);
|
|
64
69
|
if (!position) return;
|
|
65
70
|
if (draggableElement.dataset.empty !== void 0) return;
|
|
66
71
|
if (this.callbacks.canDrag && !this.callbacks.canDrag(draggableElement, position)) return;
|
|
@@ -102,7 +107,7 @@ var DragDropManager = class {
|
|
|
102
107
|
if (!this.lastPointerPos) return;
|
|
103
108
|
const pos = this.lastPointerPos;
|
|
104
109
|
const elementUnderCursor = document.elementFromPoint(pos.x, pos.y);
|
|
105
|
-
const droppableElement = this.
|
|
110
|
+
const droppableElement = this.findElementByKinds(elementUnderCursor, this.droppableKinds)?.element ?? null;
|
|
106
111
|
if (droppableElement !== this.state.lastHoveredElement) {
|
|
107
112
|
if (this.state.lastHoveredElement) this.state.lastHoveredElement.removeAttribute("data-hovered");
|
|
108
113
|
if (droppableElement) {
|
|
@@ -130,9 +135,10 @@ var DragDropManager = class {
|
|
|
130
135
|
return;
|
|
131
136
|
}
|
|
132
137
|
const target = document.elementFromPoint(e.clientX, e.clientY);
|
|
133
|
-
const
|
|
134
|
-
|
|
135
|
-
|
|
138
|
+
const droppableMatch = this.findElementByKinds(target, this.droppableKinds);
|
|
139
|
+
const droppableElement = droppableMatch?.element ?? null;
|
|
140
|
+
if (droppableElement && droppableMatch) {
|
|
141
|
+
const targetPosition = this.callbacks.getItemPosition(droppableElement, droppableMatch.kind);
|
|
136
142
|
if (targetPosition) this.callbacks.onDrop?.(this.state.sourcePosition, targetPosition, this.state.sourceItem);
|
|
137
143
|
}
|
|
138
144
|
this.cleanup();
|
|
@@ -194,13 +200,21 @@ var DragDropManager = class {
|
|
|
194
200
|
const pointerId = this.getPointerId(e);
|
|
195
201
|
return pointerId === null || pointerId === this.state.activePointerId;
|
|
196
202
|
}
|
|
203
|
+
normalizeKindList(kinds) {
|
|
204
|
+
const values = Array.isArray(kinds) ? kinds : [kinds];
|
|
205
|
+
return [...new Set(values.map((value) => value.trim()).filter(Boolean))];
|
|
206
|
+
}
|
|
197
207
|
/**
|
|
198
|
-
* Find closest ancestor element with
|
|
208
|
+
* Find closest ancestor element with a `data-kind` matching any expected kind.
|
|
199
209
|
*/
|
|
200
|
-
|
|
210
|
+
findElementByKinds(element, kinds) {
|
|
201
211
|
let current = element;
|
|
202
212
|
while (current && current !== this.container) {
|
|
203
|
-
|
|
213
|
+
const elementKind = current.dataset.kind;
|
|
214
|
+
if (elementKind && kinds.includes(elementKind)) return {
|
|
215
|
+
element: current,
|
|
216
|
+
kind: elementKind
|
|
217
|
+
};
|
|
204
218
|
current = current.parentElement;
|
|
205
219
|
}
|
|
206
220
|
return null;
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../src/drag-drop-manager.ts","../src/drag-preview-controller.ts"],"sourcesContent":["/**\n * Framework-agnostic Drag and Drop Manager\n *\n * A data-driven drag-and-drop utility that works with React and vanilla JS.\n * Uses data attributes and direct DOM manipulation for optimal performance.\n *\n * @template TItem - The type of data associated with draggable items\n * @template TPosition - The type representing position/coordinates (e.g., {x, y}, {box, cell}, etc.)\n */\n\nexport type DragDropConfig = {\n /**\n * Minimum distance in pixels the pointer must move before a drag operation starts.\n * Prevents accidental drags from small movements and allows normal scrolling on mobile.\n * Checked during `pointermove` before drag has started.\n * @default 10\n * @example\n * dragThreshold: 15 // Harder to start drag (better for mobile scroll)\n */\n dragThreshold?: number\n\n /**\n * Maximum distance in pixels between pointerdown and pointerup to treat as a click/tap.\n * Used to distinguish intentional clicks from cancelled drag attempts.\n * Only checked on `pointerup` if drag never started.\n * @default 10\n * @example\n * clickThreshold: 5 // Stricter click detection\n * clickThreshold: 15 // More forgiving for mobile taps\n */\n clickThreshold?: number\n\n /**\n * Distance in pixels from viewport edge that triggers auto-scrolling during drag.\n * When dragging near screen edges, the window automatically scrolls.\n * @default 100\n * @example\n * scrollThreshold: 50 // Start scrolling closer to edges\n * scrollThreshold: 150 // Larger safe zone before scrolling\n */\n scrollThreshold?: number\n\n /**\n * Scroll speed in pixels per animation frame during auto-scroll.\n * Higher values = faster scrolling when dragging near viewport edges.\n * @default 10\n * @example\n * scrollSpeed: 5 // Slower, smoother scroll\n * scrollSpeed: 20 // Faster scroll\n */\n scrollSpeed?: number\n\n /**\n * Cancel the current drag operation when Escape is pressed.\n * Useful for explicit keyboard cancellation.\n * @default true\n */\n cancelOnEscape?: boolean\n\n /**\n * Cancel the current drag operation when the pointer leaves the browser window.\n * Prevents \"stuck dragging\" when pointerup happens outside the app/window.\n * @default true\n */\n cancelOnPointerLeave?: boolean\n\n /**\n * The `data-kind` attribute value for draggable elements.\n * Elements with this attribute can be picked up and dragged.\n * @example\n * draggableKind: 'cell' // <div data-kind=\"cell\">\n * draggableKind: 'card' // <div data-kind=\"card\">\n * draggableKind: 'list-item' // <div data-kind=\"list-item\">\n */\n draggableKind: string\n\n /**\n * The `data-kind` attribute value for droppable target elements.\n * Elements with this attribute can receive dropped items.\n * Often the same as `draggableKind` for reorderable lists.\n * @example\n * droppableKind: 'cell' // Can drop on cells\n * droppableKind: 'zone' // Can drop on zones\n */\n droppableKind: string\n}\n\nexport type PointerPosition = {\n x: number\n y: number\n}\n\nexport type DragDropCallbacks<TItem, TPosition> = {\n /** Called when determining if an element can be dragged */\n canDrag?: (element: HTMLElement, position: TPosition) => boolean\n /** Called when drag starts (after threshold is met) */\n onDragStart?: (element: HTMLElement, position: TPosition, item: TItem) => void\n /** Called continuously during drag */\n onDragMove?: (pos: PointerPosition, hoveredElement: HTMLElement | null) => void\n /** Called when item is dropped on a valid target */\n onDrop?: (sourcePosition: TPosition, targetPosition: TPosition, sourceItem: TItem) => void\n /** Called when drag ends (regardless of success) */\n onDragEnd?: () => void\n /** Called on simple click/tap (no drag) */\n onClick?: (element: HTMLElement, position: TPosition) => void\n /** Called to extract item data from an element */\n getItemData?: (element: HTMLElement, position: TPosition) => TItem | null\n /** Called to get the custom position/coordinates of an element */\n getItemPosition: (element: HTMLElement, kind: string) => TPosition | null\n}\n\ntype DragState<TItem, TPosition> = {\n startPosition: PointerPosition | null\n sourceElement: HTMLElement | null\n sourcePosition: TPosition | null\n sourceItem: TItem | null\n activePointerId: number | null\n isDragging: boolean\n lastHoveredElement: HTMLElement | null\n}\n\nexport class DragDropManager<TItem = unknown, TPosition = unknown> {\n private container: HTMLElement\n private config: Required<DragDropConfig>\n private callbacks: DragDropCallbacks<TItem, TPosition>\n private state: DragState<TItem, TPosition> = {\n startPosition: null,\n sourceElement: null,\n sourcePosition: null,\n sourceItem: null,\n activePointerId: null,\n isDragging: false,\n lastHoveredElement: null,\n }\n private rafId: number | null = null\n private lastPointerPos: PointerPosition | null = null\n private boundHandlers: {\n pointerDown: (e: PointerEvent) => void\n pointerMove: (e: PointerEvent) => void\n pointerUp: (e: PointerEvent) => void\n pointerCancel: (e: PointerEvent) => void\n keyDown: (e: KeyboardEvent) => void\n mouseOut: (e: MouseEvent) => void\n }\n\n constructor(\n containerRef: { current: HTMLElement | null } | string | HTMLElement,\n config: DragDropConfig,\n callbacks: DragDropCallbacks<TItem, TPosition>,\n ) {\n // Resolve container element\n if (typeof containerRef === 'string') {\n const element = document.querySelector<HTMLElement>(containerRef)\n if (!element) {\n throw new Error(`DragDropManager: Could not find element with selector \"${containerRef}\"`)\n }\n this.container = element\n } else if (typeof containerRef === 'object' && 'current' in containerRef) {\n if (!containerRef.current) {\n throw new Error('DragDropManager: Ref.current is null')\n }\n this.container = containerRef.current\n } else {\n this.container = containerRef as HTMLElement\n }\n\n this.config = {\n dragThreshold: config.dragThreshold ?? 10,\n clickThreshold: config.clickThreshold ?? 10,\n scrollThreshold: config.scrollThreshold ?? 100,\n scrollSpeed: config.scrollSpeed ?? 10,\n cancelOnEscape: config.cancelOnEscape ?? true,\n cancelOnPointerLeave: config.cancelOnPointerLeave ?? true,\n draggableKind: config.draggableKind,\n droppableKind: config.droppableKind,\n }\n\n this.callbacks = callbacks\n\n // Bind handlers to preserve context\n this.boundHandlers = {\n pointerDown: this.handlePointerDown.bind(this),\n pointerMove: this.handlePointerMove.bind(this),\n pointerUp: this.handlePointerUp.bind(this),\n pointerCancel: this.handlePointerCancel.bind(this),\n keyDown: this.handleKeyDown.bind(this),\n mouseOut: this.handleMouseOut.bind(this),\n }\n\n this.attachListeners()\n }\n\n private attachListeners(): void {\n this.container.addEventListener('pointerdown', this.boundHandlers.pointerDown)\n this.container.addEventListener('pointermove', this.boundHandlers.pointerMove)\n this.container.addEventListener('pointerup', this.boundHandlers.pointerUp)\n this.container.addEventListener('pointercancel', this.boundHandlers.pointerCancel)\n window.addEventListener('pointerup', this.boundHandlers.pointerUp)\n window.addEventListener('pointercancel', this.boundHandlers.pointerCancel)\n window.addEventListener('keydown', this.boundHandlers.keyDown)\n window.addEventListener('mouseout', this.boundHandlers.mouseOut)\n }\n\n private handlePointerDown(e: PointerEvent): void {\n if (this.state.startPosition) return\n\n const target = e.target as HTMLElement\n const draggableElement = this.findElementByKind(target, this.config.draggableKind)\n\n if (!draggableElement) return\n\n const position = this.callbacks.getItemPosition(draggableElement, this.config.draggableKind)\n if (!position) return\n\n // Check if element is empty (convention: data-empty=\"any-value\")\n const isEmpty = draggableElement.dataset.empty !== undefined\n if (isEmpty) return\n\n // Check canDrag callback\n if (this.callbacks.canDrag && !this.callbacks.canDrag(draggableElement, position)) {\n return\n }\n\n // Get item data\n const item = this.callbacks.getItemData?.(draggableElement, position) ?? null\n if (!item) return\n\n // Prevent text selection during potential drag\n e.preventDefault()\n\n // Store start position and element info\n this.state.startPosition = { x: e.clientX, y: e.clientY }\n this.state.sourceElement = draggableElement\n this.state.sourcePosition = position\n this.state.sourceItem = item\n this.state.activePointerId = this.getPointerId(e)\n\n // Capture pointer events outside container boundaries when supported.\n if (\n this.state.activePointerId !== null &&\n typeof draggableElement.setPointerCapture === 'function'\n ) {\n try {\n draggableElement.setPointerCapture(this.state.activePointerId)\n } catch {\n // Ignore unsupported/inactive-pointer capture errors.\n }\n }\n }\n\n private handlePointerMove(e: PointerEvent): void {\n if (!this.isActivePointer(e)) return\n\n const currentPos: PointerPosition = { x: e.clientX, y: e.clientY }\n\n // Check if we should start dragging (threshold not yet met)\n if (this.state.startPosition && !this.state.isDragging) {\n const distance = this.getDistance(this.state.startPosition, currentPos)\n\n if (distance >= this.config.dragThreshold) {\n // Start drag\n this.state.isDragging = true\n\n if (this.state.sourceElement && this.state.sourcePosition && this.state.sourceItem) {\n // Set data-dragging attribute on source\n this.state.sourceElement.setAttribute('data-dragging', 'true')\n\n // Notify drag start\n this.callbacks.onDragStart?.(\n this.state.sourceElement,\n this.state.sourcePosition,\n this.state.sourceItem,\n )\n }\n }\n return\n }\n\n if (!this.state.isDragging) return\n\n // Store latest position for RAF\n this.lastPointerPos = currentPos\n\n // Throttle updates to 60fps using RAF\n if (this.rafId === null) {\n this.rafId = requestAnimationFrame(() => {\n this.rafId = null\n if (!this.lastPointerPos) return\n\n const pos = this.lastPointerPos\n\n // Find hovered droppable element\n const elementUnderCursor = document.elementFromPoint(pos.x, pos.y) as HTMLElement\n const droppableElement = this.findElementByKind(\n elementUnderCursor,\n this.config.droppableKind,\n )\n\n // Update hover feedback\n if (droppableElement !== this.state.lastHoveredElement) {\n // Remove hover from previous element\n if (this.state.lastHoveredElement) {\n this.state.lastHoveredElement.removeAttribute('data-hovered')\n }\n\n // Add hover to current element (including the source element).\n // This enables \"drag over itself\" states (e.g. combined data-dragging + data-hovered styling).\n if (droppableElement) {\n droppableElement.setAttribute('data-hovered', 'true')\n this.state.lastHoveredElement = droppableElement\n } else {\n this.state.lastHoveredElement = null\n }\n }\n\n // Auto-scroll\n this.handleAutoScroll(pos)\n\n // Notify drag move\n this.callbacks.onDragMove?.(pos, this.state.lastHoveredElement)\n })\n }\n }\n\n private handlePointerUp(e: PointerEvent): void {\n if (!this.isActivePointer(e)) return\n\n // Handle click/tap if we never started dragging\n if (this.state.startPosition && !this.state.isDragging) {\n const currentPos = { x: e.clientX, y: e.clientY }\n const distance = this.getDistance(this.state.startPosition, currentPos)\n\n // If distance is very small, treat as click\n if (\n distance < this.config.clickThreshold &&\n this.state.sourceElement &&\n this.state.sourcePosition\n ) {\n this.callbacks.onClick?.(this.state.sourceElement, this.state.sourcePosition)\n }\n\n this.cleanup()\n return\n }\n\n if (!this.state.isDragging || !this.state.sourcePosition || !this.state.sourceItem) {\n this.cleanup()\n return\n }\n\n // Find drop target\n const target = document.elementFromPoint(e.clientX, e.clientY) as HTMLElement\n const droppableElement = this.findElementByKind(target, this.config.droppableKind)\n\n if (droppableElement) {\n const targetPosition = this.callbacks.getItemPosition(\n droppableElement,\n this.config.droppableKind,\n )\n\n if (targetPosition) {\n // Notify drop\n this.callbacks.onDrop?.(this.state.sourcePosition, targetPosition, this.state.sourceItem)\n }\n }\n\n this.cleanup()\n }\n\n private handlePointerCancel(): void {\n this.cleanup()\n }\n\n private handleKeyDown(e: KeyboardEvent): void {\n if (!this.config.cancelOnEscape) return\n if (e.key !== 'Escape') return\n if (!this.state.startPosition) return\n this.cleanup()\n }\n\n private handleMouseOut(e: MouseEvent): void {\n if (!this.config.cancelOnPointerLeave) return\n if (!this.state.startPosition) return\n if (e.relatedTarget !== null) return\n this.cleanup()\n }\n\n private handleAutoScroll(pos: PointerPosition): void {\n const { scrollThreshold, scrollSpeed } = this.config\n const viewportWidth = window.innerWidth\n const viewportHeight = window.innerHeight\n\n let scrollX = 0\n let scrollY = 0\n\n // Check horizontal scroll\n if (pos.x < scrollThreshold) {\n scrollX = -scrollSpeed\n } else if (pos.x > viewportWidth - scrollThreshold) {\n scrollX = scrollSpeed\n }\n\n // Check vertical scroll\n if (pos.y < scrollThreshold) {\n scrollY = -scrollSpeed\n } else if (pos.y > viewportHeight - scrollThreshold) {\n scrollY = scrollSpeed\n }\n\n if (scrollX !== 0 || scrollY !== 0) {\n window.scrollBy(scrollX, scrollY)\n }\n }\n\n private cleanup(): void {\n // Release pointer capture if we own it.\n if (\n this.state.sourceElement &&\n this.state.activePointerId !== null &&\n typeof this.state.sourceElement.hasPointerCapture === 'function' &&\n typeof this.state.sourceElement.releasePointerCapture === 'function' &&\n this.state.sourceElement.hasPointerCapture(this.state.activePointerId)\n ) {\n try {\n this.state.sourceElement.releasePointerCapture(this.state.activePointerId)\n } catch {\n // Ignore release errors for inactive pointer.\n }\n }\n\n // Remove data-dragging attribute\n if (this.state.sourceElement) {\n this.state.sourceElement.removeAttribute('data-dragging')\n }\n\n // Remove hover attribute\n if (this.state.lastHoveredElement) {\n this.state.lastHoveredElement.removeAttribute('data-hovered')\n }\n\n // Cancel RAF\n if (this.rafId !== null) {\n cancelAnimationFrame(this.rafId)\n this.rafId = null\n }\n\n // Notify drag end\n if (this.state.isDragging) {\n this.callbacks.onDragEnd?.()\n }\n\n // Reset state\n this.state = {\n startPosition: null,\n sourceElement: null,\n sourcePosition: null,\n sourceItem: null,\n activePointerId: null,\n isDragging: false,\n lastHoveredElement: null,\n }\n this.lastPointerPos = null\n }\n\n private getPointerId(e: PointerEvent): number | null {\n return typeof e.pointerId === 'number' ? e.pointerId : null\n }\n\n private isActivePointer(e: PointerEvent): boolean {\n if (this.state.activePointerId === null) {\n return true\n }\n const pointerId = this.getPointerId(e)\n return pointerId === null || pointerId === this.state.activePointerId\n }\n\n /**\n * Find closest ancestor element with matching data-kind attribute\n */\n private findElementByKind(element: HTMLElement | null, kind: string): HTMLElement | null {\n let current = element\n while (current && current !== this.container) {\n if (current.dataset.kind === kind) {\n return current\n }\n current = current.parentElement\n }\n return null\n }\n\n /**\n * Calculate distance between two points\n */\n private getDistance(p1: PointerPosition, p2: PointerPosition): number {\n return Math.sqrt(Math.pow(p2.x - p1.x, 2) + Math.pow(p2.y - p1.y, 2))\n }\n\n /**\n * Get current drag state (read-only)\n */\n public getState(): Readonly<DragState<TItem, TPosition>> {\n return { ...this.state }\n }\n\n /**\n * Check if currently dragging\n */\n public isDragging(): boolean {\n return this.state.isDragging\n }\n\n /**\n * Cleanup and remove event listeners\n */\n public destroy(): void {\n this.cleanup()\n this.container.removeEventListener('pointerdown', this.boundHandlers.pointerDown)\n this.container.removeEventListener('pointermove', this.boundHandlers.pointerMove)\n this.container.removeEventListener('pointerup', this.boundHandlers.pointerUp)\n this.container.removeEventListener('pointercancel', this.boundHandlers.pointerCancel)\n window.removeEventListener('pointerup', this.boundHandlers.pointerUp)\n window.removeEventListener('pointercancel', this.boundHandlers.pointerCancel)\n window.removeEventListener('keydown', this.boundHandlers.keyDown)\n window.removeEventListener('mouseout', this.boundHandlers.mouseOut)\n }\n}\n","import type { PointerPosition } from './drag-drop-manager'\n\nexport type DragPreviewControllerOptions = {\n zIndex?: number\n opacity?: number\n centerOnCursor?: boolean\n className?: string | string[]\n copyComputedStyles?: string[]\n appendTo?: HTMLElement\n}\n\nconst OFFSCREEN_TRANSFORM = 'translate(-9999px, -9999px)'\n\nfunction normalizeClassNames(className: string | string[] | undefined): string[] {\n if (!className) return []\n return Array.isArray(className) ? className : [className]\n}\n\nexport class DragPreviewController {\n private options: Required<\n Pick<DragPreviewControllerOptions, 'zIndex' | 'opacity' | 'centerOnCursor'>\n > &\n Omit<DragPreviewControllerOptions, 'zIndex' | 'opacity' | 'centerOnCursor'>\n private previewElement: HTMLElement | null = null\n\n constructor(options: DragPreviewControllerOptions = {}) {\n this.options = {\n zIndex: options.zIndex ?? 9999,\n opacity: options.opacity ?? 0.95,\n centerOnCursor: options.centerOnCursor ?? true,\n className: options.className,\n copyComputedStyles: options.copyComputedStyles,\n appendTo: options.appendTo,\n }\n }\n\n public startFromElement(element: HTMLElement): void {\n this.stop()\n const rect = element.getBoundingClientRect()\n const clone = element.cloneNode(true) as HTMLElement\n const computed = window.getComputedStyle(element)\n const appendTo = this.options.appendTo ?? document.body\n\n clone.setAttribute('aria-hidden', 'true')\n clone.style.position = 'fixed'\n clone.style.left = '0'\n clone.style.top = '0'\n clone.style.width = `${rect.width}px`\n clone.style.height = `${rect.height}px`\n clone.style.pointerEvents = 'none'\n clone.style.zIndex = String(this.options.zIndex)\n clone.style.opacity = String(this.options.opacity)\n clone.style.transform = OFFSCREEN_TRANSFORM\n clone.style.transition = 'none'\n clone.style.cursor = 'grabbing'\n clone.style.margin = '0'\n clone.style.boxSizing = 'border-box'\n clone.style.background = computed.backgroundColor\n\n for (const classToken of normalizeClassNames(this.options.className)) {\n clone.classList.add(classToken)\n }\n\n if (this.options.copyComputedStyles && this.options.copyComputedStyles.length > 0) {\n for (const propertyName of this.options.copyComputedStyles) {\n clone.style.setProperty(propertyName, computed.getPropertyValue(propertyName))\n }\n }\n\n appendTo.appendChild(clone)\n this.previewElement = clone\n }\n\n public moveToPointer(pos: PointerPosition): void {\n if (!this.previewElement) return\n if (this.options.centerOnCursor) {\n this.previewElement.style.transform = `translate(${pos.x}px, ${pos.y}px) translate(-50%, -50%)`\n return\n }\n this.previewElement.style.transform = `translate(${pos.x}px, ${pos.y}px)`\n }\n\n public stop(): void {\n this.previewElement?.remove()\n this.previewElement = null\n }\n\n public destroy(): void {\n this.stop()\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/drag-drop-manager.ts","../src/drag-preview-controller.ts"],"sourcesContent":["/**\n * Framework-agnostic Drag and Drop Manager\n *\n * A data-driven drag-and-drop utility that works with React and vanilla JS.\n * Uses data attributes and direct DOM manipulation for optimal performance.\n *\n * @template TItem - The type of data associated with draggable items\n * @template TPosition - The type representing position/coordinates (e.g., {x, y}, {box, cell}, etc.)\n */\n\nexport type DragDropConfig = {\n /**\n * Minimum distance in pixels the pointer must move before a drag operation starts.\n * Prevents accidental drags from small movements and allows normal scrolling on mobile.\n * Checked during `pointermove` before drag has started.\n * @default 10\n * @example\n * dragThreshold: 15 // Harder to start drag (better for mobile scroll)\n */\n dragThreshold?: number\n\n /**\n * Maximum distance in pixels between pointerdown and pointerup to treat as a click/tap.\n * Used to distinguish intentional clicks from cancelled drag attempts.\n * Only checked on `pointerup` if drag never started.\n * @default 10\n * @example\n * clickThreshold: 5 // Stricter click detection\n * clickThreshold: 15 // More forgiving for mobile taps\n */\n clickThreshold?: number\n\n /**\n * Distance in pixels from viewport edge that triggers auto-scrolling during drag.\n * When dragging near screen edges, the window automatically scrolls.\n * @default 100\n * @example\n * scrollThreshold: 50 // Start scrolling closer to edges\n * scrollThreshold: 150 // Larger safe zone before scrolling\n */\n scrollThreshold?: number\n\n /**\n * Scroll speed in pixels per animation frame during auto-scroll.\n * Higher values = faster scrolling when dragging near viewport edges.\n * @default 10\n * @example\n * scrollSpeed: 5 // Slower, smoother scroll\n * scrollSpeed: 20 // Faster scroll\n */\n scrollSpeed?: number\n\n /**\n * Cancel the current drag operation when Escape is pressed.\n * Useful for explicit keyboard cancellation.\n * @default true\n */\n cancelOnEscape?: boolean\n\n /**\n * Cancel the current drag operation when the pointer leaves the browser window.\n * Prevents \"stuck dragging\" when pointerup happens outside the app/window.\n * @default true\n */\n cancelOnPointerLeave?: boolean\n\n /**\n * The `data-kind` value(s) for draggable elements.\n * Can be a single kind or multiple kinds.\n * Each draggable element should still expose a single `data-kind` value.\n * @example\n * draggableKind: 'cell' // <div data-kind=\"cell\">\n * draggableKind: ['quest', 'bounty'] // <div data-kind=\"quest\">\n * draggableKind: 'card' // <div data-kind=\"card\">\n * draggableKind: 'list-item' // <div data-kind=\"list-item\">\n */\n draggableKind: string | string[]\n\n /**\n * The `data-kind` value(s) for droppable target elements.\n * Can be a single kind or multiple kinds.\n * Elements with matching kinds can receive dropped items.\n * Each droppable element should still expose a single `data-kind` value.\n * Often the same as `draggableKind` for reorderable lists.\n * @example\n * droppableKind: 'cell' // Can drop on cells\n * droppableKind: ['quest', 'bounty'] // Can drop on either kind\n * droppableKind: 'zone' // Can drop on zones\n */\n droppableKind: string | string[]\n}\n\nexport type PointerPosition = {\n x: number\n y: number\n}\n\nexport type DragDropCallbacks<TItem, TPosition> = {\n /** Called when determining if an element can be dragged */\n canDrag?: (element: HTMLElement, position: TPosition) => boolean\n /** Called when drag starts (after threshold is met) */\n onDragStart?: (element: HTMLElement, position: TPosition, item: TItem) => void\n /** Called continuously during drag */\n onDragMove?: (pos: PointerPosition, hoveredElement: HTMLElement | null) => void\n /** Called when item is dropped on a valid target */\n onDrop?: (sourcePosition: TPosition, targetPosition: TPosition, sourceItem: TItem) => void\n /** Called when drag ends (regardless of success) */\n onDragEnd?: () => void\n /** Called on simple click/tap (no drag) */\n onClick?: (element: HTMLElement, position: TPosition) => void\n /** Called to extract item data from an element */\n getItemData?: (element: HTMLElement, position: TPosition) => TItem | null\n /** Called to get the custom position/coordinates of an element */\n getItemPosition: (element: HTMLElement, kind: string) => TPosition | null\n}\n\ntype DragState<TItem, TPosition> = {\n startPosition: PointerPosition | null\n sourceElement: HTMLElement | null\n sourcePosition: TPosition | null\n sourceItem: TItem | null\n activePointerId: number | null\n isDragging: boolean\n lastHoveredElement: HTMLElement | null\n}\n\nexport class DragDropManager<TItem = unknown, TPosition = unknown> {\n private container: HTMLElement\n private config: Required<DragDropConfig>\n private draggableKinds: string[]\n private droppableKinds: string[]\n private callbacks: DragDropCallbacks<TItem, TPosition>\n private state: DragState<TItem, TPosition> = {\n startPosition: null,\n sourceElement: null,\n sourcePosition: null,\n sourceItem: null,\n activePointerId: null,\n isDragging: false,\n lastHoveredElement: null,\n }\n private rafId: number | null = null\n private lastPointerPos: PointerPosition | null = null\n private boundHandlers: {\n pointerDown: (e: PointerEvent) => void\n pointerMove: (e: PointerEvent) => void\n pointerUp: (e: PointerEvent) => void\n pointerCancel: (e: PointerEvent) => void\n keyDown: (e: KeyboardEvent) => void\n mouseOut: (e: MouseEvent) => void\n }\n\n constructor(\n containerRef: { current: HTMLElement | null } | string | HTMLElement,\n config: DragDropConfig,\n callbacks: DragDropCallbacks<TItem, TPosition>,\n ) {\n // Resolve container element\n if (typeof containerRef === 'string') {\n const element = document.querySelector<HTMLElement>(containerRef)\n if (!element) {\n throw new Error(`DragDropManager: Could not find element with selector \"${containerRef}\"`)\n }\n this.container = element\n } else if (typeof containerRef === 'object' && 'current' in containerRef) {\n if (!containerRef.current) {\n throw new Error('DragDropManager: Ref.current is null')\n }\n this.container = containerRef.current\n } else {\n this.container = containerRef as HTMLElement\n }\n\n this.config = {\n dragThreshold: config.dragThreshold ?? 10,\n clickThreshold: config.clickThreshold ?? 10,\n scrollThreshold: config.scrollThreshold ?? 100,\n scrollSpeed: config.scrollSpeed ?? 10,\n cancelOnEscape: config.cancelOnEscape ?? true,\n cancelOnPointerLeave: config.cancelOnPointerLeave ?? true,\n draggableKind: config.draggableKind,\n droppableKind: config.droppableKind,\n }\n this.draggableKinds = this.normalizeKindList(this.config.draggableKind)\n this.droppableKinds = this.normalizeKindList(this.config.droppableKind)\n\n this.callbacks = callbacks\n\n // Bind handlers to preserve context\n this.boundHandlers = {\n pointerDown: this.handlePointerDown.bind(this),\n pointerMove: this.handlePointerMove.bind(this),\n pointerUp: this.handlePointerUp.bind(this),\n pointerCancel: this.handlePointerCancel.bind(this),\n keyDown: this.handleKeyDown.bind(this),\n mouseOut: this.handleMouseOut.bind(this),\n }\n\n this.attachListeners()\n }\n\n private attachListeners(): void {\n this.container.addEventListener('pointerdown', this.boundHandlers.pointerDown)\n this.container.addEventListener('pointermove', this.boundHandlers.pointerMove)\n this.container.addEventListener('pointerup', this.boundHandlers.pointerUp)\n this.container.addEventListener('pointercancel', this.boundHandlers.pointerCancel)\n window.addEventListener('pointerup', this.boundHandlers.pointerUp)\n window.addEventListener('pointercancel', this.boundHandlers.pointerCancel)\n window.addEventListener('keydown', this.boundHandlers.keyDown)\n window.addEventListener('mouseout', this.boundHandlers.mouseOut)\n }\n\n private handlePointerDown(e: PointerEvent): void {\n if (this.state.startPosition) return\n\n const target = e.target as HTMLElement\n const draggableMatch = this.findElementByKinds(target, this.draggableKinds)\n\n if (!draggableMatch) return\n\n const { element: draggableElement, kind: sourceKind } = draggableMatch\n\n const position = this.callbacks.getItemPosition(draggableElement, sourceKind)\n if (!position) return\n\n // Check if element is empty (convention: data-empty=\"any-value\")\n const isEmpty = draggableElement.dataset.empty !== undefined\n if (isEmpty) return\n\n // Check canDrag callback\n if (this.callbacks.canDrag && !this.callbacks.canDrag(draggableElement, position)) {\n return\n }\n\n // Get item data\n const item = this.callbacks.getItemData?.(draggableElement, position) ?? null\n if (!item) return\n\n // Prevent text selection during potential drag\n e.preventDefault()\n\n // Store start position and element info\n this.state.startPosition = { x: e.clientX, y: e.clientY }\n this.state.sourceElement = draggableElement\n this.state.sourcePosition = position\n this.state.sourceItem = item\n this.state.activePointerId = this.getPointerId(e)\n\n // Capture pointer events outside container boundaries when supported.\n if (\n this.state.activePointerId !== null &&\n typeof draggableElement.setPointerCapture === 'function'\n ) {\n try {\n draggableElement.setPointerCapture(this.state.activePointerId)\n } catch {\n // Ignore unsupported/inactive-pointer capture errors.\n }\n }\n }\n\n private handlePointerMove(e: PointerEvent): void {\n if (!this.isActivePointer(e)) return\n\n const currentPos: PointerPosition = { x: e.clientX, y: e.clientY }\n\n // Check if we should start dragging (threshold not yet met)\n if (this.state.startPosition && !this.state.isDragging) {\n const distance = this.getDistance(this.state.startPosition, currentPos)\n\n if (distance >= this.config.dragThreshold) {\n // Start drag\n this.state.isDragging = true\n\n if (this.state.sourceElement && this.state.sourcePosition && this.state.sourceItem) {\n // Set data-dragging attribute on source\n this.state.sourceElement.setAttribute('data-dragging', 'true')\n\n // Notify drag start\n this.callbacks.onDragStart?.(\n this.state.sourceElement,\n this.state.sourcePosition,\n this.state.sourceItem,\n )\n }\n }\n return\n }\n\n if (!this.state.isDragging) return\n\n // Store latest position for RAF\n this.lastPointerPos = currentPos\n\n // Throttle updates to 60fps using RAF\n if (this.rafId === null) {\n this.rafId = requestAnimationFrame(() => {\n this.rafId = null\n if (!this.lastPointerPos) return\n\n const pos = this.lastPointerPos\n\n // Find hovered droppable element\n const elementUnderCursor = document.elementFromPoint(pos.x, pos.y) as HTMLElement\n const droppableMatch = this.findElementByKinds(elementUnderCursor, this.droppableKinds)\n const droppableElement = droppableMatch?.element ?? null\n\n // Update hover feedback\n if (droppableElement !== this.state.lastHoveredElement) {\n // Remove hover from previous element\n if (this.state.lastHoveredElement) {\n this.state.lastHoveredElement.removeAttribute('data-hovered')\n }\n\n // Add hover to current element (including the source element).\n // This enables \"drag over itself\" states (e.g. combined data-dragging + data-hovered styling).\n if (droppableElement) {\n droppableElement.setAttribute('data-hovered', 'true')\n this.state.lastHoveredElement = droppableElement\n } else {\n this.state.lastHoveredElement = null\n }\n }\n\n // Auto-scroll\n this.handleAutoScroll(pos)\n\n // Notify drag move\n this.callbacks.onDragMove?.(pos, this.state.lastHoveredElement)\n })\n }\n }\n\n private handlePointerUp(e: PointerEvent): void {\n if (!this.isActivePointer(e)) return\n\n // Handle click/tap if we never started dragging\n if (this.state.startPosition && !this.state.isDragging) {\n const currentPos = { x: e.clientX, y: e.clientY }\n const distance = this.getDistance(this.state.startPosition, currentPos)\n\n // If distance is very small, treat as click\n if (\n distance < this.config.clickThreshold &&\n this.state.sourceElement &&\n this.state.sourcePosition\n ) {\n this.callbacks.onClick?.(this.state.sourceElement, this.state.sourcePosition)\n }\n\n this.cleanup()\n return\n }\n\n if (!this.state.isDragging || !this.state.sourcePosition || !this.state.sourceItem) {\n this.cleanup()\n return\n }\n\n // Find drop target\n const target = document.elementFromPoint(e.clientX, e.clientY) as HTMLElement\n const droppableMatch = this.findElementByKinds(target, this.droppableKinds)\n const droppableElement = droppableMatch?.element ?? null\n\n if (droppableElement && droppableMatch) {\n const targetPosition = this.callbacks.getItemPosition(droppableElement, droppableMatch.kind)\n\n if (targetPosition) {\n // Notify drop\n this.callbacks.onDrop?.(this.state.sourcePosition, targetPosition, this.state.sourceItem)\n }\n }\n\n this.cleanup()\n }\n\n private handlePointerCancel(): void {\n this.cleanup()\n }\n\n private handleKeyDown(e: KeyboardEvent): void {\n if (!this.config.cancelOnEscape) return\n if (e.key !== 'Escape') return\n if (!this.state.startPosition) return\n this.cleanup()\n }\n\n private handleMouseOut(e: MouseEvent): void {\n if (!this.config.cancelOnPointerLeave) return\n if (!this.state.startPosition) return\n if (e.relatedTarget !== null) return\n this.cleanup()\n }\n\n private handleAutoScroll(pos: PointerPosition): void {\n const { scrollThreshold, scrollSpeed } = this.config\n const viewportWidth = window.innerWidth\n const viewportHeight = window.innerHeight\n\n let scrollX = 0\n let scrollY = 0\n\n // Check horizontal scroll\n if (pos.x < scrollThreshold) {\n scrollX = -scrollSpeed\n } else if (pos.x > viewportWidth - scrollThreshold) {\n scrollX = scrollSpeed\n }\n\n // Check vertical scroll\n if (pos.y < scrollThreshold) {\n scrollY = -scrollSpeed\n } else if (pos.y > viewportHeight - scrollThreshold) {\n scrollY = scrollSpeed\n }\n\n if (scrollX !== 0 || scrollY !== 0) {\n window.scrollBy(scrollX, scrollY)\n }\n }\n\n private cleanup(): void {\n // Release pointer capture if we own it.\n if (\n this.state.sourceElement &&\n this.state.activePointerId !== null &&\n typeof this.state.sourceElement.hasPointerCapture === 'function' &&\n typeof this.state.sourceElement.releasePointerCapture === 'function' &&\n this.state.sourceElement.hasPointerCapture(this.state.activePointerId)\n ) {\n try {\n this.state.sourceElement.releasePointerCapture(this.state.activePointerId)\n } catch {\n // Ignore release errors for inactive pointer.\n }\n }\n\n // Remove data-dragging attribute\n if (this.state.sourceElement) {\n this.state.sourceElement.removeAttribute('data-dragging')\n }\n\n // Remove hover attribute\n if (this.state.lastHoveredElement) {\n this.state.lastHoveredElement.removeAttribute('data-hovered')\n }\n\n // Cancel RAF\n if (this.rafId !== null) {\n cancelAnimationFrame(this.rafId)\n this.rafId = null\n }\n\n // Notify drag end\n if (this.state.isDragging) {\n this.callbacks.onDragEnd?.()\n }\n\n // Reset state\n this.state = {\n startPosition: null,\n sourceElement: null,\n sourcePosition: null,\n sourceItem: null,\n activePointerId: null,\n isDragging: false,\n lastHoveredElement: null,\n }\n this.lastPointerPos = null\n }\n\n private getPointerId(e: PointerEvent): number | null {\n return typeof e.pointerId === 'number' ? e.pointerId : null\n }\n\n private isActivePointer(e: PointerEvent): boolean {\n if (this.state.activePointerId === null) {\n return true\n }\n const pointerId = this.getPointerId(e)\n return pointerId === null || pointerId === this.state.activePointerId\n }\n\n private normalizeKindList(kinds: string | string[]): string[] {\n const values = Array.isArray(kinds) ? kinds : [kinds]\n return [...new Set(values.map((value) => value.trim()).filter(Boolean))]\n }\n\n /**\n * Find closest ancestor element with a `data-kind` matching any expected kind.\n */\n private findElementByKinds(\n element: HTMLElement | null,\n kinds: string[],\n ): { element: HTMLElement; kind: string } | null {\n let current = element\n while (current && current !== this.container) {\n const elementKind = current.dataset.kind\n if (elementKind && kinds.includes(elementKind)) {\n return { element: current, kind: elementKind }\n }\n current = current.parentElement\n }\n return null\n }\n\n /**\n * Calculate distance between two points\n */\n private getDistance(p1: PointerPosition, p2: PointerPosition): number {\n return Math.sqrt(Math.pow(p2.x - p1.x, 2) + Math.pow(p2.y - p1.y, 2))\n }\n\n /**\n * Get current drag state (read-only)\n */\n public getState(): Readonly<DragState<TItem, TPosition>> {\n return { ...this.state }\n }\n\n /**\n * Check if currently dragging\n */\n public isDragging(): boolean {\n return this.state.isDragging\n }\n\n /**\n * Cleanup and remove event listeners\n */\n public destroy(): void {\n this.cleanup()\n this.container.removeEventListener('pointerdown', this.boundHandlers.pointerDown)\n this.container.removeEventListener('pointermove', this.boundHandlers.pointerMove)\n this.container.removeEventListener('pointerup', this.boundHandlers.pointerUp)\n this.container.removeEventListener('pointercancel', this.boundHandlers.pointerCancel)\n window.removeEventListener('pointerup', this.boundHandlers.pointerUp)\n window.removeEventListener('pointercancel', this.boundHandlers.pointerCancel)\n window.removeEventListener('keydown', this.boundHandlers.keyDown)\n window.removeEventListener('mouseout', this.boundHandlers.mouseOut)\n }\n}\n","import type { PointerPosition } from './drag-drop-manager'\n\nexport type DragPreviewControllerOptions = {\n zIndex?: number\n opacity?: number\n centerOnCursor?: boolean\n className?: string | string[]\n copyComputedStyles?: string[]\n appendTo?: HTMLElement\n}\n\nconst OFFSCREEN_TRANSFORM = 'translate(-9999px, -9999px)'\n\nfunction normalizeClassNames(className: string | string[] | undefined): string[] {\n if (!className) return []\n return Array.isArray(className) ? className : [className]\n}\n\nexport class DragPreviewController {\n private options: Required<\n Pick<DragPreviewControllerOptions, 'zIndex' | 'opacity' | 'centerOnCursor'>\n > &\n Omit<DragPreviewControllerOptions, 'zIndex' | 'opacity' | 'centerOnCursor'>\n private previewElement: HTMLElement | null = null\n\n constructor(options: DragPreviewControllerOptions = {}) {\n this.options = {\n zIndex: options.zIndex ?? 9999,\n opacity: options.opacity ?? 0.95,\n centerOnCursor: options.centerOnCursor ?? true,\n className: options.className,\n copyComputedStyles: options.copyComputedStyles,\n appendTo: options.appendTo,\n }\n }\n\n public startFromElement(element: HTMLElement): void {\n this.stop()\n const rect = element.getBoundingClientRect()\n const clone = element.cloneNode(true) as HTMLElement\n const computed = window.getComputedStyle(element)\n const appendTo = this.options.appendTo ?? document.body\n\n clone.setAttribute('aria-hidden', 'true')\n clone.style.position = 'fixed'\n clone.style.left = '0'\n clone.style.top = '0'\n clone.style.width = `${rect.width}px`\n clone.style.height = `${rect.height}px`\n clone.style.pointerEvents = 'none'\n clone.style.zIndex = String(this.options.zIndex)\n clone.style.opacity = String(this.options.opacity)\n clone.style.transform = OFFSCREEN_TRANSFORM\n clone.style.transition = 'none'\n clone.style.cursor = 'grabbing'\n clone.style.margin = '0'\n clone.style.boxSizing = 'border-box'\n clone.style.background = computed.backgroundColor\n\n for (const classToken of normalizeClassNames(this.options.className)) {\n clone.classList.add(classToken)\n }\n\n if (this.options.copyComputedStyles && this.options.copyComputedStyles.length > 0) {\n for (const propertyName of this.options.copyComputedStyles) {\n clone.style.setProperty(propertyName, computed.getPropertyValue(propertyName))\n }\n }\n\n appendTo.appendChild(clone)\n this.previewElement = clone\n }\n\n public moveToPointer(pos: PointerPosition): void {\n if (!this.previewElement) return\n if (this.options.centerOnCursor) {\n this.previewElement.style.transform = `translate(${pos.x}px, ${pos.y}px) translate(-50%, -50%)`\n return\n }\n this.previewElement.style.transform = `translate(${pos.x}px, ${pos.y}px)`\n }\n\n public stop(): void {\n this.previewElement?.remove()\n this.previewElement = null\n }\n\n public destroy(): void {\n this.stop()\n }\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dnd-manager",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"homepage": "https://itsjavi.com/dnd-manager",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -27,9 +27,7 @@
|
|
|
27
27
|
"@vitest/coverage-v8": "^4.0.18",
|
|
28
28
|
"jsdom": "^28.1.0",
|
|
29
29
|
"oxfmt": "^0.35.0",
|
|
30
|
-
"prettier": "^3.8.1",
|
|
31
30
|
"publint": "^0.3.17",
|
|
32
|
-
"sort-package-json": "^3.6.1",
|
|
33
31
|
"tsdown": "^0.20.3",
|
|
34
32
|
"typescript": "^5.9.3",
|
|
35
33
|
"vitest": "^4.0.18"
|