aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTyler Schicke <tyler_schicke@brown.edu>2019-04-21 01:46:02 -0400
committerTyler Schicke <tyler_schicke@brown.edu>2019-04-21 01:46:02 -0400
commitab8b4ffd7f3b89600a71d3167490c40a59be433e (patch)
tree18e2eb2d96c751fabc7e7cc9680e5c4ca939e686 /src
parent3556aae6d063d8b654509330e70bc67606f16613 (diff)
More changes/fixes
Diffstat (limited to 'src')
-rw-r--r--src/client/views/collections/CollectionSubView.tsx354
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx21
-rw-r--r--src/client/views/nodes/FieldView.tsx8
-rw-r--r--src/new_fields/Schema.ts15
-rw-r--r--src/new_fields/Types.ts2
5 files changed, 210 insertions, 190 deletions
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 5c3b2e586..3add288fd 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -18,11 +18,13 @@ import * as rp from 'request-promise';
import { CollectionView } from "./CollectionView";
import { CollectionPDFView } from "./CollectionPDFView";
import { CollectionVideoView } from "./CollectionVideoView";
+import { Doc } from "../../../new_fields/Doc";
+import { DocComponent } from "../DocComponent";
export interface CollectionViewProps extends FieldViewProps {
- addDocument: (document: Document, allowDuplicates?: boolean) => boolean;
- removeDocument: (document: Document) => boolean;
- moveDocument: (document: Document, targetCollection: Document, addDocument: (document: Document) => boolean) => boolean;
+ addDocument: (document: Doc, allowDuplicates?: boolean) => boolean;
+ removeDocument: (document: Doc) => boolean;
+ moveDocument: (document: Doc, targetCollection: Doc, addDocument: (document: Doc) => boolean) => boolean;
PanelWidth: () => number;
PanelHeight: () => number;
}
@@ -33,198 +35,202 @@ export interface SubCollectionViewProps extends CollectionViewProps {
export type CursorEntry = TupleField<[string, string], [number, number]>;
-export class CollectionSubView extends React.Component<SubCollectionViewProps> {
- private dropDisposer?: DragManager.DragDropDisposer;
- protected createDropTarget = (ele: HTMLDivElement) => {
- if (this.dropDisposer) {
- this.dropDisposer();
+export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
+ class CollectionSubView extends DocComponent<SubCollectionViewProps, T>(schemaCtor) {
+ private dropDisposer?: DragManager.DragDropDisposer;
+ protected createDropTarget = (ele: HTMLDivElement) => {
+ if (this.dropDisposer) {
+ this.dropDisposer();
+ }
+ if (ele) {
+ this.dropDisposer = DragManager.MakeDropTarget(ele, { handlers: { drop: this.drop.bind(this) } });
+ }
}
- if (ele) {
- this.dropDisposer = DragManager.MakeDropTarget(ele, { handlers: { drop: this.drop.bind(this) } });
+ protected CreateDropTarget(ele: HTMLDivElement) {
+ this.createDropTarget(ele);
}
- }
- protected CreateDropTarget(ele: HTMLDivElement) {
- this.createDropTarget(ele);
- }
- @action
- protected setCursorPosition(position: [number, number]) {
- let ind;
- let doc = this.props.Document;
- let id = CurrentUserUtils.id;
- let email = CurrentUserUtils.email;
- if (id && email) {
- let textInfo: [string, string] = [id, email];
- doc.GetTAsync(KeyStore.Prototype, Document).then(proto => {
- if (!proto) {
- return;
- }
- proto.GetOrCreateAsync<ListField<CursorEntry>>(KeyStore.Cursors, ListField, action((field: ListField<CursorEntry>) => {
- let cursors = field.Data;
- if (cursors.length > 0 && (ind = cursors.findIndex(entry => entry.Data[0][0] === id)) > -1) {
- cursors[ind].Data[1] = position;
- } else {
- let entry = new TupleField<[string, string], [number, number]>([textInfo, position]);
- cursors.push(entry);
+ @action
+ protected setCursorPosition(position: [number, number]) {
+ let ind;
+ let doc = this.props.Document;
+ let id = CurrentUserUtils.id;
+ let email = CurrentUserUtils.email;
+ if (id && email) {
+ let textInfo: [string, string] = [id, email];
+ doc.GetTAsync(KeyStore.Prototype, Document).then(proto => {
+ if (!proto) {
+ return;
}
- }));
- });
+ proto.GetOrCreateAsync<ListField<CursorEntry>>(KeyStore.Cursors, ListField, action((field: ListField<CursorEntry>) => {
+ let cursors = field.Data;
+ if (cursors.length > 0 && (ind = cursors.findIndex(entry => entry.Data[0][0] === id)) > -1) {
+ cursors[ind].Data[1] = position;
+ } else {
+ let entry = new TupleField<[string, string], [number, number]>([textInfo, position]);
+ cursors.push(entry);
+ }
+ }));
+ });
+ }
}
- }
- @undoBatch
- @action
- protected drop(e: Event, de: DragManager.DropEvent): boolean {
- if (de.data instanceof DragManager.DocumentDragData) {
- if (de.data.aliasOnDrop || de.data.copyOnDrop) {
- [KeyStore.Width, KeyStore.Height, KeyStore.CurPage].map(key =>
- de.data.draggedDocuments.map((draggedDocument: Document, i: number) =>
- draggedDocument.GetTAsync(key, NumberField, (f: Opt<NumberField>) => f ? de.data.droppedDocuments[i].SetNumber(key, f.Data) : null)));
- }
- let added = false;
- if (de.data.aliasOnDrop || de.data.copyOnDrop) {
- added = de.data.droppedDocuments.reduce((added: boolean, d) => {
- let moved = this.props.addDocument(d);
- return moved || added;
- }, false);
- } else if (de.data.moveDocument) {
- const move = de.data.moveDocument;
- added = de.data.droppedDocuments.reduce((added: boolean, d) => {
- let moved = move(d, this.props.Document, this.props.addDocument);
- return moved || added;
- }, false);
- } else {
- added = de.data.droppedDocuments.reduce((added: boolean, d) => {
- let moved = this.props.addDocument(d);
- return moved || added;
- }, false);
+ @undoBatch
+ @action
+ protected drop(e: Event, de: DragManager.DropEvent): boolean {
+ if (de.data instanceof DragManager.DocumentDragData) {
+ if (de.data.aliasOnDrop || de.data.copyOnDrop) {
+ [KeyStore.Width, KeyStore.Height, KeyStore.CurPage].map(key =>
+ de.data.draggedDocuments.map((draggedDocument: Document, i: number) =>
+ draggedDocument.GetTAsync(key, NumberField, (f: Opt<NumberField>) => f ? de.data.droppedDocuments[i].SetNumber(key, f.Data) : null)));
+ }
+ let added = false;
+ if (de.data.aliasOnDrop || de.data.copyOnDrop) {
+ added = de.data.droppedDocuments.reduce((added: boolean, d) => {
+ let moved = this.props.addDocument(d);
+ return moved || added;
+ }, false);
+ } else if (de.data.moveDocument) {
+ const move = de.data.moveDocument;
+ added = de.data.droppedDocuments.reduce((added: boolean, d) => {
+ let moved = move(d, this.props.Document, this.props.addDocument);
+ return moved || added;
+ }, false);
+ } else {
+ added = de.data.droppedDocuments.reduce((added: boolean, d) => {
+ let moved = this.props.addDocument(d);
+ return moved || added;
+ }, false);
+ }
+ e.stopPropagation();
+ return added;
}
- e.stopPropagation();
- return added;
+ return false;
}
- return false;
- }
- protected async getDocumentFromType(type: string, path: string, options: DocumentOptions): Promise<Opt<Document>> {
- let ctor: ((path: string, options: DocumentOptions) => (Document | Promise<Document | undefined>)) | undefined = undefined;
- if (type.indexOf("image") !== -1) {
- ctor = Documents.ImageDocument;
- }
- if (type.indexOf("video") !== -1) {
- ctor = Documents.VideoDocument;
- }
- if (type.indexOf("audio") !== -1) {
- ctor = Documents.AudioDocument;
- }
- if (type.indexOf("pdf") !== -1) {
- ctor = Documents.PdfDocument;
- options.nativeWidth = 1200;
- }
- if (type.indexOf("excel") !== -1) {
- ctor = Documents.DBDocument;
- options.copyDraggedItems = true;
- }
- if (type.indexOf("html") !== -1) {
- if (path.includes('localhost')) {
- let s = path.split('/');
- let id = s[s.length - 1];
- Server.GetField(id).then(field => {
- if (field instanceof Document) {
- let alias = field.CreateAlias();
- alias.SetNumber(KeyStore.X, options.x || 0);
- alias.SetNumber(KeyStore.Y, options.y || 0);
- alias.SetNumber(KeyStore.Width, options.width || 300);
- alias.SetNumber(KeyStore.Height, options.height || options.width || 300);
- this.props.addDocument(alias, false);
- }
- });
- return undefined;
+ protected async getDocumentFromType(type: string, path: string, options: DocumentOptions): Promise<Opt<Document>> {
+ let ctor: ((path: string, options: DocumentOptions) => (Document | Promise<Document | undefined>)) | undefined = undefined;
+ if (type.indexOf("image") !== -1) {
+ ctor = Documents.ImageDocument;
+ }
+ if (type.indexOf("video") !== -1) {
+ ctor = Documents.VideoDocument;
+ }
+ if (type.indexOf("audio") !== -1) {
+ ctor = Documents.AudioDocument;
+ }
+ if (type.indexOf("pdf") !== -1) {
+ ctor = Documents.PdfDocument;
+ options.nativeWidth = 1200;
+ }
+ if (type.indexOf("excel") !== -1) {
+ ctor = Documents.DBDocument;
+ options.copyDraggedItems = true;
}
- ctor = Documents.WebDocument;
- options = { height: options.width, ...options, title: path };
+ if (type.indexOf("html") !== -1) {
+ if (path.includes('localhost')) {
+ let s = path.split('/');
+ let id = s[s.length - 1];
+ Server.GetField(id).then(field => {
+ if (field instanceof Document) {
+ let alias = field.CreateAlias();
+ alias.SetNumber(KeyStore.X, options.x || 0);
+ alias.SetNumber(KeyStore.Y, options.y || 0);
+ alias.SetNumber(KeyStore.Width, options.width || 300);
+ alias.SetNumber(KeyStore.Height, options.height || options.width || 300);
+ this.props.addDocument(alias, false);
+ }
+ });
+ return undefined;
+ }
+ ctor = Documents.WebDocument;
+ options = { height: options.width, ...options, title: path };
+ }
+ return ctor ? ctor(path, options) : undefined;
}
- return ctor ? ctor(path, options) : undefined;
- }
- @undoBatch
- @action
- protected onDrop(e: React.DragEvent, options: DocumentOptions): void {
- let html = e.dataTransfer.getData("text/html");
- let text = e.dataTransfer.getData("text/plain");
+ @undoBatch
+ @action
+ protected onDrop(e: React.DragEvent, options: DocumentOptions): void {
+ let html = e.dataTransfer.getData("text/html");
+ let text = e.dataTransfer.getData("text/plain");
- if (text && text.startsWith("<div")) {
- return;
- }
- e.stopPropagation();
- e.preventDefault();
-
- if (html && html.indexOf("<img") !== 0 && !html.startsWith("<a")) {
- console.log("not good");
- let htmlDoc = Documents.HtmlDocument(html, { ...options, width: 300, height: 300 });
- htmlDoc.SetText(KeyStore.DocumentText, text);
- this.props.addDocument(htmlDoc, false);
- return;
- }
+ if (text && text.startsWith("<div")) {
+ return;
+ }
+ e.stopPropagation();
+ e.preventDefault();
- let batch = UndoManager.StartBatch("collection view drop");
- let promises: Promise<void>[] = [];
- // tslint:disable-next-line:prefer-for-of
- for (let i = 0; i < e.dataTransfer.items.length; i++) {
- const upload = window.location.origin + RouteStore.upload;
- let item = e.dataTransfer.items[i];
- if (item.kind === "string" && item.type.indexOf("uri") !== -1) {
- let str: string;
- let prom = new Promise<string>(resolve => e.dataTransfer.items[i].getAsString(resolve))
- .then(action((s: string) => rp.head(ServerUtils.prepend(RouteStore.corsProxy + "/" + (str = s)))))
- .then(result => {
- let type = result.headers["content-type"];
- if (type) {
- this.getDocumentFromType(type, str, { ...options, width: 300, nativeWidth: 300 })
- .then(doc => doc && this.props.addDocument(doc, false));
- }
- });
- promises.push(prom);
+ if (html && html.indexOf("<img") !== 0 && !html.startsWith("<a")) {
+ console.log("not good");
+ let htmlDoc = Documents.HtmlDocument(html, { ...options, width: 300, height: 300 });
+ htmlDoc.SetText(KeyStore.DocumentText, text);
+ this.props.addDocument(htmlDoc, false);
+ return;
}
- let type = item.type;
- if (item.kind === "file") {
- let file = item.getAsFile();
- let formData = new FormData();
- if (file) {
- formData.append('file', file);
+ let batch = UndoManager.StartBatch("collection view drop");
+ let promises: Promise<void>[] = [];
+ // tslint:disable-next-line:prefer-for-of
+ for (let i = 0; i < e.dataTransfer.items.length; i++) {
+ const upload = window.location.origin + RouteStore.upload;
+ let item = e.dataTransfer.items[i];
+ if (item.kind === "string" && item.type.indexOf("uri") !== -1) {
+ let str: string;
+ let prom = new Promise<string>(resolve => e.dataTransfer.items[i].getAsString(resolve))
+ .then(action((s: string) => rp.head(ServerUtils.prepend(RouteStore.corsProxy + "/" + (str = s)))))
+ .then(result => {
+ let type = result.headers["content-type"];
+ if (type) {
+ this.getDocumentFromType(type, str, { ...options, width: 300, nativeWidth: 300 })
+ .then(doc => doc && this.props.addDocument(doc, false));
+ }
+ });
+ promises.push(prom);
}
- let dropFileName = file ? file.name : "-empty-";
-
- let prom = fetch(upload, {
- method: 'POST',
- body: formData
- }).then(async (res: Response) => {
- (await res.json()).map(action((file: any) => {
- let path = window.location.origin + file;
- let docPromise = this.getDocumentFromType(type, path, { ...options, nativeWidth: 600, width: 300, title: dropFileName });
-
- docPromise.then(action((doc?: Document) => {
- let docs = this.props.Document.GetT(KeyStore.Data, ListField);
- if (docs !== FieldWaiting) {
- if (!docs) {
- docs = new ListField<Document>();
- this.props.Document.Set(KeyStore.Data, docs);
- }
- if (doc) {
- docs.Data.push(doc);
+ let type = item.type;
+ if (item.kind === "file") {
+ let file = item.getAsFile();
+ let formData = new FormData();
+
+ if (file) {
+ formData.append('file', file);
+ }
+ let dropFileName = file ? file.name : "-empty-";
+
+ let prom = fetch(upload, {
+ method: 'POST',
+ body: formData
+ }).then(async (res: Response) => {
+ (await res.json()).map(action((file: any) => {
+ let path = window.location.origin + file;
+ let docPromise = this.getDocumentFromType(type, path, { ...options, nativeWidth: 600, width: 300, title: dropFileName });
+
+ docPromise.then(action((doc?: Document) => {
+ let docs = this.props.Document.GetT(KeyStore.Data, ListField);
+ if (docs !== FieldWaiting) {
+ if (!docs) {
+ docs = new ListField<Document>();
+ this.props.Document.Set(KeyStore.Data, docs);
+ }
+ if (doc) {
+ docs.Data.push(doc);
+ }
}
- }
+ }));
}));
- }));
- });
- promises.push(prom);
+ });
+ promises.push(prom);
+ }
}
- }
- if (promises.length) {
- Promise.all(promises).finally(() => batch.end());
- } else {
- batch.end();
+ if (promises.length) {
+ Promise.all(promises).finally(() => batch.end());
+ } else {
+ batch.end();
+ }
}
}
+ return CollectionSubView;
}
+
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 945c01059..32104b49d 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1,7 +1,5 @@
import { action, computed, observable, trace } from "mobx";
import { observer } from "mobx-react";
-import { Document } from "../../../../fields/Document";
-import { KeyStore } from "../../../../fields/KeyStore";
import { emptyFunction, returnFalse, returnOne } from "../../../../Utils";
import { DocumentManager } from "../../../util/DocumentManager";
import { DragManager } from "../../../util/DragManager";
@@ -13,7 +11,7 @@ import { InkingCanvas } from "../../InkingCanvas";
import { MainOverlayTextBox } from "../../MainOverlayTextBox";
import { CollectionFreeFormDocumentView } from "../../nodes/CollectionFreeFormDocumentView";
import { DocumentContentsView } from "../../nodes/DocumentContentsView";
-import { DocumentViewProps } from "../../nodes/DocumentView";
+import { DocumentViewProps, positionSchema } from "../../nodes/DocumentView";
import { CollectionSubView } from "../CollectionSubView";
import { CollectionFreeFormLinksView } from "./CollectionFreeFormLinksView";
import { CollectionFreeFormRemoteCursors } from "./CollectionFreeFormRemoteCursors";
@@ -21,17 +19,28 @@ import "./CollectionFreeFormView.scss";
import { MarqueeView } from "./MarqueeView";
import React = require("react");
import v5 = require("uuid/v5");
-import { BooleanField } from "../../../../fields/BooleanField";
+import { createSchema, makeInterface } from "../../../../new_fields/Schema";
+import { Doc } from "../../../../new_fields/Doc";
+import { FieldValue } from "../../../../new_fields/Types";
+
+export const panZoomSchema = createSchema({
+ panX: "number",
+ panY: "number",
+ zoom: "number"
+});
+
+type PanZoomDocument = makeInterface<[typeof panZoomSchema, typeof positionSchema]>;
+const PanZoomDocument: (doc: Doc) => PanZoomDocument = makeInterface(panZoomSchema, positionSchema);
@observer
-export class CollectionFreeFormView extends CollectionSubView {
+export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
private _selectOnLoaded: string = ""; // id of document that should be selected once it's loaded (used for click-to-type)
private _lastX: number = 0;
private _lastY: number = 0;
private get _pwidth() { return this.props.PanelWidth(); }
private get _pheight() { return this.props.PanelHeight(); }
- @computed get nativeWidth() { return this.props.Document.GetNumber(KeyStore.NativeWidth, 0); }
+ @computed get nativeWidth() { return FieldValue(this.Document.nativeWidth, 0); }
@computed get nativeHeight() { return this.props.Document.GetNumber(KeyStore.NativeHeight, 0); }
private get borderWidth() { return this.isAnnotationOverlay ? 0 : COLLECTION_BORDER_WIDTH; }
private get isAnnotationOverlay() { return this.props.fieldKey && this.props.fieldKey.Id === KeyStore.Annotations.Id; } // bcz: ? Why do we need to compare Id's?
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index d8de5afec..845213366 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -31,13 +31,13 @@ export interface FieldViewProps {
select: (isCtrlPressed: boolean) => void;
isTopMost: boolean;
selectOnLoad: boolean;
- addDocument?: (document: Document, allowDuplicates?: boolean) => boolean;
- removeDocument?: (document: Document) => boolean;
- moveDocument?: (document: Document, targetCollection: Document, addDocument: (document: Document) => boolean) => boolean;
+ addDocument?: (document: Doc, allowDuplicates?: boolean) => boolean;
+ removeDocument?: (document: Doc) => boolean;
+ moveDocument?: (document: Doc, targetCollection: Doc, addDocument: (document: Doc) => boolean) => boolean;
ScreenToLocalTransform: () => Transform;
active: () => boolean;
whenActiveChanged: (isActive: boolean) => void;
- focus: (doc: Document) => void;
+ focus: (doc: Doc) => void;
}
@observer
diff --git a/src/new_fields/Schema.ts b/src/new_fields/Schema.ts
index 1d1f56844..6022716be 100644
--- a/src/new_fields/Schema.ts
+++ b/src/new_fields/Schema.ts
@@ -1,9 +1,10 @@
import { Interface, ToInterface, Cast, FieldCtor, ToConstructor, HasTail, Head, Tail } from "./Types";
-import { Doc } from "./Doc";
+import { Doc, Field, ObjectField } from "./Doc";
+import { URLField } from "./URLField";
-type All<T extends any[], U extends Doc> = {
- 1: makeInterface<Head<T>, U> & All<Tail<T>, U>,
- 0: makeInterface<Head<T>, U>
+type All<T extends Interface[], U extends Doc> = {
+ 1: makeInterface<[Head<T>], U> & All<Tail<T>, U>,
+ 0: makeInterface<[Head<T>], U>
}[HasTail<T> extends true ? 1 : 0];
type AllToInterface<T extends any[]> = {
@@ -11,10 +12,14 @@ type AllToInterface<T extends any[]> = {
0: ToInterface<Head<T>>
}[HasTail<T> extends true ? 1 : 0];
+export const emptySchema = createSchema({});
+export const Document = makeInterface(emptySchema);
+export type Document = makeInterface<[typeof emptySchema]>;
+
export type makeInterface<T extends Interface[], U extends Doc = Doc> = Partial<AllToInterface<T>> & U;
// export function makeInterface<T extends Interface[], U extends Doc>(schemas: T): (doc: U) => All<T, U>;
// export function makeInterface<T extends Interface, U extends Doc>(schema: T): (doc: U) => makeInterface<T, U>;
-export function makeInterface<T extends Interface[], U extends Doc>(schemas: T): (doc: U) => All<T, U> {
+export function makeInterface<T extends Interface[], U extends Doc>(...schemas: T): (doc: U) => makeInterface<T, U> {
let schema: Interface = {};
for (const s of schemas) {
for (const key in s) {
diff --git a/src/new_fields/Types.ts b/src/new_fields/Types.ts
index 0392dc2fb..e4d15e276 100644
--- a/src/new_fields/Types.ts
+++ b/src/new_fields/Types.ts
@@ -11,7 +11,7 @@ export type ToType<T> =
export type ToConstructor<T> =
T extends string ? "string" :
T extends number ? "number" :
- T extends boolean ? "boolean" : { new(...args: any[]): T };
+ T extends boolean ? "boolean" : new (...args: any[]) => T;
export type ToInterface<T> = {
[P in keyof T]: ToType<T[P]>;