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.ts103
1 files changed, 61 insertions, 42 deletions
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index fda505420..81ea840f1 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -1,4 +1,3 @@
-/* eslint-disable import/no-mutable-exports */
/* eslint-disable no-use-before-define */
/**
* The DragManager handles all dragging interactions that occur entirely within Dash (as opposed to external drag operations from the file system, etc)
@@ -23,13 +22,14 @@ import { DocData } from '../../fields/DocSymbols';
import { List } from '../../fields/List';
import { PrefetchProxy } from '../../fields/Proxy';
import { ScriptField } from '../../fields/ScriptField';
-import { ScriptCast } from '../../fields/Types';
+import { ScriptCast, StrCast } from '../../fields/Types';
import { Docs } from '../documents/Documents';
import { DocumentView } from '../views/nodes/DocumentView';
import { dropActionType } from './DropActionTypes';
import { SnappingManager } from './SnappingManager';
import { UndoManager } from './UndoManager';
+// eslint-disable-next-line @typescript-eslint/no-require-imports
const { contextMenuZindex } = require('../views/global/globalCssVariables.module.scss'); // prettier-ignore
/**
@@ -78,7 +78,7 @@ export namespace DragManager {
export let CompleteWindowDrag: Opt<(aborted: boolean) => void>;
export let AbortDrag: () => void = emptyFunction;
export const docsBeingDragged: Doc[] = observable([]);
- export let DocDragData: DocumentDragData | undefined;
+ export let DraggedDocs: Doc[] | undefined;
export function Root() {
const root = document.getElementById('root');
@@ -101,7 +101,6 @@ export namespace DragManager {
// event called when the drag operation results in a drop action
export class DropEvent {
- // eslint-disable-next-line no-useless-constructor
constructor(
readonly x: number,
readonly y: number,
@@ -118,7 +117,7 @@ export namespace DragManager {
// event called when the drag operation has completed (aborted or completed a drop) -- this will be after any drop event has been generated
export class DragCompleteEvent {
- constructor(aborted: boolean, dragData: { [id: string]: any }) {
+ constructor(aborted: boolean, dragData: DocumentDragData | AnchorAnnoDragData | LinkDragData | ColumnDragData) {
this.aborted = aborted;
this.docDragData = dragData instanceof DocumentDragData ? dragData : undefined;
this.annoDragData = dragData instanceof AnchorAnnoDragData ? dragData : undefined;
@@ -167,6 +166,9 @@ export namespace DragManager {
linkSourceGetAnchor: () => Doc;
linkSourceDoc?: Doc;
linkDragView: DocumentView;
+ get canEmbed() {
+ return true;
+ }
}
export class ColumnDragData {
// constructor(colKey: SchemaHeaderField) {
@@ -177,6 +179,9 @@ export namespace DragManager {
this.colIndex = colIndex;
}
colIndex: number;
+ get canEmbed() {
+ return true;
+ }
}
// used by PDFs,Text,Image,Video,Web to conditionally (if the drop completes) create a text annotation when dragging the annotate button from the AnchorMenu when a text/region selection has been made.
// this is pretty clunky and should be rethought out using linkDrag or DocumentDrag
@@ -191,6 +196,9 @@ export namespace DragManager {
offset: number[];
dropAction?: dropActionType;
userDropAction?: dropActionType;
+ get canEmbed() {
+ return true;
+ }
}
const defaultPreDropFunc = (e: Event, de: DragManager.DropEvent, targetAction: dropActionType) => {
@@ -208,7 +216,7 @@ 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, doc.dropAction as any as dropActionType);
+ (preDropFunc ?? defaultPreDropFunc)(e, de, StrCast(doc.dropAction) as dropActionType);
};
element.addEventListener('dashOnDrop', handler);
element.addEventListener('dashPreDrop', preDropHandler);
@@ -220,8 +228,8 @@ export namespace DragManager {
}
// drag a document and drop it (or make an embed/copy on drop)
- export function StartDocumentDrag(eles: HTMLElement[], dragData: DocumentDragData, downX: number, downY: number, options?: DragOptions, onDropCompleted?: (e?: DragCompleteEvent) => any) {
- const addAudioTag = (dropDoc: any) => {
+ export function StartDocumentDrag(eles: HTMLElement[], dragData: DocumentDragData, downX: number, downY: number, options?: DragOptions, onDropCompleted?: (e?: DragCompleteEvent) => unknown) {
+ const addAudioTag = (dropDoc: Doc) => {
dropDoc && !dropDoc.author_date && (dropDoc.author_date = new DateField());
dropDoc instanceof Doc && CreateLinkToActiveAudio(() => dropDoc);
return dropDoc;
@@ -236,7 +244,7 @@ export namespace DragManager {
await Promise.all(
dragData.draggedDocuments.map(async d =>
!dragData.isDocDecorationMove && !dragData.userDropAction && ScriptCast(d.onDragStart)
- ? addAudioTag(ScriptCast(d.onDragStart).script.run({ this: d }).result)
+ ? addAudioTag(ScriptCast(d.onDragStart).script.run({ this: d }).result as Doc)
: docDragData.dropAction === dropActionType.embed
? Doc.BestEmbedding(d)
: docDragData.dropAction === dropActionType.add
@@ -249,7 +257,7 @@ export namespace DragManager {
)
)
).filter(d => d);
- ![dropActionType.same, dropActionType.proto].includes(docDragData.dropAction as any) &&
+ ![dropActionType.same, dropActionType.proto].includes(StrCast(docDragData.dropAction) as dropActionType) &&
docDragData.droppedDocuments
// .filter(drop => !drop.dragOnlyWithinContainer || ['embed', 'copy'].includes(docDragData.dropAction as any))
.forEach((drop: Doc, i: number) => {
@@ -376,9 +384,18 @@ export namespace DragManager {
options?.dragComplete?.(complete);
endDrag?.();
}
- export function StartDrag(elesIn: HTMLElement[], dragData: { [id: string]: any }, downX: number, downY: number, options?: DragOptions, finishDrag?: (dropData: DragCompleteEvent) => void, dragUndoName?: string) {
- if (dragData.dropAction === 'none' || SnappingManager.ExploreMode) return;
- DocDragData = dragData as DocumentDragData;
+ export function StartDrag(
+ elesIn: HTMLElement[],
+ dragData: DocumentDragData | LinkDragData | ColumnDragData | AnchorAnnoDragData,
+ downX: number,
+ downY: number,
+ options?: DragOptions,
+ finishDrag?: (dropData: DragCompleteEvent) => void,
+ dragUndoName?: string
+ ) {
+ if (SnappingManager.ExploreMode) return;
+ const docDragData = dragData instanceof DocumentDragData ? dragData : undefined;
+ DraggedDocs = docDragData?.draggedDocuments;
const batch = UndoManager.StartBatch(dragUndoName ?? 'document drag');
const eles = elesIn.filter(e => e);
SnappingManager.SetCanEmbed(dragData.canEmbed || false);
@@ -437,8 +454,9 @@ export namespace DragManager {
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'));
+ if (next.localName.startsWith('path')) {
+ const item = next.attributes.getNamedItem(field);
+ item && next.setAttribute(field, item.value.replace('#', '#X'));
}
});
if (next.localName.startsWith('marker')) {
@@ -495,7 +513,7 @@ export namespace DragManager {
.map((pb, i) => pb.getContext('2d')!.drawImage(pdfBoxSrc[i], 0, 0));
}
[dragElement, ...Array.from(dragElement.getElementsByTagName('*'))]
- .map(dele => (dele as any).style)
+ .map(dele => (dele as HTMLElement)?.style)
.forEach(style => {
style && (style.pointerEvents = 'none');
});
@@ -536,34 +554,35 @@ export namespace DragManager {
const yFromBottom = elesCont.bottom - downY;
let scrollAwaiter: Opt<NodeJS.Timeout>;
- let startWindowDragTimer: any;
+ let startWindowDragTimer: NodeJS.Timeout | undefined;
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 ? dropActionType.copy : e.shiftKey ? dropActionType.move : e.ctrlKey ? dropActionType.embed : dragData.defaultDropAction;
- }
- if (['lm_tab', 'lm_title_wrap', 'lm_tabs', 'lm_header'].includes(typeof (e.target as any).className === 'string' ? (e.target as any)?.className : '') && dragData.draggedDocuments.length === 1) {
- if (!startWindowDragTimer) {
- startWindowDragTimer = setTimeout(async () => {
- startWindowDragTimer = undefined;
- 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')) {
- dragData.removeDocument?.(dragData.draggedDocuments[0]);
- }
- });
- }, 500);
+ if (docDragData) {
+ docDragData.userDropAction = e.ctrlKey && e.altKey ? dropActionType.copy : e.shiftKey ? dropActionType.move : e.ctrlKey ? dropActionType.embed : docDragData.defaultDropAction;
+ const targClassName = e.target instanceof HTMLElement && typeof e.target.className === 'string' ? e.target.className : '';
+ if (['lm_tab', 'lm_title_wrap', 'lm_tabs', 'lm_header'].includes(targClassName) && docDragData.draggedDocuments.length === 1) {
+ if (!startWindowDragTimer) {
+ startWindowDragTimer = setTimeout(async () => {
+ startWindowDragTimer = undefined;
+ docDragData.dropAction = docDragData.userDropAction || dropActionType.same;
+ AbortDrag();
+ await finishDrag?.(new DragCompleteEvent(true, docDragData));
+ DragManager.StartWindowDrag?.(e, docDragData.droppedDocuments, aborted => {
+ if (!aborted && (docDragData?.dropAction === dropActionType.move || docDragData?.dropAction === dropActionType.same)) {
+ docDragData.removeDocument?.(docDragData?.draggedDocuments[0]);
+ }
+ });
+ }, 500);
+ }
+ } else {
+ clearTimeout(startWindowDragTimer);
+ startWindowDragTimer = undefined;
}
- } else {
- clearTimeout(startWindowDragTimer);
- startWindowDragTimer = undefined;
}
const target = document.elementFromPoint(e.x, e.y);
- if (target && !Doc.UserDoc()._noAutoscroll && !options?.noAutoscroll && !dragData.draggedDocuments?.some((d: any) => d._freeform_noAutoPan)) {
+ if (target && !Doc.UserDoc()._noAutoscroll && !options?.noAutoscroll && !(docDragData?.draggedDocuments as Doc[])?.some(d => d._freeform_noAutoPan)) {
const autoScrollHandler = () => {
target.dispatchEvent(
new CustomEvent<React.DragEvent>('dashDragMovePause', {
@@ -587,7 +606,7 @@ export namespace DragManager {
screenX: e.screenX,
screenY: e.screenY,
detail: e.detail,
- view: e.view ? e.view : (new Window() as any),
+ view: { ...(e.view ?? new Window()), styleMedia: { type: '', matchMedium: () => false } }, // bcz: Ugh.. this looks wrong
nativeEvent: new DragEvent('dashDragMovePause'),
currentTarget: target,
target: target,
@@ -596,10 +615,10 @@ export namespace DragManager {
defaultPrevented: true,
eventPhase: e.eventPhase,
isTrusted: true,
- preventDefault: () => 'not implemented for this event' && false,
- isDefaultPrevented: () => 'not implemented for this event' && false,
- stopPropagation: () => 'not implemented for this event' && false,
- isPropagationStopped: () => 'not implemented for this event' && false,
+ preventDefault: () => 'not implemented for this event',
+ isDefaultPrevented: () => false,
+ stopPropagation: () => 'not implemented for this event',
+ isPropagationStopped: () => false,
persist: emptyFunction,
timeStamp: e.timeStamp,
type: 'dashDragMovePause',