diff options
Diffstat (limited to 'src/client/util')
-rw-r--r-- | src/client/util/DocumentManager.ts | 44 | ||||
-rw-r--r-- | src/client/util/DragManager.ts | 76 | ||||
-rw-r--r-- | src/client/util/Scripting.ts | 34 | ||||
-rw-r--r-- | src/client/util/SelectionManager.ts | 6 | ||||
-rw-r--r-- | src/client/util/SerializationHelper.ts | 130 | ||||
-rw-r--r-- | src/client/util/TooltipTextMenu.tsx | 8 | ||||
-rw-r--r-- | src/client/util/UndoManager.ts | 7 |
7 files changed, 211 insertions, 94 deletions
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index 56669fb79..69964e2c9 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -1,9 +1,8 @@ import { computed, observable } from 'mobx'; -import { Document } from "../../fields/Document"; -import { FieldWaiting } from '../../fields/Field'; -import { KeyStore } from '../../fields/KeyStore'; -import { ListField } from '../../fields/ListField'; import { DocumentView } from '../views/nodes/DocumentView'; +import { Doc } from '../../new_fields/Doc'; +import { FieldValue, Cast } from '../../new_fields/Types'; +import { listSpec } from '../../new_fields/Schema'; export class DocumentManager { @@ -25,26 +24,21 @@ export class DocumentManager { // this.DocumentViews = new Array<DocumentView>(); } - public getDocumentView(toFind: Document): DocumentView | null { + public getDocumentView(toFind: Doc): DocumentView | null { - let toReturn: DocumentView | null; - toReturn = null; + let toReturn: DocumentView | null = null; //gets document view that is in a freeform canvas collection DocumentManager.Instance.DocumentViews.map(view => { - let doc = view.props.Document; - - if (doc === toFind) { + if (view.props.Document === toFind) { toReturn = view; return; } }); if (!toReturn) { DocumentManager.Instance.DocumentViews.map(view => { - let doc = view.props.Document; - - let docSrc = doc.GetT(KeyStore.Prototype, Document); - if (docSrc && docSrc !== FieldWaiting && Object.is(docSrc, toFind)) { + let doc = view.props.Document.proto; + if (doc && Object.is(doc, toFind)) { toReturn = view; } }); @@ -52,7 +46,7 @@ export class DocumentManager { return toReturn; } - public getDocumentViews(toFind: Document): DocumentView[] { + public getDocumentViews(toFind: Doc): DocumentView[] { let toReturn: DocumentView[] = []; @@ -64,8 +58,8 @@ export class DocumentManager { if (doc === toFind) { toReturn.push(view); } else { - let docSrc = doc.GetT(KeyStore.Prototype, Document); - if (docSrc && docSrc !== FieldWaiting && Object.is(docSrc, toFind)) { + let docSrc = FieldValue(doc.proto); + if (docSrc && Object.is(docSrc, toFind)) { toReturn.push(view); } } @@ -77,20 +71,20 @@ export class DocumentManager { @computed public get LinkedDocumentViews() { return DocumentManager.Instance.DocumentViews.reduce((pairs, dv) => { - let linksList = dv.props.Document.GetT(KeyStore.LinkedToDocs, ListField); - if (linksList && linksList !== FieldWaiting && linksList.Data.length) { - pairs.push(...linksList.Data.reduce((pairs, link) => { - if (link instanceof Document) { - let linkToDoc = link.GetT(KeyStore.LinkedToDocs, Document); - if (linkToDoc && linkToDoc !== FieldWaiting) { + let linksList = Cast(dv.props.Document.linkedToDocs, listSpec(Doc)); + if (linksList && linksList.length) { + pairs.push(...linksList.reduce((pairs, link) => { + if (link) { + let linkToDoc = FieldValue(Cast(link.linkedTo, Doc)); + if (linkToDoc) { DocumentManager.Instance.getDocumentViews(linkToDoc).map(docView1 => pairs.push({ a: dv, b: docView1, l: link })); } } return pairs; - }, [] as { a: DocumentView, b: DocumentView, l: Document }[])); + }, [] as { a: DocumentView, b: DocumentView, l: Doc }[])); } return pairs; - }, [] as { a: DocumentView, b: DocumentView, l: Document }[]); + }, [] as { a: DocumentView, b: DocumentView, l: Doc }[]); } }
\ No newline at end of file diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 136852e12..a3dbe6e43 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -1,14 +1,14 @@ import { action } from "mobx"; -import { Document } from "../../fields/Document"; -import { FieldWaiting } from "../../fields/Field"; -import { KeyStore } from "../../fields/KeyStore"; import { emptyFunction } from "../../Utils"; import { CollectionDockingView } from "../views/collections/CollectionDockingView"; -import { DocumentDecorations } from "../views/DocumentDecorations"; import * as globalCssVariables from "../views/globalCssVariables.scss"; import { MainOverlayTextBox } from "../views/MainOverlayTextBox"; +import { Doc } from "../../new_fields/Doc"; +import { Cast } from "../../new_fields/Types"; +import { listSpec } from "../../new_fields/Schema"; -export function SetupDrag(_reference: React.RefObject<HTMLDivElement>, docFunc: () => Document, moveFunc?: DragManager.MoveFunction, copyOnDrop: boolean = false) { +export type dropActionType = "alias" | "copy" | undefined; +export function SetupDrag(_reference: React.RefObject<HTMLDivElement>, docFunc: () => Doc, moveFunc?: DragManager.MoveFunction, dropAction?: dropActionType) { let onRowMove = action((e: PointerEvent): void => { e.stopPropagation(); e.preventDefault(); @@ -16,7 +16,7 @@ export function SetupDrag(_reference: React.RefObject<HTMLDivElement>, docFunc: document.removeEventListener("pointermove", onRowMove); document.removeEventListener('pointerup', onRowUp); var dragData = new DragManager.DocumentDragData([docFunc()]); - dragData.copyOnDrop = copyOnDrop; + dragData.dropAction = dropAction; dragData.moveDocument = moveFunc; DragManager.StartDocumentDrag([_reference.current!], dragData, e.x, e.y); }); @@ -40,19 +40,19 @@ export function SetupDrag(_reference: React.RefObject<HTMLDivElement>, docFunc: return onItemDown; } -export async function DragLinksAsDocuments(dragEle: HTMLElement, x: number, y: number, sourceDoc: Document) { - let srcTarg = sourceDoc.GetPrototype(); +export async function DragLinksAsDocuments(dragEle: HTMLElement, x: number, y: number, sourceDoc: Doc) { + let srcTarg = sourceDoc.proto; let draggedDocs = srcTarg ? - srcTarg.GetList(KeyStore.LinkedToDocs, [] as Document[]).map(linkDoc => - (linkDoc.GetT(KeyStore.LinkedToDocs, Document)) as Document) : []; + Cast(srcTarg.linkedToDocs, listSpec(Doc), []).map(linkDoc => + Cast(linkDoc.linkedTo, Doc) as Doc) : []; let draggedFromDocs = srcTarg ? - srcTarg.GetList(KeyStore.LinkedFromDocs, [] as Document[]).map(linkDoc => - (linkDoc.GetT(KeyStore.LinkedFromDocs, Document)) as Document) : []; + Cast(srcTarg.linkedFromDocs, listSpec(Doc), []).map(linkDoc => + Cast(linkDoc.linkedFrom, Doc) as Doc) : []; draggedDocs.push(...draggedFromDocs); if (draggedDocs.length) { - let moddrag = [] as Document[]; + let moddrag: Doc[] = []; for (const draggedDoc of draggedDocs) { - let doc = await draggedDoc.GetTAsync(KeyStore.AnnotationOn, Document); + let doc = await Cast(draggedDoc.annotationOn, Doc); if (doc) moddrag.push(doc); } let dragData = new DragManager.DocumentDragData(moddrag.length ? moddrag : draggedDocs); @@ -134,37 +134,42 @@ export namespace DragManager { }; } - export type MoveFunction = (document: Document, targetCollection: Document, addDocument: (document: Document) => boolean) => boolean; + export type MoveFunction = (document: Doc, targetCollection: Doc, addDocument: (document: Doc) => boolean) => boolean; export class DocumentDragData { - constructor(dragDoc: Document[]) { + constructor(dragDoc: Doc[]) { this.draggedDocuments = dragDoc; this.droppedDocuments = dragDoc; this.xOffset = 0; this.yOffset = 0; } - draggedDocuments: Document[]; - droppedDocuments: Document[]; + draggedDocuments: Doc[]; + droppedDocuments: Doc[]; xOffset: number; yOffset: number; - aliasOnDrop?: boolean; - copyOnDrop?: boolean; + dropAction: dropActionType; + userDropAction: dropActionType; moveDocument?: MoveFunction; [id: string]: any; } export function StartDocumentDrag(eles: HTMLElement[], dragData: DocumentDragData, downX: number, downY: number, options?: DragOptions) { StartDrag(eles, dragData, downX, downY, options, - (dropData: { [id: string]: any }) => (dropData.droppedDocuments = dragData.aliasOnDrop ? dragData.draggedDocuments.map(d => d.CreateAlias()) : dragData.copyOnDrop ? dragData.draggedDocuments.map(d => d.Copy(true) as Document) : dragData.draggedDocuments)); + (dropData: { [id: string]: any }) => + (dropData.droppedDocuments = dragData.userDropAction == "alias" || (!dragData.userDropAction && dragData.dropAction == "alias") ? + dragData.draggedDocuments.map(d => Doc.MakeAlias(d)) : + dragData.userDropAction == "copy" || (!dragData.userDropAction && dragData.dropAction == "copy") ? + dragData.draggedDocuments.map(d => Doc.MakeCopy(d, true)) : + dragData.draggedDocuments)); } export class LinkDragData { - constructor(linkSourceDoc: Document, blacklist: Document[] = []) { + constructor(linkSourceDoc: Doc, blacklist: Doc[] = []) { this.linkSourceDocument = linkSourceDoc; this.blacklist = blacklist; } - droppedDocuments: Document[] = []; - linkSourceDocument: Document; - blacklist: Document[]; + droppedDocuments: Doc[] = []; + linkSourceDocument: Doc; + blacklist: Doc[]; [id: string]: any; } @@ -188,7 +193,7 @@ export namespace DragManager { let xs: number[] = []; let ys: number[] = []; - const docs: Document[] = + const docs: Doc[] = dragData instanceof DocumentDragData ? dragData.draggedDocuments : []; let dragElements = eles.map(ele => { const w = ele.offsetWidth, @@ -252,7 +257,7 @@ export namespace DragManager { e.stopPropagation(); e.preventDefault(); if (dragData instanceof DocumentDragData) { - dragData.aliasOnDrop = e.ctrlKey || e.altKey; + dragData.userDropAction = e.ctrlKey || e.altKey ? "alias" : undefined; } if (e.shiftKey) { AbortDrag(); @@ -301,15 +306,20 @@ export namespace DragManager { function dispatchDrag(dragEles: HTMLElement[], e: PointerEvent, dragData: { [index: string]: any }, options?: DragOptions, finishDrag?: (dragData: { [index: string]: any }) => void) { let removed = dragEles.map(dragEle => { - let parent = dragEle.parentElement; - if (parent) parent.removeChild(dragEle); - return [dragEle, parent]; + // let parent = dragEle.parentElement; + // if (parent) parent.removeChild(dragEle); + let ret = [dragEle, dragEle.style.width, dragEle.style.height]; + dragEle.style.width = "0"; + dragEle.style.height = "0"; + return ret; }); const target = document.elementFromPoint(e.x, e.y); removed.map(r => { - let dragEle = r[0]; - let parent = r[1]; - if (parent && dragEle) parent.appendChild(dragEle); + let dragEle = r[0] as HTMLElement; + dragEle.style.width = r[1] as string; + dragEle.style.height = r[2] as string; + // let parent = r[1]; + // if (parent && dragEle) parent.appendChild(dragEle); }); if (target) { if (finishDrag) finishDrag(dragData); diff --git a/src/client/util/Scripting.ts b/src/client/util/Scripting.ts index c67cc067a..e45f61c11 100644 --- a/src/client/util/Scripting.ts +++ b/src/client/util/Scripting.ts @@ -1,13 +1,5 @@ // import * as ts from "typescript" let ts = (window as any).ts; -import { Opt, Field } from "../../fields/Field"; -import { Document } from "../../fields/Document"; -import { NumberField } from "../../fields/NumberField"; -import { ImageField } from "../../fields/ImageField"; -import { TextField } from "../../fields/TextField"; -import { RichTextField } from "../../fields/RichTextField"; -import { KeyStore } from "../../fields/KeyStore"; -import { ListField } from "../../fields/ListField"; // // @ts-ignore // import * as typescriptlib from '!!raw-loader!../../../node_modules/typescript/lib/lib.d.ts' // // @ts-ignore @@ -15,8 +7,11 @@ import { ListField } from "../../fields/ListField"; // @ts-ignore import * as typescriptlib from '!!raw-loader!./type_decls.d'; -import { Documents } from "../documents/Documents"; -import { Key } from "../../fields/Key"; +import { Docs } from "../documents/Documents"; +import { Doc, Field } from '../../new_fields/Doc'; +import { ImageField, PdfField, VideoField, AudioField } from '../../new_fields/URLField'; +import { List } from '../../new_fields/List'; +import { RichTextField } from '../../new_fields/RichTextField'; export interface ScriptSucccess { success: true; @@ -50,9 +45,9 @@ function Run(script: string | undefined, customParams: string[], diagnostics: an return { compiled: false, errors: diagnostics }; } - let fieldTypes = [Document, NumberField, TextField, ImageField, RichTextField, ListField, Key]; - let paramNames = ["KeyStore", "Documents", ...fieldTypes.map(fn => fn.name)]; - let params: any[] = [KeyStore, Documents, ...fieldTypes]; + let fieldTypes = [Doc, ImageField, PdfField, VideoField, AudioField, List, RichTextField]; + let paramNames = ["Docs", ...fieldTypes.map(fn => fn.name)]; + let params: any[] = [Docs, ...fieldTypes]; let compiledFunction = new Function(...paramNames, `return ${script}`); let { capturedVariables = {} } = options; let run = (args: { [name: string]: any } = {}): ScriptResult => { @@ -171,17 +166,4 @@ export function CompileScript(script: string, options: ScriptOptions = {}): Comp let diagnostics = ts.getPreEmitDiagnostics(program).concat(testResult.diagnostics); return Run(outputText, paramNames, diagnostics, script, options); -} - -export function OrLiteralType(returnType: string): string { - return `${returnType} | string | number`; -} - -export function ToField(data: any): Opt<Field> { - if (typeof data === "string") { - return new TextField(data); - } else if (typeof data === "number") { - return new NumberField(data); - } - return undefined; }
\ No newline at end of file diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts index da66bf3fc..fe5acf4b4 100644 --- a/src/client/util/SelectionManager.ts +++ b/src/client/util/SelectionManager.ts @@ -1,5 +1,5 @@ -import { action, observable } from "mobx"; -import { Document } from "../../fields/Document"; +import { observable, action } from "mobx"; +import { Doc } from "../../new_fields/Doc"; import { MainOverlayTextBox } from "../views/MainOverlayTextBox"; import { DocumentView } from "../views/nodes/DocumentView"; @@ -49,7 +49,7 @@ export namespace SelectionManager { return manager.SelectedDocuments.indexOf(doc) !== -1; } - export function DeselectAll(except?: Document): void { + export function DeselectAll(except?: Doc): void { let found: DocumentView | undefined = undefined; if (except) { for (const view of manager.SelectedDocuments) { diff --git a/src/client/util/SerializationHelper.ts b/src/client/util/SerializationHelper.ts new file mode 100644 index 000000000..7ded85e43 --- /dev/null +++ b/src/client/util/SerializationHelper.ts @@ -0,0 +1,130 @@ +import { PropSchema, serialize, deserialize, custom, setDefaultModelSchema, getDefaultModelSchema, primitive, SKIP } from "serializr"; +import { Field } from "../../new_fields/Doc"; + +export namespace SerializationHelper { + let serializing: number = 0; + export function IsSerializing() { + return serializing > 0; + } + + export function Serialize(obj: Field): any { + if (obj === undefined || obj === null) { + return undefined; + } + + if (typeof obj !== 'object') { + return obj; + } + + serializing += 1; + if (!(obj.constructor.name in reverseMap)) { + throw Error(`type '${obj.constructor.name}' not registered. Make sure you register it using a @Deserializable decorator`); + } + + const json = serialize(obj); + json.__type = reverseMap[obj.constructor.name]; + serializing -= 1; + return json; + } + + export function Deserialize(obj: any): any { + if (obj === undefined || obj === null) { + return undefined; + } + + if (typeof obj !== 'object') { + return obj; + } + + serializing += 1; + if (!obj.__type) { + throw Error("No property 'type' found in JSON."); + } + + if (!(obj.__type in serializationTypes)) { + throw Error(`type '${obj.__type}' not registered. Make sure you register it using a @Deserializable decorator`); + } + + const value = deserialize(serializationTypes[obj.__type], obj); + serializing -= 1; + return value; + } +} + +let serializationTypes: { [name: string]: any } = {}; +let reverseMap: { [ctor: string]: string } = {}; + +export interface DeserializableOpts { + (constructor: { new(...args: any[]): any }): void; + withFields(fields: string[]): Function; +} + +export function Deserializable(name: string): DeserializableOpts; +export function Deserializable(constructor: { new(...args: any[]): any }): void; +export function Deserializable(constructor: { new(...args: any[]): any } | string): DeserializableOpts | void { + function addToMap(name: string, ctor: { new(...args: any[]): any }) { + const schema = getDefaultModelSchema(ctor) as any; + if (schema.targetClass !== ctor) { + const newSchema = { ...schema, factory: () => new ctor() }; + setDefaultModelSchema(ctor, newSchema); + } + if (!(name in serializationTypes)) { + serializationTypes[name] = ctor; + reverseMap[ctor.name] = name; + } else { + throw new Error(`Name ${name} has already been registered as deserializable`); + } + } + if (typeof constructor === "string") { + return Object.assign((ctor: { new(...args: any[]): any }) => { + addToMap(constructor, ctor); + }, { withFields: Deserializable.withFields }); + } + addToMap(constructor.name, constructor); +} + +export namespace Deserializable { + export function withFields(fields: string[]) { + return function (constructor: { new(...fields: any[]): any }) { + Deserializable(constructor); + let schema = getDefaultModelSchema(constructor); + if (schema) { + schema.factory = context => { + const args = fields.map(key => context.json[key]); + return new constructor(...args); + }; + // TODO A modified version of this would let us not reassign fields that we're passing into the constructor later on in deserializing + // fields.forEach(field => { + // if (field in schema.props) { + // let propSchema = schema.props[field]; + // if (propSchema === false) { + // return; + // } else if (propSchema === true) { + // propSchema = primitive(); + // } + // schema.props[field] = custom(propSchema.serializer, + // () => { + // return SKIP; + // }); + // } + // }); + } else { + schema = { + props: {}, + factory: context => { + const args = fields.map(key => context.json[key]); + return new constructor(...args); + } + }; + setDefaultModelSchema(constructor, schema); + } + }; + } +} + +export function autoObject(): PropSchema { + return custom( + (s) => SerializationHelper.Serialize(s), + (s) => SerializationHelper.Deserialize(s) + ); +}
\ No newline at end of file diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx index 14af4bdfd..68a73375e 100644 --- a/src/client/util/TooltipTextMenu.tsx +++ b/src/client/util/TooltipTextMenu.tsx @@ -38,9 +38,9 @@ export class TooltipTextMenu { private listTypeToIcon: Map<NodeType, string>; private fontSizeIndicator: HTMLSpanElement = document.createElement("span"); //dropdown doms - private fontSizeDom: Node; - private fontStyleDom: Node; - private listTypeBtnDom: Node; + private fontSizeDom?: Node; + private fontStyleDom?: Node; + private listTypeBtnDom?: Node; constructor(view: EditorView, editorProps: FieldViewProps) { this.view = view; @@ -321,7 +321,7 @@ export class TooltipTextMenu { this.tooltip.style.bottom = (box.bottom - start.top) * this.editorProps.ScreenToLocalTransform().Scale + "px"; //UPDATE LIST ITEM DROPDOWN - this.listTypeBtnDom = this.updateListItemDropdown(":", this.listTypeBtnDom); + this.listTypeBtnDom = this.updateListItemDropdown(":", this.listTypeBtnDom!); //UPDATE FONT STYLE DROPDOWN let activeStyles = this.activeMarksOnSelection(this.fontStyles); diff --git a/src/client/util/UndoManager.ts b/src/client/util/UndoManager.ts index 27aed4bac..0b5280c4a 100644 --- a/src/client/util/UndoManager.ts +++ b/src/client/util/UndoManager.ts @@ -1,4 +1,4 @@ -import { observable, action } from "mobx"; +import { observable, action, runInAction } from "mobx"; import 'source-map-support/register'; import { Without } from "../../Utils"; import { string } from "prop-types"; @@ -140,10 +140,11 @@ export namespace UndoManager { } }); - export function RunInBatch(fn: () => void, batchName: string) { + //TODO Make this return the return value + export function RunInBatch<T>(fn: () => T, batchName: string) { let batch = StartBatch(batchName); try { - fn(); + return runInAction(fn); } finally { batch.end(); } |