aboutsummaryrefslogtreecommitdiff
path: root/src/client/util/DragManager.ts
diff options
context:
space:
mode:
authorNaafiyan Ahmed <naafiyan@gmail.com>2022-08-10 17:12:58 -0400
committerNaafiyan Ahmed <naafiyan@gmail.com>2022-08-10 17:12:58 -0400
commit9dae453967183b294bf4f7444b948023a1d52d39 (patch)
tree986f4a79b8c5b92013a70b5ecba704bbba4a7ff8 /src/client/util/DragManager.ts
parentc80d0763c87d1965f451bbd7b31d8da8228dc048 (diff)
parent513dcaa2d8c86f1cb5236ce89062cace6f418d1b (diff)
Merge branch 'master' into data-visualization-view-naafi
Diffstat (limited to 'src/client/util/DragManager.ts')
-rw-r--r--src/client/util/DragManager.ts341
1 files changed, 173 insertions, 168 deletions
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 09b463c2f..ccd94c56e 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -1,41 +1,35 @@
-import { action } from "mobx";
-import { DateField } from "../../fields/DateField";
-import { Doc, Field, Opt } from "../../fields/Doc";
-import { List } from "../../fields/List";
-import { PrefetchProxy } from "../../fields/Proxy";
-import { listSpec } from "../../fields/Schema";
-import { SchemaHeaderField } from "../../fields/SchemaHeaderField";
-import { ScriptField } from "../../fields/ScriptField";
-import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from "../../fields/Types";
-import { emptyFunction, Utils } from "../../Utils";
-import { Docs, DocUtils } from "../documents/Documents";
-import * as globalCssVariables from "../views/global/globalCssVariables.scss";
-import { DocumentView } from "../views/nodes/DocumentView";
-import { SnappingManager } from "./SnappingManager";
-import { UndoManager } from "./UndoManager";
-
-export type dropActionType = "alias" | "copy" | "move" | "same" | "proto" | "none" | undefined; // undefined = move, "same" = move but don't call removeDropProperties
+import { action, observable, runInAction } from 'mobx';
+import { DateField } from '../../fields/DateField';
+import { Doc, Field, Opt } from '../../fields/Doc';
+import { List } from '../../fields/List';
+import { PrefetchProxy } from '../../fields/Proxy';
+import { listSpec } from '../../fields/Schema';
+import { SchemaHeaderField } from '../../fields/SchemaHeaderField';
+import { ScriptField } from '../../fields/ScriptField';
+import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../fields/Types';
+import { emptyFunction, Utils } from '../../Utils';
+import { Docs, DocUtils } from '../documents/Documents';
+import * as globalCssVariables from '../views/global/globalCssVariables.scss';
+import { DocumentView } from '../views/nodes/DocumentView';
+import { SnappingManager } from './SnappingManager';
+import { UndoManager } from './UndoManager';
+
+export type dropActionType = 'alias' | 'copy' | 'move' | 'same' | 'proto' | 'none' | undefined; // undefined = move, "same" = move but don't call removeDropProperties
/**
* Initialize drag
- * @param _reference: The HTMLElement that is being dragged
+ * @param _reference: The HTMLElement that is being dragged
* @param docFunc: The Dash document being moved
* @param moveFunc: The function called when the document is moved
* @param dropAction: What to do with the document when it is dropped
* @param dragStarted: Method to call when the drag is started
*/
-export function SetupDrag(
- _reference: React.RefObject<HTMLElement>,
- docFunc: () => Doc | Promise<Doc> | undefined,
- moveFunc?: DragManager.MoveFunction,
- dropAction?: dropActionType,
- dragStarted?: () => void
-) {
+export function SetupDrag(_reference: React.RefObject<HTMLElement>, docFunc: () => Doc | Promise<Doc> | undefined, moveFunc?: DragManager.MoveFunction, dropAction?: dropActionType, dragStarted?: () => void) {
const onRowMove = async (e: PointerEvent) => {
e.stopPropagation();
e.preventDefault();
- document.removeEventListener("pointermove", onRowMove);
+ document.removeEventListener('pointermove', onRowMove);
document.removeEventListener('pointerup', onRowUp);
const doc = await docFunc();
if (doc) {
@@ -47,7 +41,7 @@ export function SetupDrag(
}
};
const onRowUp = (): void => {
- document.removeEventListener("pointermove", onRowMove);
+ document.removeEventListener('pointermove', onRowMove);
document.removeEventListener('pointerup', onRowUp);
};
const onItemDown = async (e: React.PointerEvent) => {
@@ -58,8 +52,8 @@ export function SetupDrag(
const dragDoc = await docFunc();
dragDoc && DragManager.StartWindowDrag?.(e, [dragDoc]);
} else {
- document.addEventListener("pointermove", onRowMove);
- document.addEventListener("pointerup", onRowUp);
+ document.addEventListener('pointermove', onRowMove);
+ document.addEventListener('pointerup', onRowUp);
}
}
};
@@ -69,15 +63,19 @@ export function SetupDrag(
export namespace DragManager {
let dragDiv: HTMLDivElement;
let dragLabel: HTMLDivElement;
- export let StartWindowDrag: Opt<((e: { pageX: number, pageY: number }, dragDocs: Doc[], finishDrag?: (aborted: boolean) => void) => void)>;
+ export let StartWindowDrag: Opt<(e: { pageX: number; pageY: number }, dragDocs: Doc[], finishDrag?: (aborted: boolean) => void) => void>;
export let CompleteWindowDrag: Opt<(aborted: boolean) => void>;
- export function GetRaiseWhenDragged() { return BoolCast(Doc.UserDoc()._raiseWhenDragged); }
- export function SetRaiseWhenDragged(val:boolean) { Doc.UserDoc()._raiseWhenDragged = val }
+ export function GetRaiseWhenDragged() {
+ return BoolCast(Doc.UserDoc()._raiseWhenDragged);
+ }
+ export function SetRaiseWhenDragged(val: boolean) {
+ Doc.UserDoc()._raiseWhenDragged = val;
+ }
export function Root() {
- const root = document.getElementById("root");
+ const root = document.getElementById('root');
if (!root) {
- throw new Error("No root element found");
+ throw new Error('No root element found');
}
return root;
}
@@ -85,27 +83,20 @@ export namespace DragManager {
export type MoveFunction = (document: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (document: Doc | Doc[]) => boolean) => boolean;
export type RemoveFunction = (document: Doc | Doc[]) => boolean;
- export interface DragDropDisposer { (): void; }
+ export interface DragDropDisposer {
+ (): void;
+ }
export interface DragOptions {
dragComplete?: (e: DragCompleteEvent) => void; // function to invoke when drag has completed
- hideSource?: boolean; // hide source document during drag
- offsetX?: number; // offset of top left of source drag visual from cursor
+ hideSource?: boolean; // hide source document during drag
+ offsetX?: number; // offset of top left of source drag visual from cursor
offsetY?: number;
noAutoscroll?: boolean;
}
// event called when the drag operation results in a drop action
export class DropEvent {
- constructor(
- readonly x: number,
- readonly y: number,
- readonly complete: DragCompleteEvent,
- readonly shiftKey: boolean,
- readonly altKey: boolean,
- readonly metaKey: boolean,
- readonly ctrlKey: boolean,
- readonly embedKey: boolean,
- ) { }
+ constructor(readonly x: number, readonly y: number, readonly complete: DragCompleteEvent, readonly shiftKey: boolean, readonly altKey: boolean, readonly metaKey: boolean, readonly ctrlKey: boolean, readonly embedKey: boolean) {}
}
// event called when the drag operation has completed (aborted or completed a drop) -- this will be after any drop event has been generated
@@ -137,20 +128,22 @@ export namespace DragManager {
treeViewDoc?: Doc;
offset: number[];
canEmbed?: boolean;
- userDropAction: dropActionType; // the user requested drop action -- this will be honored as specified by modifier keys
- defaultDropAction?: dropActionType; // an optionally specified default drop action when there is no user drop actionl - this will be honored if there is no user drop action
- dropAction: dropActionType; // a drop action request by the initiating code. the actual drop action may be different -- eg, if the request is 'alias', but the document is dropped within the same collection, the drop action will be switched to 'move'
+ userDropAction: dropActionType; // the user requested drop action -- this will be honored as specified by modifier keys
+ defaultDropAction?: dropActionType; // an optionally specified default drop action when there is no user drop actionl - this will be honored if there is no user drop action
+ dropAction: dropActionType; // a drop action request by the initiating code. the actual drop action may be different -- eg, if the request is 'alias', but the document is dropped within the same collection, the drop action will be switched to 'move'
removeDropProperties?: string[];
moveDocument?: MoveFunction;
removeDocument?: RemoveFunction;
isDocDecorationMove?: boolean; // Flags that Document decorations are used to drag document which allows suppression of onDragStart scripts
}
export class LinkDragData {
- constructor(dragView: DocumentView, linkSourceGetAnchor: () => Doc,) {
+ constructor(dragView: DocumentView, linkSourceGetAnchor: () => Doc) {
this.linkDragView = dragView;
this.linkSourceGetAnchor = linkSourceGetAnchor;
}
- get dragDocument() { return this.linkDragView.props.Document; }
+ get dragDocument() {
+ return this.linkDragView.props.Document;
+ }
linkSourceGetAnchor: () => Doc;
linkSourceDoc?: Doc;
linkDragView: DocumentView;
@@ -177,43 +170,29 @@ export namespace DragManager {
userDropAction: dropActionType;
}
- export function MakeDropTarget(
- element: HTMLElement,
- dropFunc: (e: Event, de: DropEvent) => void,
- doc?: Doc,
- preDropFunc?: (e: Event, de: DropEvent, targetAction: dropActionType) => void,
- ): DragDropDisposer {
- if ("canDrop" in element.dataset) {
- throw new Error(
- "Element is already droppable, can't make it droppable again"
- );
+ export function MakeDropTarget(element: HTMLElement, dropFunc: (e: Event, de: DropEvent) => void, doc?: Doc, preDropFunc?: (e: Event, de: DropEvent, targetAction: dropActionType) => void): DragDropDisposer {
+ if ('canDrop' in element.dataset) {
+ throw new Error("Element is already droppable, can't make it droppable again");
}
- element.dataset.canDrop = "true";
+ element.dataset.canDrop = 'true';
const handler = (e: Event) => dropFunc(e, (e as CustomEvent<DropEvent>).detail);
const preDropHandler = (e: Event) => {
const de = (e as CustomEvent<DropEvent>).detail;
preDropFunc?.(e, de, StrCast(doc?.targetDropAction) as dropActionType);
};
- element.addEventListener("dashOnDrop", handler);
- doc && element.addEventListener("dashPreDrop", preDropHandler);
+ element.addEventListener('dashOnDrop', handler);
+ doc && element.addEventListener('dashPreDrop', preDropHandler);
return () => {
- element.removeEventListener("dashOnDrop", handler);
- doc && element.removeEventListener("dashPreDrop", preDropHandler);
+ element.removeEventListener('dashOnDrop', handler);
+ doc && element.removeEventListener('dashPreDrop', preDropHandler);
delete element.dataset.canDrop;
};
}
// drag a document and drop it (or make an alias/copy on drop)
- export function StartDocumentDrag(
- eles: HTMLElement[],
- dragData: DocumentDragData,
- downX: number,
- downY: number,
- options?: DragOptions,
- dropEvent?: () => any
- ) {
+ export function StartDocumentDrag(eles: HTMLElement[], dragData: DocumentDragData, downX: number, downY: number, options?: DragOptions, dropEvent?: () => any) {
const addAudioTag = (dropDoc: any) => {
- dropDoc && !dropDoc.creationDate && (dropDoc.creationDate = new DateField);
+ dropDoc && !dropDoc.creationDate && (dropDoc.creationDate = new DateField());
dropDoc instanceof Doc && DocUtils.MakeLinkToActiveAudio(() => dropDoc);
return dropDoc;
};
@@ -222,19 +201,27 @@ export namespace DragManager {
dropEvent?.(); // glr: optional additional function to be called - in this case with presentation trails
if (docDragData && !docDragData.droppedDocuments.length) {
docDragData.dropAction = dragData.userDropAction || dragData.dropAction;
- docDragData.droppedDocuments =
- await Promise.all(dragData.draggedDocuments.map(async d =>
- !dragData.isDocDecorationMove && !dragData.userDropAction && ScriptCast(d.onDragStart) ?
- addAudioTag(ScriptCast(d.onDragStart).script.run({ this: d }).result) :
- docDragData.dropAction === "alias" ? Doc.MakeAlias(d) :
- docDragData.dropAction === "proto" ? Doc.GetProto(d) :
- docDragData.dropAction === "copy" ?
- (await Doc.MakeClone(d)).clone : d));
- !["same", "proto"].includes(docDragData.dropAction as any) && docDragData.droppedDocuments.forEach((drop: Doc, i: number) => {
- const dragProps = Cast(dragData.draggedDocuments[i].removeDropProperties, listSpec("string"), []);
- const remProps = (dragData?.removeDropProperties || []).concat(Array.from(dragProps));
- remProps.map(prop => drop[prop] = undefined);
- });
+ docDragData.droppedDocuments = await Promise.all(
+ dragData.draggedDocuments.map(async d =>
+ !dragData.isDocDecorationMove && !dragData.userDropAction && ScriptCast(d.onDragStart)
+ ? addAudioTag(ScriptCast(d.onDragStart).script.run({ this: d }).result)
+ : docDragData.dropAction === 'alias'
+ ? Doc.MakeAlias(d)
+ : docDragData.dropAction === 'proto'
+ ? Doc.GetProto(d)
+ : docDragData.dropAction === 'copy'
+ ? (
+ await Doc.MakeClone(d)
+ ).clone
+ : d
+ )
+ );
+ !['same', 'proto'].includes(docDragData.dropAction as any) &&
+ docDragData.droppedDocuments.forEach((drop: Doc, i: number) => {
+ const dragProps = Cast(dragData.draggedDocuments[i].removeDropProperties, listSpec('string'), []);
+ const remProps = (dragData?.removeDropProperties || []).concat(Array.from(dragProps));
+ remProps.map(prop => (drop[prop] = undefined));
+ });
}
return e;
};
@@ -243,23 +230,22 @@ export namespace DragManager {
return true;
}
- // drag a button template and drop a new button
- export function
- StartButtonDrag(eles: HTMLElement[], script: string, title: string, vars: { [name: string]: Field }, params: string[], initialize: (button: Doc) => void, downX: number, downY: number, options?: DragOptions) {
+ // drag a button template and drop a new button
+ export function StartButtonDrag(eles: HTMLElement[], script: string, title: string, vars: { [name: string]: Field }, params: string[], initialize: (button: Doc) => void, downX: number, downY: number, options?: DragOptions) {
const finishDrag = (e: DragCompleteEvent) => {
const bd = Docs.Create.ButtonDocument({ toolTip: title, z: 1, _width: 150, _height: 50, title, onClick: ScriptField.MakeScript(script) });
params.map(p => Object.keys(vars).indexOf(p) !== -1 && (Doc.GetProto(bd)[p] = new PrefetchProxy(vars[p] as Doc))); // copy all "captured" arguments into document parameterfields
initialize?.(bd);
- Doc.GetProto(bd)["onClick-paramFieldKeys"] = new List<string>(params);
+ Doc.GetProto(bd)['onClick-paramFieldKeys'] = new List<string>(params);
e.docDragData && (e.docDragData.droppedDocuments = [bd]);
return e;
};
options = options ?? {};
- options.noAutoscroll = true; // these buttons are being dragged on the overlay layer, so scrollin the underlay is not appropriate
+ options.noAutoscroll = true; // these buttons are being dragged on the overlay layer, so scrollin the underlay is not appropriate
StartDrag(eles, new DragManager.DocumentDragData([]), downX, downY, options, finishDrag);
}
- // drag&drop the pdf annotation anchor which will create a text note on drop via a dropCompleted() DragOption
+ // drag&drop the pdf annotation anchor which will create a text note on drop via a dropCompleted() DragOption
export function StartAnchorAnnoDrag(eles: HTMLElement[], dragData: AnchorAnnoDragData, downX: number, downY: number, options?: DragOptions) {
StartDrag(eles, dragData, downX, downY, options);
}
@@ -286,8 +272,8 @@ export namespace DragManager {
let near = dragPt;
const intersect = (x1: number, y1: number, x2: number, y2: number, x3: number, y3: number, x4: number, y4: number, dragx: number, dragy: number) => {
if ((x1 === x2 && y1 === y2) || (x3 === x4 && y3 === y4)) return undefined; // Check if none of the lines are of length 0
- const denominator = ((y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1));
- if (denominator === 0) return undefined; // Lines are parallel
+ const denominator = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1);
+ if (denominator === 0) return undefined; // Lines are parallel
const ua = ((x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3)) / denominator;
// let ub = ((x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3)) / denominator;
@@ -315,14 +301,14 @@ export namespace DragManager {
});
return { x: near[0], y: near[1] };
}
- // snap to the active snap lines - if oneAxis is set (eg, for maintaining aspect ratios), then it only snaps to the nearest horizontal/vertical line
+ // snap to the active snap lines - if oneAxis is set (eg, for maintaining aspect ratios), then it only snaps to the nearest horizontal/vertical line
export function snapDrag(e: PointerEvent, xFromLeft: number, yFromTop: number, xFromRight: number, yFromBottom: number) {
const snapThreshold = Utils.SNAP_THRESHOLD;
const snapVal = (pts: number[], drag: number, snapLines: number[]) => {
if (snapLines.length) {
- const offs = [pts[0], (pts[0] - pts[1]) / 2, -pts[1]]; // offsets from drag pt
+ const offs = [pts[0], (pts[0] - pts[1]) / 2, -pts[1]]; // offsets from drag pt
const rangePts = [drag - offs[0], drag - offs[1], drag - offs[2]]; // left, mid, right or top, mid, bottom pts to try to snap to snaplines
- const closestPts = rangePts.map(pt => snapLines.reduce((nearest, curr) => Math.abs(nearest - pt) > Math.abs(curr - pt) ? curr : nearest));
+ const closestPts = rangePts.map(pt => snapLines.reduce((nearest, curr) => (Math.abs(nearest - pt) > Math.abs(curr - pt) ? curr : nearest)));
const closestDists = rangePts.map((pt, i) => Math.abs(pt - closestPts[i]));
const minIndex = closestDists[0] < closestDists[1] && closestDists[0] < closestDists[2] ? 0 : closestDists[1] < closestDists[2] ? 1 : 2;
return closestDists[minIndex] < snapThreshold ? closestPts[minIndex] + offs[minIndex] : drag;
@@ -331,55 +317,61 @@ export namespace DragManager {
};
return {
x: snapVal([xFromLeft, xFromRight], e.pageX, SnappingManager.vertSnapLines()),
- y: snapVal([yFromTop, yFromBottom], e.pageY, SnappingManager.horizSnapLines())
+ y: snapVal([yFromTop, yFromBottom], e.pageY, SnappingManager.horizSnapLines()),
};
}
- export let docsBeingDragged: Doc[] = [];
+ export let docsBeingDragged: Doc[] = observable([] as Doc[]);
export let CanEmbed = false;
export let DocDragData: DocumentDragData | undefined;
export function StartDrag(eles: HTMLElement[], dragData: { [id: string]: any }, downX: number, downY: number, options?: DragOptions, finishDrag?: (dropData: DragCompleteEvent) => void) {
- if (dragData.dropAction === "none") return;
+ if (dragData.dropAction === 'none') return;
DocDragData = dragData instanceof DocumentDragData ? dragData : undefined;
- const batch = UndoManager.StartBatch("dragging");
+ const batch = UndoManager.StartBatch('dragging');
eles = eles.filter(e => e);
CanEmbed = dragData.canEmbed || false;
if (!dragDiv) {
- dragDiv = document.createElement("div");
- dragDiv.className = "dragManager-dragDiv";
- dragDiv.style.pointerEvents = "none";
- dragLabel = document.createElement("div");
- dragLabel.className = "dragManager-dragLabel";
- dragLabel.style.zIndex = "100001";
- dragLabel.style.fontSize = "10px";
- dragLabel.style.position = "absolute";
- dragLabel.innerText = "drag titlebar to embed on drop"; // bcz: need to move this to a status bar
+ dragDiv = document.createElement('div');
+ dragDiv.className = 'dragManager-dragDiv';
+ dragDiv.style.pointerEvents = 'none';
+ dragLabel = document.createElement('div');
+ dragLabel.className = 'dragManager-dragLabel';
+ dragLabel.style.zIndex = '100001';
+ dragLabel.style.fontSize = '10px';
+ dragLabel.style.position = 'absolute';
+ dragLabel.innerText = 'drag titlebar to embed on drop'; // bcz: need to move this to a status bar
dragDiv.appendChild(dragLabel);
DragManager.Root().appendChild(dragDiv);
}
- Object.assign(dragDiv.style, { width: "", height: "", overflow: "" });
+ Object.assign(dragDiv.style, { width: '', height: '', overflow: '' });
dragDiv.hidden = false;
- const scaleXs: number[] = [], scaleYs: number[] = [], xs: number[] = [], ys: number[] = [];
+ const scaleXs: number[] = [],
+ scaleYs: number[] = [],
+ xs: number[] = [],
+ ys: number[] = [];
- docsBeingDragged = dragData instanceof DocumentDragData ? dragData.draggedDocuments : dragData instanceof AnchorAnnoDragData ? [dragData.dragDocument] : [];
const elesCont = {
- left: Number.MAX_SAFE_INTEGER, right: Number.MIN_SAFE_INTEGER,
- top: Number.MAX_SAFE_INTEGER, bottom: Number.MIN_SAFE_INTEGER
+ left: Number.MAX_SAFE_INTEGER,
+ right: Number.MIN_SAFE_INTEGER,
+ top: Number.MAX_SAFE_INTEGER,
+ bottom: Number.MIN_SAFE_INTEGER,
};
+ const docsToDrag = dragData instanceof DocumentDragData ? dragData.draggedDocuments : dragData instanceof AnchorAnnoDragData ? [dragData.dragDocument] : [];
const dragElements = eles.map(ele => {
if (!ele.parentNode) dragDiv.appendChild(ele);
- const dragElement = ele.parentNode === dragDiv ? ele : ele.cloneNode(true) as HTMLElement;
+ const dragElement = ele.parentNode === dragDiv ? ele : (ele.cloneNode(true) as HTMLElement);
const children = Array.from(dragElement.children);
- while (children.length) { // need to replace all the maker node reference ids with new unique ids. otherwise, the clone nodes will reference the original nodes which are all hidden during the drag
+ while (children.length) {
+ // need to replace all the maker node reference ids with new unique ids. otherwise, the clone nodes will reference the original nodes which are all hidden during the drag
const next = children.pop();
next && children.push(...Array.from(next.children));
if (next) {
- ["marker-start", "marker-mid", "marker-end"].forEach(field => {
- if (next.localName.startsWith("path") && (next.attributes as any)[field]) {
- next.setAttribute(field, (next.attributes as any)[field].value.replace("#", "#X"));
+ ['marker-start', 'marker-mid', 'marker-end'].forEach(field => {
+ if (next.localName.startsWith('path') && (next.attributes as any)[field]) {
+ next.setAttribute(field, (next.attributes as any)[field].value.replace('#', '#X'));
}
});
- if (next.localName.startsWith("marker")) {
- next.id = "X" + next.id;
+ if (next.localName.startsWith('marker')) {
+ next.id = 'X' + next.id;
}
}
}
@@ -396,20 +388,31 @@ export namespace DragManager {
scaleXs.push(scaleX);
scaleYs.push(scaleY);
Object.assign(dragElement.style, {
- opacity: "0.7", position: "absolute", margin: "0", top: "0", bottom: "", left: "0", color: "black", transition: "none",
- borderRadius: getComputedStyle(ele).borderRadius, zIndex: globalCssVariables.contextMenuZindex,
- transformOrigin: "0 0", width: `${rect.width / scaleX}px`, height: `${rect.height / scaleY}px`,
+ opacity: '0.7',
+ position: 'absolute',
+ margin: '0',
+ top: '0',
+ bottom: '',
+ left: '0',
+ color: 'black',
+ transition: 'none',
+ borderRadius: getComputedStyle(ele).borderRadius,
+ zIndex: globalCssVariables.contextMenuZindex,
+ transformOrigin: '0 0',
+ width: `${rect.width / scaleX}px`,
+ height: `${rect.height / scaleY}px`,
transform: `translate(${rect.left + (options?.offsetX || 0)}px, ${rect.top + (options?.offsetY || 0)}px) scale(${scaleX}, ${scaleY})`,
});
dragLabel.style.transform = `translate(${rect.left + (options?.offsetX || 0)}px, ${rect.top + (options?.offsetY || 0) - 20}px)`;
- if (docsBeingDragged.length) {
- const pdfBox = dragElement.getElementsByTagName("canvas");
- const pdfBoxSrc = ele.getElementsByTagName("canvas");
- Array.from(pdfBox).filter(pb => pb.width && pb.height).map((pb, i) => pb.getContext('2d')!.drawImage(pdfBoxSrc[i], 0, 0));
+ if (docsToDrag.length) {
+ const pdfBox = dragElement.getElementsByTagName('canvas');
+ const pdfBoxSrc = ele.getElementsByTagName('canvas');
+ Array.from(pdfBox)
+ .filter(pb => pb.width && pb.height)
+ .map((pb, i) => pb.getContext('2d')!.drawImage(pdfBoxSrc[i], 0, 0));
}
- [dragElement, ...Array.from(dragElement.getElementsByTagName('*'))].forEach(ele =>
- (ele as any).style && ((ele as any).style.pointerEvents = "none"));
+ [dragElement, ...Array.from(dragElement.getElementsByTagName('*'))].forEach(ele => (ele as any).style && ((ele as any).style.pointerEvents = 'none'));
dragDiv.appendChild(dragElement);
if (dragElement !== ele) {
@@ -426,10 +429,12 @@ export namespace DragManager {
return dragElement;
});
+ runInAction(() => docsBeingDragged.push(...docsToDrag));
+
const hideDragShowOriginalElements = (hide: boolean) => {
- dragLabel.style.display = hide ? "" : "none";
+ dragLabel.style.display = hide ? '' : 'none';
!hide && dragElements.map(dragElement => dragElement.parentNode === dragDiv && dragDiv.removeChild(dragElement));
- eles.forEach(ele => ele.hidden = hide);
+ eles.forEach(ele => (ele.hidden = hide));
};
options?.hideSource && hideDragShowOriginalElements(true);
@@ -443,32 +448,34 @@ export namespace DragManager {
AbortDrag = () => {
options?.dragComplete?.(new DragCompleteEvent(true, dragData));
- cleanupDrag();
+ cleanupDrag(true);
};
- const cleanupDrag = action(() => {
+ const cleanupDrag = action((undo: boolean) => {
hideDragShowOriginalElements(false);
- document.removeEventListener("pointermove", moveHandler, true);
- document.removeEventListener("pointerup", upHandler);
+ document.removeEventListener('pointermove', moveHandler, true);
+ document.removeEventListener('pointerup', upHandler, true);
SnappingManager.SetIsDragging(false);
SnappingManager.clearSnapLines();
- batch.end();
+ const ended = batch.end();
+ if (undo && ended) UndoManager.Undo();
+ docsBeingDragged.length = 0;
});
var startWindowDragTimer: any;
const moveHandler = (e: PointerEvent) => {
e.preventDefault(); // required or dragging text menu link item ends up dragging the link button as native drag/drop
if (dragData instanceof DocumentDragData) {
- dragData.userDropAction = e.ctrlKey && e.altKey ? "copy" : e.ctrlKey ? "alias" : dragData.defaultDropAction;
+ dragData.userDropAction = e.ctrlKey && e.altKey ? 'copy' : e.ctrlKey ? 'alias' : dragData.defaultDropAction;
}
- if (((e.target as any)?.className === "lm_tabs" || (e.target as any)?.className === "lm_header" || e?.shiftKey) && dragData.draggedDocuments.length === 1) {
+ if (((e.target as any)?.className === 'lm_tabs' || (e.target as any)?.className === 'lm_header' || e?.shiftKey) && dragData.draggedDocuments.length === 1) {
if (!startWindowDragTimer) {
startWindowDragTimer = setTimeout(async () => {
startWindowDragTimer = undefined;
- dragData.dropAction = dragData.userDropAction || "same";
+ dragData.dropAction = dragData.userDropAction || 'same';
AbortDrag();
await finishDrag?.(new DragCompleteEvent(true, dragData));
- DragManager.StartWindowDrag?.(e, dragData.droppedDocuments, (aborted) => {
- if (!aborted && (dragData.dropAction === "move" || dragData.dropAction === "same")) {
+ DragManager.StartWindowDrag?.(e, dragData.droppedDocuments, aborted => {
+ if (!aborted && (dragData.dropAction === 'move' || dragData.dropAction === 'same')) {
dragData.removeDocument?.(dragData.draggedDocuments[0]);
}
});
@@ -484,7 +491,7 @@ export namespace DragManager {
if (target && !Doc.UserDoc()._noAutoscroll && !options?.noAutoscroll && !dragData.draggedDocuments?.some((d: any) => d._noAutoscroll)) {
const autoScrollHandler = () => {
target.dispatchEvent(
- new CustomEvent<React.DragEvent>("dashDragAutoScroll", {
+ new CustomEvent<React.DragEvent>('dashDragAutoScroll', {
bubbles: true,
detail: {
shiftKey: e.shiftKey,
@@ -493,7 +500,7 @@ export namespace DragManager {
ctrlKey: e.ctrlKey,
clientX: e.clientX,
clientY: e.clientY,
- dataTransfer: new DataTransfer,
+ dataTransfer: new DataTransfer(),
button: e.button,
buttons: e.buttons,
getModifierState: e.getModifierState,
@@ -505,8 +512,8 @@ export namespace DragManager {
screenX: e.screenX,
screenY: e.screenY,
detail: e.detail,
- view: e.view ? e.view : new Window as any,
- nativeEvent: new DragEvent("dashDragAutoScroll"),
+ view: e.view ? e.view : (new Window() as any),
+ nativeEvent: new DragEvent('dashDragAutoScroll'),
currentTarget: target,
target: target,
bubbles: true,
@@ -514,14 +521,14 @@ export namespace DragManager {
defaultPrevented: true,
eventPhase: e.eventPhase,
isTrusted: true,
- preventDefault: () => "not implemented for this event" ? false : false,
- isDefaultPrevented: () => "not implemented for this event" ? false : false,
- stopPropagation: () => "not implemented for this event" ? false : false,
- isPropagationStopped: () => "not implemented for this event" ? false : false,
+ preventDefault: () => ('not implemented for this event' ? false : false),
+ isDefaultPrevented: () => ('not implemented for this event' ? false : false),
+ stopPropagation: () => ('not implemented for this event' ? false : false),
+ isPropagationStopped: () => ('not implemented for this event' ? false : false),
persist: emptyFunction,
timeStamp: e.timeStamp,
- type: "dashDragAutoScroll"
- }
+ type: 'dashDragAutoScroll',
+ },
})
);
@@ -537,20 +544,18 @@ export namespace DragManager {
lastPt = { x, y };
dragLabel.style.transform = `translate(${xs[0] + moveVec.x + (options?.offsetX || 0)}px, ${ys[0] + moveVec.y + (options?.offsetY || 0) - 20}px)`;
- dragElements.map((dragElement, i) => (dragElement.style.transform =
- `translate(${(xs[i] += moveVec.x) + (options?.offsetX || 0)}px, ${(ys[i] += moveVec.y) + (options?.offsetY || 0)}px) scale(${scaleXs[i]}, ${scaleYs[i]})`)
- );
+ dragElements.map((dragElement, i) => (dragElement.style.transform = `translate(${(xs[i] += moveVec.x) + (options?.offsetX || 0)}px, ${(ys[i] += moveVec.y) + (options?.offsetY || 0)}px) scale(${scaleXs[i]}, ${scaleYs[i]})`));
};
const upHandler = (e: PointerEvent) => {
clearTimeout(startWindowDragTimer);
startWindowDragTimer = undefined;
- dispatchDrag(document.elementFromPoint(e.x, e.y) || document.body, e, new DragCompleteEvent(false, dragData), snapDrag(e, xFromLeft, yFromTop, xFromRight, yFromBottom), finishDrag, options, cleanupDrag);
+ dispatchDrag(document.elementFromPoint(e.x, e.y) || document.body, e, new DragCompleteEvent(false, dragData), snapDrag(e, xFromLeft, yFromTop, xFromRight, yFromBottom), finishDrag, options, () => cleanupDrag(false));
};
- document.addEventListener("pointermove", moveHandler, true);
- document.addEventListener("pointerup", upHandler);
+ document.addEventListener('pointermove', moveHandler, true);
+ document.addEventListener('pointerup', upHandler, true);
}
- async function dispatchDrag(target: Element, e: PointerEvent, complete: DragCompleteEvent, pos: { x: number, y: number }, finishDrag?: (e: DragCompleteEvent) => void, options?: DragOptions, endDrag?: () => void) {
+ async function dispatchDrag(target: Element, e: PointerEvent, complete: DragCompleteEvent, pos: { x: number; y: number }, finishDrag?: (e: DragCompleteEvent) => void, options?: DragOptions, endDrag?: () => void) {
const dropArgs = {
bubbles: true,
detail: {
@@ -560,13 +565,13 @@ export namespace DragManager {
altKey: e.altKey,
metaKey: e.metaKey,
ctrlKey: e.ctrlKey,
- embedKey: CanEmbed
- }
+ embedKey: CanEmbed,
+ },
};
- target.dispatchEvent(new CustomEvent<DropEvent>("dashPreDrop", dropArgs));
+ target.dispatchEvent(new CustomEvent<DropEvent>('dashPreDrop', dropArgs));
await finishDrag?.(complete);
- target.dispatchEvent(new CustomEvent<DropEvent>("dashOnDrop", dropArgs));
+ target.dispatchEvent(new CustomEvent<DropEvent>('dashOnDrop', dropArgs));
options?.dragComplete?.(complete);
endDrag?.();
}
-} \ No newline at end of file
+}