import { action, observable, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import 'normalize.css';
import * as React from 'react';
import { Doc } from '../../fields/Doc';
import { Cast, NumCast, StrCast } from '../../fields/Types';
import { returnFalse } from '../../Utils';
import { DocServer } from '../DocServer';
import { Docs, DocUtils } from '../documents/Documents';
import { Transform } from '../util/Transform';
import { undoBatch, UndoManager } from '../util/UndoManager';
import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox';
import './PreviewCursor.scss';
@observer
export class PreviewCursor extends React.Component<{}> {
static _onKeyPress?: (e: KeyboardEvent) => void;
static _getTransform: () => Transform;
static _addDocument: (doc: Doc | Doc[]) => void;
static _addLiveTextDoc: (doc: Doc) => void;
static _nudge?: undefined | ((x: number, y: number) => boolean);
@observable static _clickPoint = [0, 0];
@observable public static Visible = false;
constructor(props: any) {
super(props);
document.addEventListener('keydown', this.onKeyPress);
document.addEventListener('paste', this.paste);
}
paste = async (e: ClipboardEvent) => {
if (PreviewCursor.Visible && e.clipboardData) {
const newPoint = PreviewCursor._getTransform().transformPoint(PreviewCursor._clickPoint[0], PreviewCursor._clickPoint[1]);
runInAction(() => (PreviewCursor.Visible = false));
// tests for URL and makes web document
const re: any = /^https?:\/\//g;
const plain = e.clipboardData.getData('text/plain');
if (plain) {
// tests for youtube and makes video document
if (plain.indexOf('www.youtube.com/watch') !== -1) {
const url = plain.replace('youtube.com/watch?v=', 'youtube.com/embed/');
undoBatch(() =>
PreviewCursor._addDocument(
Docs.Create.VideoDocument(url, {
title: url,
_width: 400,
_height: 315,
_nativeWidth: 600,
_nativeHeight: 472.5,
x: newPoint[0],
y: newPoint[1],
})
)
)();
} else if (re.test(plain)) {
const url = plain;
undoBatch(() =>
PreviewCursor._addDocument(
Docs.Create.WebDocument(url, {
title: url,
_width: 500,
_height: 300,
useCors: true,
x: newPoint[0],
y: newPoint[1],
})
)
)();
} else if (plain.startsWith('__DashDocId(') || plain.startsWith('__DashCloneId(')) {
const clone = plain.startsWith('__DashCloneId(');
const docids = plain.split(':');
const strs = docids[0].split(',');
const ptx = Number(strs[0].substring((clone ? '__DashCloneId(' : '__DashDocId(').length));
const pty = Number(strs[1].substring(0, strs[1].length - 1));
const batch = UndoManager.StartBatch('cloning');
{
const docs = await Promise.all(
docids
.filter((did, i) => i)
.map(async did => {
const doc = Cast(await DocServer.GetRefField(did), Doc, null);
return clone ? (await Doc.MakeClone(doc)).clone : doc;
})
);
const firstx = docs.length ? NumCast(docs[0].x) + ptx - newPoint[0] : 0;
const firsty = docs.length ? NumCast(docs[0].y) + pty - newPoint[1] : 0;
docs.map(doc => {
doc.x = NumCast(doc.x) - firstx;
doc.y = NumCast(doc.y) - firsty;
});
PreviewCursor._addDocument(docs);
}
batch.end();
e.stopPropagation();
} else {
// creates text document
FormattedTextBox.PasteOnLoad = e;
UndoManager.RunInBatch(() => PreviewCursor._addLiveTextDoc(DocUtils.GetNewTextDoc('-pasted text-', newPoint[0], newPoint[1], 500, undefined, undefined, undefined, 750)), 'paste');
}
}
//pasting in images
else if (e.clipboardData.getData('text/html') !== '' && e.clipboardData.getData('text/html').includes('
PreviewCursor._addDocument(
Docs.Create.ImageDocument(arr[1], {
_width: 300,
title: arr[1],
x: newPoint[0],
y: newPoint[1],
})
)
)();
} else if (e.clipboardData.items.length) {
const batch = UndoManager.StartBatch('collection view drop');
const files: File[] = [];
Array.from(e.clipboardData.items).forEach(item => {
const file = item.getAsFile();
file && files.push(file);
});
const generatedDocuments = await DocUtils.uploadFilesToDocs(files, { x: newPoint[0], y: newPoint[1] });
generatedDocuments.forEach(PreviewCursor._addDocument);
batch.end();
}
}
};
@action
onKeyPress = (e: KeyboardEvent) => {
// Mixing events between React and Native is finicky.
//if not these keys, make a textbox if preview cursor is active!
if (
e.key !== 'Escape' &&
e.key !== 'Backspace' &&
e.key !== 'Delete' &&
e.key !== 'CapsLock' &&
e.key !== 'Alt' &&
e.key !== 'Shift' &&
e.key !== 'Meta' &&
e.key !== 'Control' &&
e.key !== 'Insert' &&
e.key !== 'Home' &&
e.key !== 'End' &&
e.key !== 'PageUp' &&
e.key !== 'PageDown' &&
e.key !== 'NumLock' &&
e.key !== ' ' &&
(e.keyCode < 112 || e.keyCode > 123) && // F1 thru F12 keys
(e.keyCode < 173 || e.keyCode > 183 || e.key === '-') && // mute, volume up/down etc, - is there specifically because its keycode is 173 in Firefox so shouldn't be avoided
!e.key.startsWith('Arrow') &&
!e.defaultPrevented
) {
if ((!e.metaKey && !e.ctrlKey) || (e.keyCode >= 48 && e.keyCode <= 57) || (e.keyCode >= 65 && e.keyCode <= 90)) {
// /^[a-zA-Z0-9$*^%#@+-=_|}{[]"':;?/><.,}]$/.test(e.key)) {
PreviewCursor.Visible && PreviewCursor._onKeyPress?.(e);
((!e.ctrlKey && !e.metaKey) || e.key !== 'v') && (PreviewCursor.Visible = false);
}
} else if (PreviewCursor.Visible) {
if (e.key === 'ArrowRight') {
PreviewCursor._nudge?.(1 * (e.shiftKey ? 2 : 1), 0) && e.stopPropagation();
} else if (e.key === 'ArrowLeft') {
PreviewCursor._nudge?.(-1 * (e.shiftKey ? 2 : 1), 0) && e.stopPropagation();
} else if (e.key === 'ArrowUp') {
PreviewCursor._nudge?.(0, 1 * (e.shiftKey ? 2 : 1)) && e.stopPropagation();
} else if (e.key === 'ArrowDown') {
PreviewCursor._nudge?.(0, -1 * (e.shiftKey ? 2 : 1)) && e.stopPropagation();
}
}
};
//when focus is lost, this will remove the preview cursor
@action onBlur = (): void => {
PreviewCursor.Visible = false;
};
@action
public static Show(
x: number,
y: number,
onKeyPress: (e: KeyboardEvent) => void,
addLiveText: (doc: Doc) => void,
getTransform: () => Transform,
addDocument: undefined | ((doc: Doc | Doc[]) => boolean),
nudge: undefined | ((nudgeX: number, nudgeY: number) => boolean)
) {
this._clickPoint = [x, y];
this._onKeyPress = onKeyPress;
this._addLiveTextDoc = addLiveText;
this._getTransform = getTransform;
this._addDocument = addDocument || returnFalse;
this._nudge = nudge;
this.Visible = true;
}
render() {
return !PreviewCursor._clickPoint || !PreviewCursor.Visible ? null : (