Skip to content

Commit 691bd2b

Browse files
author
Frank Schimmel
committed
Fix coordinate calculations.
Add marker for visual debugging.
1 parent 17a2e53 commit 691bd2b

File tree

1 file changed

+75
-36
lines changed

1 file changed

+75
-36
lines changed

src/html_dnd.ts

Lines changed: 75 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -53,23 +53,28 @@ namespace dnd {
5353
export class Simulation {
5454
private store: DragDataStore;
5555
private dataTransfer: DataTransfer;
56+
private marker: HTMLElement;
5657

57-
constructor(private draggable: Element, private droppable: Element) {
58+
constructor(private draggable: HTMLElement, private droppable: HTMLElement) {
5859
this.store = new DragDataStore();
5960
this.store.mode = "readwrite";
6061
this.dataTransfer = new DataTransfer(this.store);
62+
droppable.style.outline = "thin double #ffa1ce";
63+
this.marker = Simulation.createAndAttachMarker(droppable);
6164
}
6265

6366
dragStart() {
6467
dragStart(this.draggable, this.dataTransfer);
6568
}
6669

67-
dragOver() {
68-
dragOver(this.store, this.droppable, this.dataTransfer);
70+
dragOver(x: number, y: number) {
71+
const event = dragOver(this.store, this.droppable, this.dataTransfer, x, y);
72+
this.moveMarkerToEventPageCoordinates(event);
6973
}
7074

7175
dragOverElement(x: number, y: number) {
72-
dragOverElement(this.droppable, { x, y }, this.dataTransfer);
76+
const event = dragOverElement(this.droppable, x, y, this.dataTransfer);
77+
this.moveMarkerToEventPageCoordinates(event);
7378
}
7479

7580
drop(element?: Element) {
@@ -79,31 +84,65 @@ namespace dnd {
7984
endDrag() {
8085
endDrag(this.store, this.draggable, this.dataTransfer);
8186
}
87+
88+
private moveMarkerToEventPageCoordinates(event: MouseEvent) {
89+
this.marker.style.left = `${event.pageX}px`;
90+
this.marker.style.top = `${event.pageY}px`;
91+
}
92+
93+
private static createAndAttachMarker(droppable: HTMLElement): HTMLElement {
94+
const style = document.createElement("style");
95+
style.textContent = `
96+
#content_main {
97+
outline: thin double #ffa1ce;
98+
}
99+
#dnd-debug-marker {
100+
position: absolute;
101+
width: 20px;
102+
height: 20px;
103+
box-sizing: border-box;
104+
top: -9999px;
105+
left: -9999px;
106+
transform: translate(-50%, -50%);
107+
background: rgba(255, 0, 0, 0.2);
108+
border: thin solid rgba(255, 255, 255, 0.75);
109+
border-radius: 50%;
110+
z-index: 9999;
111+
}
112+
`;
113+
const body = (droppable as any).closest("body");
114+
const marker = document.createElement("div");
115+
marker.id = "dnd-debug-marker";
116+
body.appendChild(style);
117+
body.appendChild(marker);
118+
return marker;
119+
}
82120
}
83121

84122
function dragStart(draggable: Element, dataTransfer: DataTransfer): void {
85123
const dragstartEvent = createEventWithDataTransfer("dragstart", dataTransfer);
86124
draggable.dispatchEvent(dragstartEvent);
87125
}
88126

89-
function dragOver(store: DragDataStore, droppable: Element, dataTransfer: DataTransfer): void {
127+
function dragOver(store: DragDataStore, droppable: Element, dataTransfer: DataTransfer, xOffset: number = 0, yOffset: number = 0): DragEvent {
90128
// For the drop event. The list of items representing dragged data can be
91129
// read, including the data. No new data can be added.
92130
store.mode = "readonly";
93131
const dragEnterEvent = createEventWithDataTransfer("dragenter", dataTransfer);
94132
droppable.dispatchEvent(dragEnterEvent);
95133

96-
dragOverElement(droppable, { x: 0, y: 0 }, dataTransfer);
134+
return dragOverElement(droppable, xOffset, yOffset, dataTransfer);
97135
}
98136

99-
function dragOverElement(droppable: Element, offset: { x: number, y: number }, dataTransfer: DataTransfer) {
137+
function dragOverElement(droppable: Element, xOffset: number, yOffset: number, dataTransfer: DataTransfer): DragEvent {
100138
const dragOverEvent = createEventWithDataTransfer("dragover", dataTransfer);
101-
const {pageX, pageY} = elementCenterInPage(droppable);
139+
const { pageX, pageY } = coordinatesOfElementWithOffset(droppable, xOffset, yOffset);
102140
// We are assigning read-only properties here.
103141
// That only works because this is not a browser-created native event!
104-
dragOverEvent.pageX = pageX + offset.x;
105-
dragOverEvent.pageY = pageY + offset.y;
142+
dragOverEvent.pageX = pageX;
143+
dragOverEvent.pageY = pageY;
106144
droppable.dispatchEvent(dragOverEvent);
145+
return dragOverEvent;
107146
}
108147

109148
function drop(droppable: Element, dataTransfer: DataTransfer): void {
@@ -129,6 +168,15 @@ namespace dnd {
129168
}
130169

131170

171+
function coordinatesOfElementWithOffset(droppable: Element, xOffset: number, yOffset: number): { pageX: number, pageY: number } {
172+
const rect = droppable.getBoundingClientRect();
173+
const body = (droppable as any).closest("body");
174+
const bodyRect = body.getBoundingClientRect();
175+
const pageX = rect.left - bodyRect.left + xOffset;
176+
const pageY = rect.top - bodyRect.top + yOffset;
177+
return {pageX, pageY};
178+
}
179+
132180
/**
133181
* Creates an event instance with a DataTransfer.
134182
*/
@@ -139,15 +187,6 @@ namespace dnd {
139187
return event;
140188
}
141189

142-
function elementCenterInPage(droppable: Element) {
143-
let rect = droppable.getBoundingClientRect();
144-
return {
145-
pageX : window.scrollX + rect.left + rect.width / 2,
146-
pageY : window.scrollY + rect.top + rect.height / 2,
147-
};
148-
}
149-
150-
151190
type EventType = 'dragstart'
152191
| 'drag'
153192
| 'dragenter'
@@ -175,7 +214,7 @@ namespace dnd {
175214

176215
/**
177216
* Returns the kind of operation that is currently selected. If the kind of
178-
* operation isn't one of those that is allowed by the effectAllowed
217+
* operation isn"t one of those that is allowed by the effectAllowed
179218
* attribute, then the operation will fail.
180219
*
181220
* Can be set, to change the selected operation.
@@ -228,7 +267,7 @@ namespace dnd {
228267
// If the DataTransfer object is no longer associated with a drag data
229268
// store, return the empty string and abort these steps.
230269

231-
// If the drag data store's mode is in the protected mode, return the empty
270+
// If the drag data store"s mode is in the protected mode, return the empty
232271
// string and abort these steps.
233272
if (this.store.mode === "protected") {
234273
return "";
@@ -283,7 +322,7 @@ namespace dnd {
283322
return;
284323
}
285324

286-
// If the drag data store's mode is not the read/write mode, abort these
325+
// If the drag data store"s mode is not the read/write mode, abort these
287326
// steps. Nothing happens.
288327
if (this.store.mode !== "readwrite") {
289328
return;
@@ -304,7 +343,7 @@ namespace dnd {
304343
// Unicode string and whose type string is equal to format, if there is
305344
// one. Add an item to the drag data store item list whose kind is Plain
306345
// Unicode string, whose type string is equal to format, and whose data
307-
// is the string given by the method's second argument.
346+
// is the string given by the method"s second argument.
308347
this.typeTable[format] = data;
309348
this.types = Object.keys(this.typeTable);
310349
}
@@ -321,7 +360,7 @@ namespace dnd {
321360
return;
322361
}
323362

324-
// If the drag data store's mode is not the read/write mode, abort these
363+
// If the drag data store"s mode is not the read/write mode, abort these
325364
// steps. Nothing happens.
326365
if (this.store.mode !== "readwrite") {
327366
return;
@@ -332,7 +371,7 @@ namespace dnd {
332371
// these steps.
333372
if (typeof format === "undefined") {
334373
// Note: The clearData() method does not affect whether any files were
335-
// included in the drag, so the types attribute's list might still not
374+
// included in the drag, so the types attribute"s list might still not
336375
// be empty after calling clearData() (it would still contain the
337376
// "Files" string if any files were included in the drag).
338377
this.types.filter((type) => type !== "Files")
@@ -511,7 +550,7 @@ namespace dnd {
511550
if (typeof data === "string") {
512551
// If there is already an item in the drag data store item list whose
513552
// kind is Plain Unicode string and whose type string is equal to the
514-
// value of the method's second argument, converted to ASCII lowercase,
553+
// value of the method"s second argument, converted to ASCII lowercase,
515554
// then throw a NotSupportedError exception and abort these steps.
516555
const typeLowerCase = type.toLowerCase();
517556
if (this.typeTable[typeLowerCase]) {
@@ -520,8 +559,8 @@ namespace dnd {
520559

521560
// Otherwise, add an item to the drag data store item list whose kind is
522561
// Plain Unicode string, whose type string is equal to the value of the
523-
// method's second argument, converted to ASCII lowercase, and whose
524-
// data is the string given by the method's first argument.
562+
// method"s second argument, converted to ASCII lowercase, and whose
563+
// data is the string given by the method"s first argument.
525564
const stringItem = DataTransferItem.createForString(
526565
data, typeLowerCase, this.store);
527566
this.items.push(stringItem);
@@ -530,7 +569,7 @@ namespace dnd {
530569
else {
531570
// Add an item to the drag data store item list whose kind is File,
532571
// whose type string is the type of the File, converted to ASCII
533-
// lowercase, and whose data is the same as the File's data.
572+
// lowercase, and whose data is the same as the File"s data.
534573
const fileItem = DataTransferItem.createForFile(
535574
data, this.store);
536575
this.items.push(fileItem);
@@ -556,17 +595,17 @@ namespace dnd {
556595

557596

558597
/**
559-
* While the DataTransferItem object's DataTransfer object is associated with
560-
* a drag data store and that drag data store's drag data store item list
598+
* While the DataTransferItem object"s DataTransfer object is associated with
599+
* a drag data store and that drag data store"s drag data store item list
561600
* still contains the item that the DataTransferItem object represents, the
562-
* DataTransferItem object's mode is the same as the drag data store mode.
563-
* When the DataTransferItem object's DataTransfer object is not associated
601+
* DataTransferItem object"s mode is the same as the drag data store mode.
602+
* When the DataTransferItem object"s DataTransfer object is not associated
564603
* with a drag data store, or if the item that the DataTransferItem object
565604
* represents has been removed from the relevant drag data store item list,
566-
* the DataTransferItem object's mode is the disabled mode. The drag data
605+
* the DataTransferItem object"s mode is the disabled mode. The drag data
567606
* store referenced in this section (which is used only when the
568607
* DataTransferItem object is not in the disabled mode) is the drag data store
569-
* with which the DataTransferItem object's DataTransfer object is associated.
608+
* with which the DataTransferItem object"s DataTransfer object is associated.
570609
*
571610
* @see https://html.spec.whatwg.org/multipage/interaction.html#datatransferitem
572611
*/
@@ -711,7 +750,7 @@ namespace dnd {
711750
}
712751

713752
return textUriList.split(/\r\n/).filter((line) => {
714-
// Any lines beginning with the '#' character are comment lines
753+
// Any lines beginning with the "#" character are comment lines
715754
// and are ignored during processing.
716755
// The remaining non-comment lines shall be URIs (URNs or URLs),
717756
// encoded according to the URL or URN specifications (RFC2141,

0 commit comments

Comments
 (0)