import { action, observable, runInAction } from 'mobx'; import { observer } from 'mobx-react'; 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, DocumentOptions, DocUtils } from '../documents/Documents'; import { ImageUtils } from '../util/Import & Export/ImageUtils'; 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[]) => boolean; static _addLiveTextDoc: (doc: Doc) => void; static _nudge?: undefined | ((x: number, y: number) => boolean); static _slowLoadDocuments?: (files: File[] | string, options: DocumentOptions, generatedDocuments: Doc[], text: string, completed: ((doc: Doc[]) => void) | undefined, addDocument: (doc: Doc | Doc[]) => boolean) => Promise; @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, true); } 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 batch = UndoManager.StartBatch('youtube upload'); const generatedDocuments: Doc[] = []; const options = { title: plain, _width: 400, _height: 315, x: newPoint[0], y: newPoint[1], }; PreviewCursor._slowLoadDocuments?.(plain.split('v=')[1].split('&')[0], options, generatedDocuments, '', undefined, PreviewCursor._addDocument).then(batch.end); } 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 toCopy = await Promise.all(docids.slice(1).map(async did => Cast(await DocServer.GetRefField(did), Doc, null))); const docs = clone ? (await Promise.all(Doc.MakeClones(toCopy, false))).map(res => res.clone) : toCopy; 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 { FormattedTextBox.PasteOnLoad = e; if (e.clipboardData.getData('dash/pdfAnchor')) e.preventDefault(); UndoManager.RunInBatch(() => PreviewCursor._addLiveTextDoc(DocUtils.GetNewTextDoc('', 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(' { const doc = Docs.Create.ImageDocument(arr[1], { _width: 300, title: arr[1], x: newPoint[0], y: newPoint[1], }); ImageUtils.ExtractExif(doc); PreviewCursor._addDocument(doc); })(); } 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), slowLoadDocuments: (files: File[] | string, options: DocumentOptions, generatedDocuments: Doc[], text: string, completed: ((doc: Doc[]) => void) | undefined, addDocument: (doc: Doc | Doc[]) => boolean) => Promise ) { this._clickPoint = [x, y]; this._onKeyPress = onKeyPress; this._addLiveTextDoc = addLiveText; this._getTransform = getTransform; this._addDocument = addDocument || returnFalse; this._nudge = nudge; this._slowLoadDocuments = slowLoadDocuments; this.Visible = true; } render() { return !PreviewCursor._clickPoint || !PreviewCursor.Visible ? null : (
e?.focus()} style={{ transform: `translate(${PreviewCursor._clickPoint[0]}px, ${PreviewCursor._clickPoint[1]}px)` }}> I
); } }