aboutsummaryrefslogtreecommitdiff
path: root/src/client/util/DragManager.ts
diff options
context:
space:
mode:
authorSophie Zhang <sophie_zhang@brown.edu>2024-01-25 11:35:26 -0500
committerSophie Zhang <sophie_zhang@brown.edu>2024-01-25 11:35:26 -0500
commitf3dab2a56db5e4a6a3dca58185d94e1ff7d1dc32 (patch)
treea7bc895266b53bb620dbd2dd71bad2e83b555446 /src/client/util/DragManager.ts
parentb5c5410b4af5d2c68d2107d3f064f6e3ec4ac3f2 (diff)
parent136f3d9f349d54e8bdd73b6380ea47c19e5edebf (diff)
Merge branch 'master' into sophie-ai-images
Diffstat (limited to 'src/client/util/DragManager.ts')
-rw-r--r--src/client/util/DragManager.ts103
1 files changed, 60 insertions, 43 deletions
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index ea13eaa5b..a6ad0f1b3 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -4,15 +4,17 @@ import { Doc, Field, Opt, StrListCast } from '../../fields/Doc';
import { List } from '../../fields/List';
import { PrefetchProxy } from '../../fields/Proxy';
import { ScriptField } from '../../fields/ScriptField';
-import { BoolCast, ScriptCast, StrCast } from '../../fields/Types';
+import { 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 { CollectionFreeFormDocumentView } from '../views/nodes/CollectionFreeFormDocumentView';
import { DocumentView } from '../views/nodes/DocumentView';
import { ScriptingGlobals } from './ScriptingGlobals';
import { SelectionManager } from './SelectionManager';
import { SnappingManager } from './SnappingManager';
import { UndoManager } from './UndoManager';
+import { DocData } from '../../fields/DocSymbols';
+const { default : { contextMenuZindex } } = require('../views/global/globalCssVariables.module.scss'); // prettier-ignore
export type dropActionType = 'embed' | 'copy' | 'move' | 'add' | 'same' | 'inSame' | 'proto' | 'none' | undefined; // undefined = move, "same" = move but don't call dropPropertiesToRemove
@@ -57,7 +59,7 @@ export function SetupDrag(_reference: React.RefObject<HTMLElement>, docFunc: ()
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) => boolean>;
export let CompleteWindowDrag: Opt<(aborted: boolean) => void>;
export function Root() {
@@ -84,7 +86,16 @@ export namespace DragManager {
// 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
@@ -132,7 +143,7 @@ export namespace DragManager {
this.linkSourceGetAnchor = linkSourceGetAnchor;
}
get dragDocument() {
- return this.linkDragView.props.Document;
+ return this.linkDragView.Document;
}
linkSourceGetAnchor: () => Doc;
linkSourceDoc?: Doc;
@@ -170,7 +181,7 @@ export namespace DragManager {
}
};
- export function MakeDropTarget(element: HTMLElement, dropFunc: (e: Event, de: DropEvent) => void, doc?: Doc, preDropFunc?: (e: Event, de: DropEvent, targetAction: dropActionType) => void): DragDropDisposer {
+ 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");
}
@@ -178,13 +189,13 @@ export namespace DragManager {
const handler = (e: Event) => dropFunc(e, (e as CustomEvent<DropEvent>).detail);
const preDropHandler = (e: Event) => {
const de = (e as CustomEvent<DropEvent>).detail;
- (preDropFunc ?? defaultPreDropFunc)(e, de, StrCast(doc?.dropAction) as dropActionType);
+ (preDropFunc ?? defaultPreDropFunc)(e, de, StrCast(doc.dropAction) as dropActionType);
};
element.addEventListener('dashOnDrop', handler);
- doc && element.addEventListener('dashPreDrop', preDropHandler);
+ element.addEventListener('dashPreDrop', preDropHandler);
return () => {
element.removeEventListener('dashOnDrop', handler);
- doc && element.removeEventListener('dashPreDrop', preDropHandler);
+ element.removeEventListener('dashPreDrop', preDropHandler);
delete element.dataset.canDrop;
};
}
@@ -198,7 +209,7 @@ export namespace DragManager {
};
const finishDrag = async (e: DragCompleteEvent) => {
const docDragData = e.docDragData;
- setTimeout(() => dragData.draggedViews.forEach(view => view.props.CollectionFreeFormDocumentView?.().dragEnding()));
+ setTimeout(() => dragData.draggedViews.forEach(view => view.props.dragEnding?.()));
onDropCompleted?.(e); // glr: optional additional function to be called - in this case with presentation trails
if (docDragData && !docDragData.droppedDocuments.length) {
docDragData.dropAction = dragData.userDropAction || dragData.dropAction;
@@ -208,16 +219,14 @@ export namespace DragManager {
!dragData.isDocDecorationMove && !dragData.userDropAction && ScriptCast(d.onDragStart)
? addAudioTag(ScriptCast(d.onDragStart).script.run({ this: d }).result)
: docDragData.dropAction === 'embed'
- ? Doc.BestEmbedding(d)
- : docDragData.dropAction === 'add'
- ? d
- : docDragData.dropAction === 'proto'
- ? Doc.GetProto(d)
- : docDragData.dropAction === 'copy'
- ? (
- await Doc.MakeClone(d)
- ).clone
- : d
+ ? Doc.BestEmbedding(d)
+ : docDragData.dropAction === 'add'
+ ? d
+ : docDragData.dropAction === 'proto'
+ ? d[DocData]
+ : docDragData.dropAction === 'copy'
+ ? (await Doc.MakeClone(d)).clone
+ : d
)
)
).filter(d => d);
@@ -234,7 +243,7 @@ export namespace DragManager {
};
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);
- dragData.draggedViews.forEach(view => view.props.CollectionFreeFormDocumentView?.().dragStarting());
+ dragData.draggedViews.forEach(view => view.props.dragStarting?.());
return true;
}
@@ -242,9 +251,9 @@ export namespace DragManager {
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
+ params.map(p => Object.keys(vars).indexOf(p) !== -1 && (bd[DocData][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);
+ bd[DocData]['onClick-paramFieldKeys'] = new List<string>(params);
e.docDragData && (e.docDragData.droppedDocuments = [bd]);
return e;
};
@@ -260,7 +269,7 @@ export namespace DragManager {
// drags a linker button and creates a link on drop
export function StartLinkDrag(ele: HTMLElement, sourceView: DocumentView, sourceDocGetAnchor: undefined | ((addAsAnnotation: boolean) => Doc), downX: number, downY: number, options?: DragOptions) {
- StartDrag([ele], new DragManager.LinkDragData(sourceView, () => sourceDocGetAnchor?.(true) ?? sourceView.rootDoc), downX, downY, options);
+ StartDrag([ele], new DragManager.LinkDragData(sourceView, () => sourceDocGetAnchor?.(true) ?? sourceView.Document), downX, downY, options);
}
// drags a column from a schema view
@@ -286,14 +295,14 @@ export namespace DragManager {
const dist = Math.sqrt((dragx - x) * (dragx - x) + (dragy - y) * (dragy - y));
return { pt: [x, y], dist };
};
- SnappingManager.vertSnapLines().forEach((xCoord, i) => {
+ SnappingManager.VertSnapLines.forEach((xCoord, i) => {
const pt = intersect(dragPt[0], dragPt[1], dragPt[0] + snapAspect, dragPt[1] + 1, xCoord, -1, xCoord, 1, dragPt[0], dragPt[1]);
if (pt && pt.dist < closest) {
closest = pt.dist;
near = pt.pt;
}
});
- SnappingManager.horizSnapLines().forEach((yCoord, i) => {
+ SnappingManager.HorizSnapLines.forEach((yCoord, i) => {
const pt = intersect(dragPt[0], dragPt[1], dragPt[0] + snapAspect, dragPt[1] + 1, -1, yCoord, 1, yCoord, dragPt[0], dragPt[1]);
if (pt && pt.dist < closest) {
closest = pt.dist;
@@ -317,19 +326,19 @@ export namespace DragManager {
return drag;
};
return {
- x: snapVal([xFromLeft, xFromRight], e.pageX, SnappingManager.vertSnapLines()),
- y: snapVal([yFromTop, yFromBottom], e.pageY, SnappingManager.horizSnapLines()),
+ x: snapVal([xFromLeft, xFromRight], e.pageX, SnappingManager.VertSnapLines),
+ y: snapVal([yFromTop, yFromBottom], e.pageY, SnappingManager.HorizSnapLines),
};
}
- export let docsBeingDragged: Doc[] = observable([] as Doc[]);
+ export let docsBeingDragged: Doc[] = observable([]);
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, dragUndoName?: string) {
- if (dragData.dropAction === 'none' || DocumentView.ExploreMode) return;
+ if (dragData.dropAction === 'none' || SnappingManager.ExploreMode) return;
DocDragData = dragData as DocumentDragData;
const batch = UndoManager.StartBatch(dragUndoName ?? 'document drag');
eles = eles.filter(e => e);
- CanEmbed = dragData.canEmbed || false;
+ SnappingManager.SetCanEmbed(dragData.canEmbed || false);
if (!dragDiv) {
dragDiv = document.createElement('div');
dragDiv.className = 'dragManager-dragDiv';
@@ -360,11 +369,18 @@ export namespace DragManager {
const docsToDrag = dragData instanceof DocumentDragData ? dragData.draggedDocuments : dragData instanceof AnchorAnnoDragData ? [dragData.dragDocument] : [];
const dragElements = eles.map(ele => {
// bcz: very hacky -- if dragged element is a freeForm view with a rotation, then extract the rotation in order to apply it to the dragged element
- let useDim = false; // if doc is rotated by freeformview, then the dragged elements width and height won't reflect the unrotated dimensions, so we need to rely on the element knowing its own width/height. \
+ // bcz: used to be false, but that made dragging collection w/ native dim's not work...
+ let useDim = true; // if doc is rotated by freeformview, then the dragged elements width and height won't reflect the unrotated dimensions, so we need to rely on the element knowing its own width/height. \
// if the parent isn't a freeform view, then the element's width and height are presumed to match the acutal doc's dimensions (eg, dragging from import sidebar menu)
- if (ele?.parentElement?.parentElement?.parentElement?.className === 'collectionFreeFormDocumentView-container') {
- ele = ele.parentElement.parentElement.parentElement;
- rot.push(Number(ele.style.transform.replace(/.*rotate\(([-0-9.e]*)deg\).*/, '$1') || 0));
+ let rotation: number | undefined;
+ for (let parEle: HTMLElement | null | undefined = ele.parentElement; parEle; parEle = parEle?.parentElement) {
+ if (parEle.className === CollectionFreeFormDocumentView.CollectionFreeFormDocViewClassName) {
+ rotation = (rotation ?? 0) + Number(parEle.style.transform.replace(/.*rotate\(([-0-9.e]*)deg\).*/, '$1') || 0);
+ }
+ parEle = parEle.parentElement;
+ }
+ if (rotation !== undefined) {
+ rot.push(rotation);
} else {
useDim = true;
rot.push(0);
@@ -420,7 +436,7 @@ export namespace DragManager {
color: 'black',
transition: 'none',
borderRadius: getComputedStyle(ele).borderRadius,
- zIndex: globalCssVariables.contextMenuZindex,
+ zIndex: contextMenuZindex,
transformOrigin: '0 0',
width,
height,
@@ -455,7 +471,7 @@ export namespace DragManager {
runInAction(() => docsBeingDragged.push(...docsToDrag));
const hideDragShowOriginalElements = (hide: boolean) => {
- dragLabel.style.display = hide && !CanEmbed ? '' : 'none';
+ dragLabel.style.display = hide && !SnappingManager.CanEmbed ? '' : 'none';
!hide && dragElements.map(dragElement => dragElement.parentNode === dragDiv && dragDiv.removeChild(dragElement));
setTimeout(() => eles.forEach(ele => (ele.hidden = hide)));
};
@@ -475,13 +491,14 @@ export namespace DragManager {
};
const cleanupDrag = action((undo: boolean) => {
- (dragData as DocumentDragData).draggedViews?.forEach(view => view.props.CollectionFreeFormDocumentView?.().dragEnding());
+ (dragData as DocumentDragData).draggedViews?.forEach(view => view.props.dragEnding?.());
hideDragShowOriginalElements(false);
document.removeEventListener('pointermove', moveHandler, true);
document.removeEventListener('pointerup', upHandler, true);
SnappingManager.SetIsDragging(false);
if (batch.end() && undo) UndoManager.Undo();
docsBeingDragged.length = 0;
+ SnappingManager.SetCanEmbed(false);
});
var startWindowDragTimer: any;
const moveHandler = (e: PointerEvent) => {
@@ -489,7 +506,7 @@ export namespace DragManager {
if (dragData instanceof DocumentDragData) {
dragData.userDropAction = e.ctrlKey && e.altKey ? 'copy' : e.ctrlKey ? 'embed' : 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') && dragData.draggedDocuments.length === 1) {
if (!startWindowDragTimer) {
startWindowDragTimer = setTimeout(async () => {
startWindowDragTimer = undefined;
@@ -555,7 +572,7 @@ export namespace DragManager {
);
scrollAwaiter && clearTimeout(scrollAwaiter);
- SnappingManager.GetIsDragging() && (scrollAwaiter = setTimeout(autoScrollHandler, 25));
+ SnappingManager.IsDragging && (scrollAwaiter = setTimeout(autoScrollHandler, 25));
};
scrollAwaiter && clearTimeout(scrollAwaiter);
scrollAwaiter = setTimeout(autoScrollHandler, 250);
@@ -588,7 +605,7 @@ export namespace DragManager {
altKey: e.altKey,
metaKey: e.metaKey,
ctrlKey: e.ctrlKey,
- embedKey: CanEmbed,
+ embedKey: SnappingManager.CanEmbed,
},
};
target.dispatchEvent(new CustomEvent<DropEvent>('dashPreDrop', dropArgs));
@@ -602,7 +619,7 @@ export namespace DragManager {
ScriptingGlobals.add(function toggleRaiseOnDrag(readOnly?: boolean) {
if (readOnly) {
- return SelectionManager.Views().some(dv => dv.rootDoc.keepZWhenDragged);
+ return SelectionManager.Views.some(dv => dv.Document.keepZWhenDragged);
}
- SelectionManager.Views().map(dv => (dv.rootDoc.keepZWhenDragged = !dv.rootDoc.keepZWhenDragged));
+ SelectionManager.Views.map(dv => (dv.Document.keepZWhenDragged = !dv.Document.keepZWhenDragged));
});