aboutsummaryrefslogtreecommitdiff
path: root/src/client/util/DragManager.ts
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/util/DragManager.ts')
-rw-r--r--src/client/util/DragManager.ts135
1 files changed, 105 insertions, 30 deletions
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 2a9c1633a..4b1860b5c 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -7,20 +7,18 @@ import { listSpec } from "../../fields/Schema";
import { SchemaHeaderField } from "../../fields/SchemaHeaderField";
import { ScriptField } from "../../fields/ScriptField";
import { Cast, NumCast, ScriptCast, StrCast } from "../../fields/Types";
-import { emptyFunction } from "../../Utils";
+import { emptyFunction, returnTrue } from "../../Utils";
import { Docs, DocUtils } from "../documents/Documents";
import * as globalCssVariables from "../views/globalCssVariables.scss";
import { UndoManager } from "./UndoManager";
import { SnappingManager } from "./SnappingManager";
-export type dropActionType = "alias" | "copy" | "move" | undefined; // undefined = move
+export type dropActionType = "alias" | "copy" | "move" | "same" | undefined; // undefined = move
export function SetupDrag(
_reference: React.RefObject<HTMLElement>,
docFunc: () => Doc | Promise<Doc> | undefined,
moveFunc?: DragManager.MoveFunction,
dropAction?: dropActionType,
- treeViewId?: string,
- dontHideOnDrop?: boolean,
dragStarted?: () => void
) {
const onRowMove = async (e: PointerEvent) => {
@@ -34,10 +32,8 @@ export function SetupDrag(
const dragData = new DragManager.DocumentDragData([doc]);
dragData.dropAction = dropAction;
dragData.moveDocument = moveFunc;
- dragData.treeViewId = treeViewId;
- dragData.dontHideOnDrop = dontHideOnDrop;
DragManager.StartDocumentDrag([_reference.current!], dragData, e.x, e.y);
- dragStarted && dragStarted();
+ dragStarted?.();
}
};
const onRowUp = (): void => {
@@ -67,6 +63,7 @@ export function SetupDrag(
export namespace DragManager {
let dragDiv: HTMLDivElement;
+ let dragLabel: HTMLDivElement;
export let StartWindowDrag: Opt<((e: any, dragDocs: Doc[]) => void)> = undefined;
export function Root() {
@@ -86,6 +83,7 @@ export namespace DragManager {
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
@@ -97,7 +95,8 @@ export namespace DragManager {
readonly shiftKey: boolean,
readonly altKey: boolean,
readonly metaKey: boolean,
- readonly ctrlKey: boolean
+ readonly ctrlKey: boolean,
+ readonly embedKey: boolean,
) { }
}
@@ -120,19 +119,18 @@ export namespace DragManager {
export class DocumentDragData {
constructor(dragDoc: Doc[]) {
this.draggedDocuments = dragDoc;
- this.droppedDocuments = dragDoc;
+ this.droppedDocuments = [];
this.offset = [0, 0];
}
draggedDocuments: Doc[];
droppedDocuments: Doc[];
dragDivName?: string;
- treeViewId?: string;
+ treeViewDoc?: Doc;
dontHideOnDrop?: boolean;
offset: number[];
dropAction: dropActionType;
removeDropProperties?: string[];
userDropAction: dropActionType;
- embedDoc?: boolean;
moveDocument?: MoveFunction;
removeDocument?: RemoveFunction;
isSelectionMove?: boolean; // indicates that an explicitly selected Document is being dragged. this will suppress onDragStart scripts
@@ -205,31 +203,39 @@ export namespace DragManager {
dropDoc instanceof Doc && DocUtils.MakeLinkToActiveAudio(dropDoc);
return dropDoc;
};
- const batch = UndoManager.StartBatch("dragging");
const finishDrag = (e: DragCompleteEvent) => {
- e.docDragData && (e.docDragData.droppedDocuments =
- dragData.draggedDocuments.map(d => !dragData.isSelectionMove && !dragData.userDropAction && ScriptCast(d.onDragStart) ? addAudioTag(ScriptCast(d.onDragStart).script.run({ this: d }).result) :
- dragData.userDropAction === "alias" || (!dragData.userDropAction && dragData.dropAction === "alias") ? Doc.MakeAlias(d) :
- dragData.userDropAction === "copy" || (!dragData.userDropAction && dragData.dropAction === "copy") ? Doc.MakeClone(d) : d)
- );
- e.docDragData?.droppedDocuments.forEach((drop: Doc, i: number) =>
- (dragData?.removeDropProperties || []).concat(Cast(dragData.draggedDocuments[i].removeDropProperties, listSpec("string"), [])).map(prop => drop[prop] = undefined)
- );
- batch.end();
+ const docDragData = e.docDragData;
+ if (docDragData && !docDragData.droppedDocuments.length) {
+ docDragData.dropAction = dragData.userDropAction || dragData.dropAction;
+ docDragData.droppedDocuments =
+ dragData.draggedDocuments.map(d => !dragData.isSelectionMove && !dragData.userDropAction && ScriptCast(d.onDragStart) ? addAudioTag(ScriptCast(d.onDragStart).script.run({ this: d }).result) :
+ docDragData.dropAction === "alias" ? Doc.MakeAlias(d) :
+ docDragData.dropAction === "copy" ? Doc.MakeDelegate(d) : d);
+ docDragData.dropAction !== "same" && 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;
};
dragData.draggedDocuments.map(d => d.dragFactory); // does this help? trying to make sure the dragFactory Doc is loaded
StartDrag(eles, dragData, downX, downY, options, finishDrag);
}
// 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) {
+ 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({ _width: 150, _height: 50, title, onClick: ScriptField.MakeScript(script) });
+ 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);
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
StartDrag(eles, new DragManager.DocumentDragData([]), downX, downY, options, finishDrag);
}
@@ -309,14 +315,25 @@ export namespace DragManager {
};
}
export let docsBeingDragged: Doc[] = [];
+ export let CanEmbed = false;
export function StartDrag(eles: HTMLElement[], dragData: { [id: string]: any }, downX: number, downY: number, options?: DragOptions, finishDrag?: (dropData: DragCompleteEvent) => void) {
+ const batch = UndoManager.StartBatch("dragging");
eles = eles.filter(e => e);
+ 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 = "10pt";
+ dragLabel.style.position = "absolute";
+ // dragLabel.innerText = "press 'a' to embed on drop"; // bcz: need to move this to a status bar
+ dragDiv.appendChild(dragLabel);
DragManager.Root().appendChild(dragDiv);
}
+ dragLabel.style.display = "";
SnappingManager.SetIsDragging(true);
const scaleXs: number[] = [];
const scaleYs: number[] = [];
@@ -335,7 +352,7 @@ export namespace DragManager {
const dragElement = ele.parentNode === dragDiv ? ele : ele.cloneNode(true) as HTMLElement;
const rect = ele.getBoundingClientRect();
const scaleX = rect.width / ele.offsetWidth,
- scaleY = rect.height / ele.offsetHeight;
+ scaleY = ele.offsetHeight ? rect.height / ele.offsetHeight : scaleX;
elesCont.left = Math.min(rect.left, elesCont.left);
elesCont.top = Math.min(rect.top, elesCont.top);
elesCont.right = Math.max(rect.right, elesCont.right);
@@ -358,6 +375,7 @@ export namespace DragManager {
dragElement.style.transform = `translate(${rect.left + (options?.offsetX || 0)}px, ${rect.top + (options?.offsetY || 0)}px) scale(${scaleX}, ${scaleY})`;
dragElement.style.width = `${rect.width / scaleX}px`;
dragElement.style.height = `${rect.height / scaleY}px`;
+ 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");
@@ -393,14 +411,14 @@ export namespace DragManager {
const yFromTop = downY - elesCont.top;
const xFromRight = elesCont.right - downX;
const yFromBottom = elesCont.bottom - downY;
- let alias = "alias";
+ let scrollAwaiter: Opt<NodeJS.Timeout>;
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" : undefined;
}
- if (e.shiftKey && dragData.droppedDocuments.length === 1) {
- !dragData.dropAction && (dragData.dropAction = alias);
+ if (e?.shiftKey && dragData.draggedDocuments.length === 1) {
+ dragData.dropAction = dragData.userDropAction || "same";
if (dragData.dropAction === "move") {
dragData.removeDocument?.(dragData.draggedDocuments[0]);
}
@@ -414,19 +432,73 @@ export namespace DragManager {
}, dragData.droppedDocuments);
}
+ const target = document.elementFromPoint(e.x, e.y);
+
+ if (target && !options?.noAutoscroll && !dragData.draggedDocuments?.some((d: any) => d._noAutoscroll)) {
+ const autoScrollHandler = () => {
+ target.dispatchEvent(
+ new CustomEvent<React.DragEvent>("dashDragAutoScroll", {
+ bubbles: true,
+ detail: {
+ shiftKey: e.shiftKey,
+ altKey: e.altKey,
+ metaKey: e.metaKey,
+ ctrlKey: e.ctrlKey,
+ clientX: e.clientX,
+ clientY: e.clientY,
+ dataTransfer: new DataTransfer,
+ button: e.button,
+ buttons: e.buttons,
+ getModifierState: e.getModifierState,
+ movementX: e.movementX,
+ movementY: e.movementY,
+ pageX: e.pageX,
+ pageY: e.pageY,
+ relatedTarget: e.relatedTarget,
+ screenX: e.screenX,
+ screenY: e.screenY,
+ detail: e.detail,
+ view: e.view ? e.view : new Window,
+ nativeEvent: new DragEvent("dashDragAutoScroll"),
+ currentTarget: target,
+ target: target,
+ bubbles: true,
+ cancelable: true,
+ 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,
+ persist: emptyFunction,
+ timeStamp: e.timeStamp,
+ type: "dashDragAutoScroll"
+ }
+ })
+ );
+
+ scrollAwaiter && clearTimeout(scrollAwaiter);
+ SnappingManager.GetIsDragging() && (scrollAwaiter = setTimeout(autoScrollHandler, 25));
+ };
+ scrollAwaiter && clearTimeout(scrollAwaiter);
+ scrollAwaiter = setTimeout(autoScrollHandler, 250);
+ }
+
const { thisX, thisY } = snapDrag(e, xFromLeft, yFromTop, xFromRight, yFromBottom);
- alias = "move";
const moveX = thisX - lastX;
const moveY = thisY - lastY;
lastX = thisX;
lastY = thisY;
+ dragLabel.style.transform = `translate(${xs[0] + moveX + (options?.offsetX || 0)}px, ${ys[0] + moveY + (options?.offsetY || 0) - 20}px)`;
dragElements.map((dragElement, i) => (dragElement.style.transform =
`translate(${(xs[i] += moveX) + (options?.offsetX || 0)}px, ${(ys[i] += moveY) + (options?.offsetY || 0)}px) scale(${scaleXs[i]}, ${scaleYs[i]})`)
);
};
const hideDragShowOriginalElements = () => {
+ dragLabel.style.display = "none";
dragElements.map(dragElement => dragElement.parentNode === dragDiv && dragDiv.removeChild(dragElement));
eles.map(ele => ele.parentElement && ele.parentElement?.className === dragData.dragDivName ? (ele.parentElement.hidden = false) : (ele.hidden = false));
};
@@ -434,6 +506,7 @@ export namespace DragManager {
document.removeEventListener("pointermove", moveHandler, true);
document.removeEventListener("pointerup", upHandler);
SnappingManager.clearSnapLines();
+ batch.end();
});
AbortDrag = () => {
@@ -481,7 +554,8 @@ export namespace DragManager {
shiftKey: e.shiftKey,
altKey: e.altKey,
metaKey: e.metaKey,
- ctrlKey: e.ctrlKey
+ ctrlKey: e.ctrlKey,
+ embedKey: CanEmbed
}
})
);
@@ -496,7 +570,8 @@ export namespace DragManager {
shiftKey: e.shiftKey,
altKey: e.altKey,
metaKey: e.metaKey,
- ctrlKey: e.ctrlKey
+ ctrlKey: e.ctrlKey,
+ embedKey: CanEmbed
}
})
);