aboutsummaryrefslogtreecommitdiff
path: root/src/client/views
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views')
-rw-r--r--src/client/views/DocComponent.tsx14
-rw-r--r--src/client/views/DocumentDecorations.tsx147
-rw-r--r--src/client/views/collections/CollectionBaseView.tsx104
-rw-r--r--src/client/views/collections/CollectionDockingView.tsx110
-rw-r--r--src/client/views/collections/CollectionSchemaView.tsx150
-rw-r--r--src/client/views/collections/CollectionSubView.tsx339
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx164
-rw-r--r--src/client/views/nodes/AudioBox.tsx27
-rw-r--r--src/client/views/nodes/CollectionFreeFormDocumentView.tsx129
-rw-r--r--src/client/views/nodes/DocumentContentsView.tsx28
-rw-r--r--src/client/views/nodes/DocumentView.tsx129
-rw-r--r--src/client/views/nodes/FieldView.tsx59
-rw-r--r--src/client/views/nodes/FormattedTextBox.tsx41
-rw-r--r--src/client/views/nodes/ImageBox.tsx75
-rw-r--r--src/client/views/nodes/LinkMenu.tsx26
-rw-r--r--src/client/views/nodes/PDFBox.tsx56
-rw-r--r--src/client/views/nodes/VideoBox.tsx37
-rw-r--r--src/client/views/nodes/WebBox.tsx23
18 files changed, 807 insertions, 851 deletions
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
new file mode 100644
index 000000000..d6562492f
--- /dev/null
+++ b/src/client/views/DocComponent.tsx
@@ -0,0 +1,14 @@
+import * as React from 'react';
+import { Doc } from '../../new_fields/Doc';
+import { computed } from 'mobx';
+
+export function DocComponent<P extends { Document: Doc }, T>(schemaCtor: (doc: Doc) => T) {
+ class Component extends React.Component<P> {
+ //TODO This might be pretty inefficient if doc isn't observed, because computed doesn't cache then
+ @computed
+ get Document(): T {
+ return schemaCtor(this.props.Document);
+ }
+ }
+ return Component;
+} \ No newline at end of file
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 06d2f580c..6e361d76a 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -1,24 +1,21 @@
import { action, computed, observable, runInAction } from "mobx";
import { observer } from "mobx-react";
-import { Key } from "../../fields/Key";
-import { KeyStore } from "../../fields/KeyStore";
-import { ListField } from "../../fields/ListField";
-import { NumberField } from "../../fields/NumberField";
-import { TextField } from "../../fields/TextField";
-import { Document } from "../../fields/Document";
import { emptyFunction, Utils } from "../../Utils";
import { DragLinksAsDocuments, DragManager } from "../util/DragManager";
import { SelectionManager } from "../util/SelectionManager";
import { undoBatch } from "../util/UndoManager";
import './DocumentDecorations.scss';
import { MainOverlayTextBox } from "./MainOverlayTextBox";
-import { DocumentView } from "./nodes/DocumentView";
+import { DocumentView, PositionDocument } from "./nodes/DocumentView";
import { LinkMenu } from "./nodes/LinkMenu";
import React = require("react");
import { CompileScript } from "../util/Scripting";
import { IconBox } from "./nodes/IconBox";
-import { FieldValue, Field } from "../../fields/Field";
-import { Documents } from "../documents/Documents";
+import { Cast, FieldValue, NumCast, StrCast } from "../../new_fields/Types";
+import { Doc, FieldResult } from "../../new_fields/Doc";
+import { listSpec } from "../../new_fields/Schema";
+import { Docs } from "../documents/Documents";
+import { List } from "../../new_fields/List";
const higflyout = require("@hig/flyout");
export const { anchorPoints } = higflyout;
export const Flyout = higflyout.default;
@@ -40,8 +37,8 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
@observable private _minimizedX = 0;
@observable private _minimizedY = 0;
//@observable private _title: string = this._documents[0].props.Document.Title;
- @observable private _title: string = this._documents.length > 0 ? this._documents[0].props.Document.Title : "";
- @observable private _fieldKey: Key = KeyStore.Title;
+ @observable private _title: string = this._documents.length > 0 ? Cast(this._documents[0].props.Document.title, "string", "") : "";
+ @observable private _fieldKey: string = "title";
@observable private _hidden = false;
@observable private _opacity = 1;
@observable private _dragging = false;
@@ -69,7 +66,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
var text = e.target.value;
if (text[0] === '#') {
let command = text.slice(1, text.length);
- this._fieldKey = new Key(command);
+ this._fieldKey = command;
// if (command === "Title" || command === "title") {
// this._fieldKey = KeyStore.Title;
// }
@@ -81,14 +78,14 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
}
else {
if (this._documents.length > 0) {
- let field = this._documents[0].props.Document.Get(this._fieldKey);
- if (field instanceof TextField) {
+ let field = this._documents[0].props.Document[this._fieldKey];
+ if (typeof field === "string") {
this._documents.forEach(d =>
- d.props.Document.Set(this._fieldKey, new TextField(this._title)));
+ d.props.Document[this._fieldKey] = this._title);
}
- else if (field instanceof NumberField) {
+ else if (typeof field === "number") {
this._documents.forEach(d =>
- d.props.Document.Set(this._fieldKey, new NumberField(+this._title)));
+ d.props.Document[this._fieldKey] = +this._title);
}
this._title = "changed";
}
@@ -215,7 +212,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
this._minimizedX = snapped ? selDocPos[0] + 4 : e.clientX;
this._minimizedY = snapped ? selDocPos[1] - 18 : e.clientY;
let selectedDocs = SelectionManager.SelectedDocuments().map(sd => sd);
- Promise.all(selectedDocs.map(async selDoc => await this.getIconDoc(selDoc))).then(minDocSet =>
+ Promise.all(selectedDocs.map(selDoc => this.getIconDoc(selDoc))).then(minDocSet =>
this.moveIconDocs(SelectionManager.SelectedDocuments())
);
this._iconifying = snapped;
@@ -223,30 +220,35 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
}
- @action createIcon = (docView: DocumentView, layoutString: string): Document => {
+ @action createIcon = (docView: DocumentView, layoutString: string): Doc => {
let doc = docView.props.Document;
- let iconDoc = Documents.IconDocument(layoutString);
- iconDoc.SetText(KeyStore.Title, "ICON" + doc.Title)
- iconDoc.SetBoolean(KeyStore.IsMinimized, false);
- iconDoc.SetNumber(KeyStore.NativeWidth, 0);
- iconDoc.SetNumber(KeyStore.NativeHeight, 0);
- iconDoc.SetNumber(KeyStore.X, doc.GetNumber(KeyStore.X, 0));
- iconDoc.SetNumber(KeyStore.Y, doc.GetNumber(KeyStore.Y, 0) - 24);
- iconDoc.Set(KeyStore.Prototype, doc);
- iconDoc.Set(KeyStore.MaximizedDoc, doc);
- doc.Set(KeyStore.MinimizedDoc, iconDoc);
+ let iconDoc = Docs.IconDocument(layoutString);
+ iconDoc.title = "ICON" + StrCast(doc.title);
+ iconDoc.isMinimized = false;
+ iconDoc.nativeWidth = 0;
+ iconDoc.nativeHeight = 0;
+ iconDoc.x = NumCast(doc.x);
+ iconDoc.y = NumCast(doc.y) - 24;
+ iconDoc.proto = doc;
+ iconDoc.maximizedDoc = doc;
+ doc.minimizedDoc = iconDoc;
docView.props.addDocument && docView.props.addDocument(iconDoc, false);
return iconDoc;
}
@action
- public getIconDoc = async (docView: DocumentView): Promise<Document | undefined> => {
+ public getIconDoc = async (docView: DocumentView): Promise<Doc | undefined> => {
let doc = docView.props.Document;
- return await doc.GetTAsync(KeyStore.MinimizedDoc, Document).then(async mindoc =>
- mindoc ? mindoc :
- await doc.GetTAsync(KeyStore.BackgroundLayout, TextField).then(async field =>
- (field instanceof TextField) ? this.createIcon(docView, field.Data) :
- await doc.GetTAsync(KeyStore.Layout, TextField).then(field =>
- (field instanceof TextField) ? this.createIcon(docView, field.Data) : undefined)));
+
+ const minDoc = await Cast(doc.minimizedDoc, Doc);
+ if (minDoc) return minDoc;
+
+ const field = StrCast(doc.backgroundLayout, undefined);
+ if (field !== undefined) return this.createIcon(docView, field);
+
+ const layout = StrCast(doc.layout, undefined);
+ if (layout !== undefined) return this.createIcon(docView, field);
+
+ return undefined;
}
@action
onMinimizeUp = (e: PointerEvent): void => {
@@ -255,15 +257,15 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
document.removeEventListener("pointermove", this.onMinimizeMove);
document.removeEventListener("pointerup", this.onMinimizeUp);
let selectedDocs = SelectionManager.SelectedDocuments().map(sd => sd);
- Promise.all(selectedDocs.map(async selDoc => await this.getIconDoc(selDoc))).then(minDocSet => {
- let minDocs = minDocSet.filter(minDoc => minDoc instanceof Document).map(minDoc => minDoc as Document);
+ Promise.all(selectedDocs.map(selDoc => this.getIconDoc(selDoc))).then(minDocSet => {
+ let minDocs = minDocSet.filter(minDoc => minDoc instanceof Doc).map(minDoc => minDoc as Doc);
minDocs.map(minDoc => {
- minDoc.SetNumber(KeyStore.X, minDocs[0].GetNumber(KeyStore.X, 0));
- minDoc.SetNumber(KeyStore.Y, minDocs[0].GetNumber(KeyStore.Y, 0));
- minDoc.SetData(KeyStore.LinkTags, minDocs, ListField);
+ minDoc.x = NumCast(minDocs[0].x);
+ minDoc.y = NumCast(minDocs[0].y);
+ minDoc.linkTags = new List(minDocs);
if (this._iconifying && selectedDocs[0].props.removeDocument) {
selectedDocs[0].props.removeDocument(minDoc);
- (minDoc.Get(KeyStore.MaximizedDoc, false) as Document)!.Set(KeyStore.MinimizedDoc, undefined);
+ (minDoc.maximizedDoc as Doc).minimizedDoc = undefined;
}
});
runInAction(() => this._minimizedX = this._minimizedY = 0);
@@ -272,15 +274,15 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
});
}
}
- moveIconDocs(selViews: DocumentView[], minDocSet?: FieldValue<Field>[]) {
+ moveIconDocs(selViews: DocumentView[], minDocSet?: FieldResult[]) {
selViews.map(selDoc => {
- let minDoc = selDoc.props.Document.Get(KeyStore.MinimizedDoc);
- if (minDoc instanceof Document) {
- let zoom = selDoc.props.Document.GetNumber(KeyStore.ZoomBasis, 1);
+ let minDoc = selDoc.props.Document.minimizedDoc;
+ if (minDoc instanceof Doc) {
+ let zoom = NumCast(selDoc.props.Document.zoomBasis, 1);
let where = (selDoc.props.ScreenToLocalTransform()).scale(selDoc.props.ContentScaling()).scale(1 / zoom).
transformPoint(this._minimizedX - 12, this._minimizedY - 12);
- minDoc.SetNumber(KeyStore.X, where[0] + selDoc.props.Document.GetNumber(KeyStore.X, 0));
- minDoc.SetNumber(KeyStore.Y, where[1] + selDoc.props.Document.GetNumber(KeyStore.Y, 0));
+ minDoc.x = where[0] + NumCast(selDoc.props.Document.x);
+ minDoc.y = where[1] + NumCast(selDoc.props.Document.y);
}
});
}
@@ -317,7 +319,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
document.removeEventListener("pointermove", this.onLinkerButtonMoved);
document.removeEventListener("pointerup", this.onLinkerButtonUp);
let selDoc = SelectionManager.SelectedDocuments()[0];
- let container = selDoc.props.ContainingCollectionView ? selDoc.props.ContainingCollectionView.props.Document.GetPrototype() : undefined;
+ let container = selDoc.props.ContainingCollectionView ? selDoc.props.ContainingCollectionView.props.Document.proto : undefined;
let dragData = new DragManager.LinkDragData(selDoc.props.Document, container ? [container] : []);
DragManager.StartLinkDrag(this._linkerButton.current, dragData, e.pageX, e.pageY, {
handlers: {
@@ -406,28 +408,30 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
const rect = element.ContentDiv ? element.ContentDiv.getBoundingClientRect() : new DOMRect();
if (rect.width !== 0) {
- let doc = element.props.Document;
- let width = doc.GetNumber(KeyStore.Width, 0);
- let nwidth = doc.GetNumber(KeyStore.NativeWidth, 0);
- let nheight = doc.GetNumber(KeyStore.NativeHeight, 0);
- let height = doc.GetNumber(KeyStore.Height, nwidth ? nheight / nwidth * width : 0);
- let x = doc.GetOrCreate(KeyStore.X, NumberField);
- let y = doc.GetOrCreate(KeyStore.Y, NumberField);
+ let doc = PositionDocument(element.props.Document);
+ let width = FieldValue(doc.width, 0);
+ let nwidth = FieldValue(doc.nativeWidth, 0);
+ let nheight = FieldValue(doc.nativeHeight, 0);
+ let height = FieldValue(doc.height, nwidth ? nheight / nwidth * width : 0);
+ let x = FieldValue(doc.x, 0);
+ let y = FieldValue(doc.y, 0);
let scale = width / rect.width;
let actualdW = Math.max(width + (dW * scale), 20);
let actualdH = Math.max(height + (dH * scale), 20);
- x.Data += dX * (actualdW - width);
- y.Data += dY * (actualdH - height);
- var nativeWidth = doc.GetNumber(KeyStore.NativeWidth, 0);
- var nativeHeight = doc.GetNumber(KeyStore.NativeHeight, 0);
+ x += dX * (actualdW - width);
+ y += dY * (actualdH - height);
+ doc.x = x;
+ doc.y = y;
+ var nativeWidth = FieldValue(doc.nativeWidth, 0);
+ var nativeHeight = FieldValue(doc.nativeHeight, 0);
if (nativeWidth > 0 && nativeHeight > 0) {
if (Math.abs(dW) > Math.abs(dH)) {
actualdH = nativeHeight / nativeWidth * actualdW;
}
else actualdW = nativeWidth / nativeHeight * actualdH;
}
- doc.SetNumber(KeyStore.Width, actualdW);
- doc.SetNumber(KeyStore.Height, actualdH);
+ doc.width = actualdW;
+ doc.height = actualdH;
}
});
}
@@ -448,12 +452,12 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
getValue = (): string => {
if (this._documents.length > 0) {
- let field = this._documents[0].props.Document.Get(this._fieldKey);
- if (field instanceof TextField) {
- return (field).GetValue();
+ let field = this._documents[0].props.Document[this._fieldKey];
+ if (typeof field === "string") {
+ return field;
}
- else if (field instanceof NumberField) {
- return (field).GetValue().toString();
+ else if (typeof field === "number") {
+ return field.toString();
}
}
return this._title;
@@ -473,7 +477,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
}
let minimizeIcon = (
<div className="documentDecorations-minimizeButton" onPointerDown={this.onMinimizeDown}>
- {SelectionManager.SelectedDocuments().length == 1 ? IconBox.DocumentIcon(SelectionManager.SelectedDocuments()[0].props.Document.GetText(KeyStore.Layout, "...")) : "..."}
+ {SelectionManager.SelectedDocuments().length === 1 ? IconBox.DocumentIcon(StrCast(SelectionManager.SelectedDocuments()[0].props.Document.layout, "...")) : "..."}
</div>);
if (this.Hidden) {
@@ -487,14 +491,15 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
let linkButton = null;
if (SelectionManager.SelectedDocuments().length > 0) {
let selFirst = SelectionManager.SelectedDocuments()[0];
- let linkToSize = selFirst.props.Document.GetData(KeyStore.LinkedToDocs, ListField, []).length;
- let linkFromSize = selFirst.props.Document.GetData(KeyStore.LinkedFromDocs, ListField, []).length;
+ let linkToSize = Cast(selFirst.props.Document.linkedToDocs, listSpec(Doc), []).length;
+ let linkFromSize = Cast(selFirst.props.Document.linkedFromDocs, listSpec(Doc), []).length;
let linkCount = linkToSize + linkFromSize;
linkButton = (<Flyout
anchorPoint={anchorPoints.RIGHT_TOP}
content={<LinkMenu docView={selFirst}
changeFlyout={this.changeFlyoutContent} />}>
- <div className={"linkButton-" + (selFirst.props.Document.GetData(KeyStore.LinkedToDocs, ListField, []).length ? "nonempty" : "empty")} onPointerDown={this.onLinkButtonDown} >{linkCount}</div>
+ {/* <div className={"linkButton-" + (selFirst.props.Document.GetData(KeyStore.LinkedToDocs, ListField, []).length ? "nonempty" : "empty")} onPointerDown={this.onLinkButtonDown} >{linkCount}</div> */}
+ <div className={"linkButton-empty"} onPointerDown={this.onLinkButtonDown} >{linkCount}</div>
</Flyout>);
}
return (<div className="documentDecorations">
diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx
index 87b767c93..4807dc40a 100644
--- a/src/client/views/collections/CollectionBaseView.tsx
+++ b/src/client/views/collections/CollectionBaseView.tsx
@@ -1,13 +1,12 @@
import { action, computed } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
-import { Document } from '../../../fields/Document';
-import { FieldValue, FieldWaiting } from '../../../fields/Field';
-import { KeyStore } from '../../../fields/KeyStore';
-import { ListField } from '../../../fields/ListField';
-import { NumberField } from '../../../fields/NumberField';
import { ContextMenu } from '../ContextMenu';
import { FieldViewProps } from '../nodes/FieldView';
+import { Cast, FieldValue, PromiseValue, NumCast } from '../../../new_fields/Types';
+import { Doc, FieldResult, Opt, Id } from '../../../new_fields/Doc';
+import { listSpec } from '../../../new_fields/Schema';
+import { List } from '../../../new_fields/List';
export enum CollectionViewType {
Invalid,
@@ -18,9 +17,9 @@ export enum CollectionViewType {
}
export interface CollectionRenderProps {
- 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;
active: () => boolean;
whenActiveChanged: (isActive: boolean) => void;
}
@@ -37,11 +36,9 @@ export interface CollectionViewProps extends FieldViewProps {
export class CollectionBaseView extends React.Component<CollectionViewProps> {
get collectionViewType(): CollectionViewType | undefined {
let Document = this.props.Document;
- let viewField = Document.GetT(KeyStore.ViewType, NumberField);
- if (viewField === FieldWaiting) {
- return undefined;
- } else if (viewField) {
- return viewField.Data;
+ let viewField = Cast(Document.viewType, "number");
+ if (viewField !== undefined) {
+ return viewField;
} else {
return CollectionViewType.Invalid;
}
@@ -60,101 +57,74 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> {
this.props.whenActiveChanged(isActive);
}
- createsCycle(documentToAdd: Document, containerDocument: Document): boolean {
- if (!(documentToAdd instanceof Document)) {
+ createsCycle(documentToAdd: Doc, containerDocument: Doc): boolean {
+ if (!(documentToAdd instanceof Doc)) {
return false;
}
- let data = documentToAdd.GetList(KeyStore.Data, [] as Document[]);
+ let data = Cast(documentToAdd.data, listSpec(Doc), []);
for (const doc of data.filter(d => d instanceof Document)) {
if (this.createsCycle(doc, containerDocument)) {
return true;
}
}
- let annots = documentToAdd.GetList(KeyStore.Annotations, [] as Document[]);
+ let annots = Cast(documentToAdd.annotations, listSpec(Doc), []);
for (const annot of annots) {
if (this.createsCycle(annot, containerDocument)) {
return true;
}
}
- for (let containerProto: FieldValue<Document> = containerDocument; containerProto && containerProto !== FieldWaiting; containerProto = containerProto.GetPrototype()) {
- if (containerProto.Id === documentToAdd.Id) {
+ for (let containerProto: Opt<Doc> = containerDocument; containerProto !== undefined; containerProto = FieldValue(containerProto.proto)) {
+ if (containerProto[Id] === documentToAdd[Id]) {
return true;
}
}
return false;
}
- @computed get isAnnotationOverlay() { return this.props.fieldKey && this.props.fieldKey.Id === KeyStore.Annotations.Id; } // bcz: ? Why do we need to compare Id's?
+ @computed get isAnnotationOverlay() { return this.props.fieldKey && this.props.fieldKey === "annotations"; }
@action.bound
- addDocument(doc: Document, allowDuplicates: boolean = false): boolean {
- var curPage = this.props.Document.GetNumber(KeyStore.CurPage, -1);
- doc.SetOnPrototype(KeyStore.Page, new NumberField(curPage));
+ addDocument(doc: Doc, allowDuplicates: boolean = false): boolean {
+ let props = this.props;
+ var curPage = Cast(props.Document.curPage, "number", -1);
+ Doc.SetOnPrototype(doc, "page", curPage);
if (curPage >= 0) {
- doc.SetOnPrototype(KeyStore.AnnotationOn, this.props.Document);
+ Doc.SetOnPrototype(doc, "annotationOn", props.Document);
}
- if (!this.createsCycle(doc, this.props.Document)) {
- let value = this.props.Document.Get(this.props.fieldKey) as ListField<Document>;
- if (value) {
- if (!value.Data.some(v => v.Id === doc.Id) || allowDuplicates) {
- value.Data.push(doc);
+ if (!this.createsCycle(doc, props.Document)) {
+ //TODO This won't create the field if it doesn't already exist
+ const value = Cast(props.Document[props.fieldKey], listSpec(Doc));
+ if (value !== undefined) {
+ if (allowDuplicates || !value.some(v => v.Id === doc.Id)) {
+ value.push(doc);
}
} else {
- this.props.Document.Set(this.props.fieldKey, new ListField([doc]));
+ this.props.Document[this.props.fieldKey] = new List([doc]);
}
// set the ZoomBasis only if hasn't already been set -- bcz: maybe set/resetting the ZoomBasis should be a parameter to addDocument?
if (this.collectionViewType === CollectionViewType.Freeform || this.collectionViewType === CollectionViewType.Invalid) {
- let zoom = this.props.Document.GetNumber(KeyStore.Scale, 1);
+ let zoom = NumCast(this.props.Document.scale, 1);
let screen = this.props.ScreenToLocalTransform().inverse().Scale / (this.props as any).ContentScaling() * zoom;
- doc.SetNumber(KeyStore.ZoomBasis, screen);
+ doc.zoomBasis = screen;
}
}
return true;
- // bcz: What is this code trying to do?
- // else {
- // let proto = props.Document.GetPrototype();
- // if (!proto || proto === FieldWaiting || !this.createsCycle(proto, doc)) {
- // const field = new ListField([doc]);
- // // const script = CompileScript(`
- // // if(added) {
- // // console.log("added " + field.Title + " " + doc.Title);
- // // } else {
- // // console.log("removed " + field.Title + " " + doc.Title);
- // // }
- // // `, {
- // // addReturn: false,
- // // params: {
- // // field: Document.name,
- // // added: "boolean"
- // // },
- // // capturedVariables: {
- // // doc: this.props.Document
- // // }
- // // });
- // // if (script.compiled) {
- // // field.addScript(new ScriptField(script));
- // // }
- // props.Document.SetOnPrototype(props.fieldKey, field);
- // return true;
- // }
- // }
- return false;
}
@action.bound
- removeDocument(doc: Document): boolean {
+ removeDocument(doc: Doc): boolean {
const props = this.props;
//TODO This won't create the field if it doesn't already exist
- const value = props.Document.GetData(props.fieldKey, ListField, new Array<Document>());
+ const value = Cast(props.Document[props.fieldKey], listSpec(Doc), []);
let index = -1;
for (let i = 0; i < value.length; i++) {
- if (value[i].Id === doc.Id) {
+ if (value[i][Id] === doc[Id]) {
index = i;
break;
}
}
- doc.GetTAsync(KeyStore.AnnotationOn, Document).then((annotationOn) => {
+ PromiseValue(Cast(doc.annotationOn, Doc)).then((annotationOn) => {
if (annotationOn === props.Document) {
- doc.Set(KeyStore.AnnotationOn, undefined, true);
+ doc.annotationOn = undefined;
}
});
@@ -169,7 +139,7 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> {
}
@action.bound
- moveDocument(doc: Document, targetCollection: Document, addDocument: (doc: Document) => boolean): boolean {
+ moveDocument(doc: Doc, targetCollection: Doc, addDocument: (doc: Doc) => boolean): boolean {
if (this.props.Document === targetCollection) {
return true;
}
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 3267f2455..ec5f823b0 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -4,11 +4,8 @@ import 'golden-layout/src/css/goldenlayout-dark-theme.css';
import { action, observable, reaction, trace } from "mobx";
import { observer } from "mobx-react";
import * as ReactDOM from 'react-dom';
-import { Document } from "../../../fields/Document";
-import { KeyStore } from "../../../fields/KeyStore";
import Measure from "react-measure";
-import { FieldId, Opt, Field, FieldWaiting } from "../../../fields/Field";
-import { Utils, returnTrue, emptyFunction, emptyDocFunction, returnOne, returnZero } from "../../../Utils";
+import { Utils, returnTrue, emptyFunction, returnOne, returnZero } from "../../../Utils";
import { Server } from "../../Server";
import { undoBatch } from "../../util/UndoManager";
import { DocumentView } from "../nodes/DocumentView";
@@ -17,20 +14,23 @@ import React = require("react");
import { SubCollectionViewProps } from "./CollectionSubView";
import { ServerUtils } from "../../../server/ServerUtil";
import { DragManager, DragLinksAsDocuments } from "../../util/DragManager";
-import { TextField } from "../../../fields/TextField";
-import { ListField } from "../../../fields/ListField";
-import { Transform } from '../../util/Transform'
+import { Transform } from '../../util/Transform';
+import { Doc, Id, Opt, Field, FieldId } from "../../../new_fields/Doc";
+import { Cast, NumCast } from "../../../new_fields/Types";
+import { List } from "../../../new_fields/List";
+import { DocServer } from "../../DocServer";
+import { listSpec } from "../../../new_fields/Schema";
@observer
export class CollectionDockingView extends React.Component<SubCollectionViewProps> {
public static Instance: CollectionDockingView;
- public static makeDocumentConfig(document: Document) {
+ public static makeDocumentConfig(document: Doc) {
return {
type: 'react-component',
component: 'DocumentFrameRenderer',
- title: document.Title,
+ title: document.title,
props: {
- documentId: document.Id,
+ documentId: document[Id],
//collectionDockingView: CollectionDockingView.Instance
}
};
@@ -48,14 +48,14 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
(window as any).React = React;
(window as any).ReactDOM = ReactDOM;
}
- public StartOtherDrag(dragDocs: Document[], e: any) {
+ public StartOtherDrag(dragDocs: Doc[], e: any) {
dragDocs.map(dragDoc =>
this.AddRightSplit(dragDoc, true).contentItems[0].tab._dragListener.
onMouseDown({ pageX: e.pageX, pageY: e.pageY, preventDefault: emptyFunction, button: 0 }));
}
@action
- public OpenFullScreen(document: Document) {
+ public OpenFullScreen(document: Doc) {
let newItemStackConfig = {
type: 'stack',
content: [CollectionDockingView.makeDocumentConfig(document)]
@@ -83,7 +83,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
// Creates a vertical split on the right side of the docking view, and then adds the Document to that split
//
@action
- public AddRightSplit(document: Document, minimize: boolean = false) {
+ public AddRightSplit(document: Doc, minimize: boolean = false) {
let newItemStackConfig = {
type: 'stack',
content: [CollectionDockingView.makeDocumentConfig(document)]
@@ -119,7 +119,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
}
setupGoldenLayout() {
- var config = this.props.Document.GetText(KeyStore.Data, "");
+ var config = Cast(this.props.Document.data, "string", "");
if (config) {
if (!this._goldenLayout) {
this._goldenLayout = new GoldenLayout(JSON.parse(config));
@@ -154,7 +154,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
componentDidMount: () => void = () => {
if (this._containerRef.current) {
reaction(
- () => this.props.Document.GetText(KeyStore.Data, ""),
+ () => Cast(this.props.Document.data, "string", ""),
() => {
if (!this._goldenLayout || this._ignoreStateChange !== JSON.stringify(this._goldenLayout.toConfig())) {
setTimeout(() => this.setupGoldenLayout(), 1);
@@ -203,7 +203,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
let docid = (e.target as any).DashDocId;
let tab = (e.target as any).parentElement as HTMLElement;
Server.GetField(docid, action(async (sourceDoc: Opt<Field>) =>
- (sourceDoc instanceof Document) && DragLinksAsDocuments(tab, x, y, sourceDoc)));
+ (sourceDoc instanceof Doc) && DragLinksAsDocuments(tab, x, y, sourceDoc)));
} else
if ((className === "lm_title" || className === "lm_tab lm_active") && !e.shiftKey) {
e.stopPropagation();
@@ -213,11 +213,11 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
let docid = (e.target as any).DashDocId;
let tab = (e.target as any).parentElement as HTMLElement;
Server.GetField(docid, action((f: Opt<Field>) => {
- if (f instanceof Document) {
+ if (f instanceof Doc) {
DragManager.StartDocumentDrag([tab], new DragManager.DocumentDragData([f]), x, y,
{
handlers: {
- dragComplete: action(emptyFunction),
+ dragComplete: emptyFunction,
},
hideSource: false
});
@@ -235,7 +235,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
@undoBatch
stateChanged = () => {
var json = JSON.stringify(this._goldenLayout.toConfig());
- this.props.Document.SetText(KeyStore.Data, json);
+ this.props.Document.data = json;
}
itemDropped = () => {
@@ -251,29 +251,26 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
tabCreated = (tab: any) => {
if (tab.hasOwnProperty("contentItem") && tab.contentItem.config.type !== "stack") {
- Server.GetField(tab.contentItem.config.props.documentId, action((f: Opt<Field>) => {
- if (f !== undefined && f instanceof Document) {
- f.GetTAsync(KeyStore.Title, TextField, (tfield) => {
- if (tfield !== undefined) {
- tab.titleElement[0].textContent = f.Title;
- }
- });
- f.GetTAsync(KeyStore.LinkedFromDocs, ListField).then(lf =>
- f.GetTAsync(KeyStore.LinkedToDocs, ListField).then(lt => {
- let count = (lf ? lf.Data.length : 0) + (lt ? lt.Data.length : 0);
- let counter: any = this.htmlToElement(`<div class="messageCounter">${count}</div>`);
- tab.element.append(counter);
- counter.DashDocId = tab.contentItem.config.props.documentId;
- tab.reactionDisposer = reaction(() => [f.GetT(KeyStore.LinkedFromDocs, ListField), f.GetT(KeyStore.LinkedToDocs, ListField)],
- (lists) => {
- let count = (lists.length > 0 && lists[0] && lists[0]!.Data ? lists[0]!.Data.length : 0) +
- (lists.length > 1 && lists[1] && lists[1]!.Data ? lists[1]!.Data.length : 0);
- counter.innerHTML = count;
- });
- }));
+ DocServer.GetRefField(tab.contentItem.config.props.documentId).then(async f => {
+ if (f instanceof Doc) {
+ const tfield = await Cast(f.title, "string");
+ if (tfield !== undefined) {
+ tab.titleElement[0].textContent = f.Title;
+ }
+ const lf = await Cast(f.linkedFromDocs, listSpec(Doc));
+ const lt = await Cast(f.linkedToDocs, listSpec(Doc));
+ let count = (lf ? lf.length : 0) + (lt ? lt.length : 0);
+ let counter: any = this.htmlToElement(`<div class="messageCounter">${count}</div>`);
+ tab.element.append(counter);
+ counter.DashDocId = tab.contentItem.config.props.documentId;
+ tab.reactionDisposer = reaction((): [List<Field> | null | undefined, List<Field> | null | undefined] => [lf, lt],
+ ([linkedFrom, linkedTo]) => {
+ let count = (linkedFrom ? linkedFrom.length : 0) + (linkedTo ? linkedTo.length : 0);
+ counter.innerHTML = count;
+ });
tab.titleElement[0].DashDocId = tab.contentItem.config.props.documentId;
}
- }));
+ });
}
tab.closeElement.off('click') //unbind the current click handler
.click(function () {
@@ -319,32 +316,29 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
_mainCont = React.createRef<HTMLDivElement>();
@observable private _panelWidth = 0;
@observable private _panelHeight = 0;
- @observable private _document: Opt<Document>;
+ @observable private _document: Opt<Doc>;
constructor(props: any) {
super(props);
- Server.GetField(this.props.documentId, action((f: Opt<Field>) => this._document = f as Document));
+ DocServer.GetRefField(this.props.documentId).then(action((f: Opt<Field>) => this._document = f as Doc));
}
- nativeWidth = () => {
- let pw = this._document!.GetNumber(KeyStore.NativeWidth, 0);
- return pw ? pw : this._panelWidth;
- }
- nativeHeight = () => {
- let pw = this._document!.GetNumber(KeyStore.NativeHeight, 0);
- return pw ? pw : this._panelHeight;
- }
+ nativeWidth = () => NumCast(this._document!.nativeWidth, this._panelWidth);
+ nativeHeight = () => NumCast(this._document!.nativeHeight, this._panelHeight);
contentScaling = () => {
- let wscale = this._panelWidth / (this.nativeWidth() ? this.nativeWidth() : this._panelWidth);
- if (wscale * this.nativeHeight() > this._panelHeight)
- return this._panelHeight / (this.nativeHeight() ? this.nativeHeight() : this._panelHeight);
+ const nativeH = this.nativeHeight();
+ const nativeW = this.nativeWidth();
+ let wscale = this._panelWidth / nativeW;
+ if (wscale * nativeH > this._panelHeight) {
+ return this._panelHeight / nativeH;
+ }
return wscale;
}
ScreenToLocalTransform = () => {
if (this._mainCont.current && this._mainCont.current.children) {
- let { scale, translateX, translateY } = Utils.GetScreenTransform(this._mainCont.current!.children[0].firstChild as HTMLElement);
- scale = Utils.GetScreenTransform(this._mainCont.current!).scale;
+ let { scale, translateX, translateY } = Utils.GetScreenTransform(this._mainCont.current.children[0].firstChild as HTMLElement);
+ scale = Utils.GetScreenTransform(this._mainCont.current).scale;
return CollectionDockingView.Instance.props.ScreenToLocalTransform().translate(-translateX, -translateY).scale(scale / this.contentScaling());
}
return Transform.Identity();
@@ -355,7 +349,7 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
return (
<div className="collectionDockingView-content" ref={this._mainCont}
style={{ transform: `translate(${this.previewPanelCenteringOffset}px, 0px)` }}>
- <DocumentView key={this._document!.Id} Document={this._document!}
+ <DocumentView key={this._document![Id]} Document={this._document!}
toggleMinimized={emptyFunction}
addDocument={undefined}
removeDocument={undefined}
@@ -367,9 +361,9 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
selectOnLoad={false}
parentActive={returnTrue}
whenActiveChanged={emptyFunction}
- focus={emptyDocFunction}
+ focus={emptyFunction}
ContainingCollectionView={undefined} />
- </div>);
+ </div >);
}
render() {
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index b6d5f1bfa..2e1175f28 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -7,15 +7,9 @@ import { observer } from "mobx-react";
import ReactTable, { CellInfo, ComponentPropsGetterR, ReactTableDefaults } from "react-table";
import { MAX_ROW_HEIGHT } from '../../views/globalCssVariables.scss'
import "react-table/react-table.css";
-import { Document } from "../../../fields/Document";
-import { Field, Opt } from "../../../fields/Field";
-import { Key } from "../../../fields/Key";
-import { KeyStore } from "../../../fields/KeyStore";
-import { ListField } from "../../../fields/ListField";
-import { emptyDocFunction, emptyFunction, returnFalse, returnZero } from "../../../Utils";
-import { Server } from "../../Server";
+import { emptyFunction, returnFalse, returnZero } from "../../../Utils";
import { SetupDrag } from "../../util/DragManager";
-import { CompileScript, ToField } from "../../util/Scripting";
+import { CompileScript } from "../../util/Scripting";
import { Transform } from "../../util/Transform";
import { COLLECTION_BORDER_WIDTH } from "../../views/globalCssVariables.scss";
import { anchorPoints, Flyout } from "../DocumentDecorations";
@@ -25,43 +19,45 @@ import { DocumentView } from "../nodes/DocumentView";
import { FieldView, FieldViewProps } from "../nodes/FieldView";
import "./CollectionSchemaView.scss";
import { CollectionSubView } from "./CollectionSubView";
+import { Opt, Field, Doc, Id } from "../../../new_fields/Doc";
+import { Cast, FieldValue, NumCast } from "../../../new_fields/Types";
+import { listSpec } from "../../../new_fields/Schema";
+import { List } from "../../../new_fields/List";
// bcz: need to add drag and drop of rows and columns. This seems like it might work for rows: https://codesandbox.io/s/l94mn1q657
@observer
-class KeyToggle extends React.Component<{ keyId: string, checked: boolean, toggle: (key: Key) => void }> {
- @observable key: Key | undefined;
-
+class KeyToggle extends React.Component<{ keyName: string, checked: boolean, toggle: (key: string) => void }> {
constructor(props: any) {
super(props);
- Server.GetField(this.props.keyId, action((field: Opt<Field>) => field instanceof Key && (this.key = field)));
}
render() {
- return !this.key ? (null) :
- (<div key={this.key.Id}>
- <input type="checkbox" checked={this.props.checked} onChange={() => this.key && this.props.toggle(this.key)} />
- {this.key.Name}
- </div>);
+ return (
+ <div key={this.props.keyName}>
+ <input type="checkbox" checked={this.props.checked} onChange={() => this.props.toggle(this.props.keyName)} />
+ {this.props.keyName}
+ </div>
+ );
}
}
@observer
-export class CollectionSchemaView extends CollectionSubView {
+export class CollectionSchemaView extends CollectionSubView(doc => doc) {
private _mainCont?: HTMLDivElement;
private _startSplitPercent = 0;
private DIVIDER_WIDTH = 4;
- @observable _columns: Array<Key> = [KeyStore.Title, KeyStore.Data, KeyStore.Author];
+ @observable _columns: Array<string> = ["title", "data", "author"];
@observable _selectedIndex = 0;
@observable _columnsPercentage = 0;
- @observable _keys: Key[] = [];
+ @observable _keys: string[] = [];
@observable _newKeyName: string = "";
- @computed get splitPercentage() { return this.props.Document.GetNumber(KeyStore.SchemaSplitPercentage, 0); }
- @computed get columns() { return this.props.Document.GetList(KeyStore.ColumnsKey, [] as Key[]); }
+ @computed get splitPercentage() { return NumCast(this.props.Document.schemaSplitPercentage); }
+ @computed get columns() { return Cast(this.props.Document.columns, listSpec("string"), []); }
@computed get borderWidth() { return Number(COLLECTION_BORDER_WIDTH); }
renderCell = (rowProps: CellInfo) => {
@@ -74,7 +70,7 @@ export class CollectionSchemaView extends CollectionSubView {
isTopMost: false,
selectOnLoad: false,
ScreenToLocalTransform: Transform.Identity,
- focus: emptyDocFunction,
+ focus: emptyFunction,
active: returnFalse,
whenActiveChanged: emptyFunction,
PanelHeight: returnZero,
@@ -85,34 +81,27 @@ export class CollectionSchemaView extends CollectionSubView {
);
let reference = React.createRef<HTMLDivElement>();
let onItemDown = SetupDrag(reference, () => props.Document, this.props.moveDocument);
- let applyToDoc = (doc: Document, run: (args?: { [name: string]: any }) => any) => {
+ let applyToDoc = (doc: Doc, run: (args?: { [name: string]: any }) => any) => {
const res = run({ this: doc });
if (!res.success) return false;
const field = res.result;
- if (field instanceof Field) {
- doc.Set(props.fieldKey, field);
- return true;
- } else {
- let dataField = ToField(field);
- if (dataField) {
- doc.Set(props.fieldKey, dataField);
- return true;
- }
- }
- return false;
+ doc[props.fieldKey] = field;
+ return true;
};
return (
- <div className="collectionSchemaView-cellContents" onPointerDown={onItemDown} key={props.Document.Id} ref={reference}>
+ <div className="collectionSchemaView-cellContents" onPointerDown={onItemDown} key={props.Document[Id]} ref={reference}>
<EditableView
display={"inline"}
contents={contents}
height={Number(MAX_ROW_HEIGHT)}
GetValue={() => {
- let field = props.Document.Get(props.fieldKey);
- if (field && field instanceof Field) {
- return field.ToScriptString();
+ let field = props.Document[props.fieldKey];
+ if (field) {
+ //TODO Types
+ // return field.ToScriptString();
+ return String(field);
}
- return field || "";
+ return "";
}}
SetValue={(value: string) => {
let script = CompileScript(value, { addReturn: true, params: { this: Document.name } });
@@ -128,14 +117,13 @@ export class CollectionSchemaView extends CollectionSubView {
}
const run = script.run;
//TODO This should be able to be refactored to compile the script once
- this.props.Document.GetTAsync<ListField<Document>>(this.props.fieldKey, ListField).then((val) => {
- if (val) {
- val.Data.forEach(doc => applyToDoc(doc, run));
- }
- });
+ const val = Cast(this.props.Document[this.props.fieldKey], listSpec(Doc));
+ if (val) {
+ val.forEach(doc => applyToDoc(doc, run));
+ }
}}>
</EditableView>
- </div>
+ </div >
);
}
@@ -166,36 +154,37 @@ export class CollectionSchemaView extends CollectionSubView {
}
@action
- toggleKey = (key: Key) => {
- this.props.Document.GetOrCreateAsync<ListField<Key>>(KeyStore.ColumnsKey, ListField,
- (field) => {
- const index = field.Data.indexOf(key);
- if (index === -1) {
- this.columns.push(key);
- } else {
- this.columns.splice(index, 1);
- }
-
- });
+ toggleKey = (key: string) => {
+ let list = Cast(this.props.Document.columns, listSpec("string"));
+ if (list === undefined) {
+ this.props.Document.columns = list = new List<string>([key]);
+ } else {
+ const index = list.indexOf(key);
+ if (index === -1) {
+ list.push(key);
+ } else {
+ list.splice(index, 1);
+ }
+ }
}
//toggles preview side-panel of schema
@action
toggleExpander = (event: React.ChangeEvent<HTMLInputElement>) => {
- this.props.Document.SetNumber(KeyStore.SchemaSplitPercentage, this.splitPercentage === 0 ? 33 : 0);
+ this.props.Document.schemaSplitPercentage = this.splitPercentage === 0 ? 33 : 0;
}
@action
onDividerMove = (e: PointerEvent): void => {
let nativeWidth = this._mainCont!.getBoundingClientRect();
- this.props.Document.SetNumber(KeyStore.SchemaSplitPercentage, Math.max(0, 100 - Math.round((e.clientX - nativeWidth.left) / nativeWidth.width * 100)));
+ this.props.Document.schemaSplitPercentage = Math.max(0, 100 - Math.round((e.clientX - nativeWidth.left) / nativeWidth.width * 100));
}
@action
onDividerUp = (e: PointerEvent): void => {
document.removeEventListener("pointermove", this.onDividerMove);
document.removeEventListener('pointerup', this.onDividerUp);
if (this._startSplitPercent === this.splitPercentage) {
- this.props.Document.SetNumber(KeyStore.SchemaSplitPercentage, this.splitPercentage === 0 ? 33 : 0);
+ this.props.Document.schemaSplitPercentage = this.splitPercentage === 0 ? 33 : 0;
}
}
onDividerDown = (e: React.PointerEvent) => {
@@ -208,8 +197,7 @@ export class CollectionSchemaView extends CollectionSubView {
onPointerDown = (e: React.PointerEvent): void => {
if (e.button === 0 && !e.altKey && !e.ctrlKey && !e.metaKey) {
- if (this.props.isSelected())
- e.stopPropagation();
+ if (this.props.isSelected()) e.stopPropagation();
else e.preventDefault();
}
}
@@ -222,7 +210,7 @@ export class CollectionSchemaView extends CollectionSubView {
@action
addColumn = () => {
- this.columns.push(new Key(this._newKeyName));
+ this.columns.push(this._newKeyName);
this._newKeyName = "";
}
@@ -237,21 +225,22 @@ export class CollectionSchemaView extends CollectionSubView {
this.previewScript = e.currentTarget.value;
}
- get previewDocument(): Document | undefined {
- const children = this.props.Document.GetList(this.props.fieldKey, [] as Document[]);
+ get previewDocument(): Doc | undefined {
+ const children = Cast(this.props.Document[this.props.fieldKey], listSpec(Doc), []);
const selected = children.length > this._selectedIndex ? children[this._selectedIndex] : undefined;
- return selected ? (this.previewScript ? selected.Get(new Key(this.previewScript)) as Document : selected) : undefined;
+ return selected ? (this.previewScript ? FieldValue(Cast(selected[this.previewScript], Doc)) : selected) : undefined;
}
get tableWidth() { return (this.props.PanelWidth() - 2 * this.borderWidth - this.DIVIDER_WIDTH) * (1 - this.splitPercentage / 100); }
get previewRegionHeight() { return this.props.PanelHeight() - 2 * this.borderWidth; }
get previewRegionWidth() { return (this.props.PanelWidth() - 2 * this.borderWidth - this.DIVIDER_WIDTH) * this.splitPercentage / 100; }
- private previewDocNativeWidth = () => this.previewDocument!.GetNumber(KeyStore.NativeWidth, this.previewRegionWidth);
- private previewDocNativeHeight = () => this.previewDocument!.GetNumber(KeyStore.NativeHeight, this.previewRegionHeight);
+ private previewDocNativeWidth = () => Cast(this.previewDocument!.nativeWidth, "number", this.previewRegionWidth);
+ private previewDocNativeHeight = () => Cast(this.previewDocument!.nativeHeight, "number", this.previewRegionHeight);
private previewContentScaling = () => {
let wscale = this.previewRegionWidth / (this.previewDocNativeWidth() ? this.previewDocNativeWidth() : this.previewRegionWidth);
- if (wscale * this.previewDocNativeHeight() > this.previewRegionHeight)
+ if (wscale * this.previewDocNativeHeight() > this.previewRegionHeight) {
return this.previewRegionHeight / (this.previewDocNativeHeight() ? this.previewDocNativeHeight() : this.previewRegionHeight);
+ }
return wscale;
}
private previewPanelWidth = () => this.previewDocNativeWidth() * this.previewContentScaling();
@@ -259,7 +248,7 @@ export class CollectionSchemaView extends CollectionSubView {
get previewPanelCenteringOffset() { return (this.previewRegionWidth - this.previewDocNativeWidth() * this.previewContentScaling()) / 2; }
getPreviewTransform = (): Transform => this.props.ScreenToLocalTransform().translate(
- this.borderWidth - this.DIVIDER_WIDTH - this.tableWidth - this.previewPanelCenteringOffset,
- - this.borderWidth).scale(1 / this.previewContentScaling());
+ - this.borderWidth).scale(1 / this.previewContentScaling())
@computed
get previewPanel() {
@@ -274,7 +263,7 @@ export class CollectionSchemaView extends CollectionSubView {
ContentScaling={this.previewContentScaling}
PanelWidth={this.previewPanelWidth} PanelHeight={this.previewPanelHeight}
ContainingCollectionView={this.props.CollectionView}
- focus={emptyDocFunction}
+ focus={emptyFunction}
parentActive={this.props.active}
whenActiveChanged={this.props.whenActiveChanged}
/>
@@ -286,18 +275,19 @@ export class CollectionSchemaView extends CollectionSubView {
}
get documentKeysCheckList() {
- const docs = this.props.Document.GetList(this.props.fieldKey, [] as Document[]);
- let keys: { [id: string]: boolean } = {};
+ const docs = Cast(this.props.Document[this.props.fieldKey], listSpec(Doc), []);
+ let keys: { [key: string]: boolean } = {};
// bcz: ugh. this is untracked since otherwise a large collection of documents will blast the server for all their fields.
// then as each document's fields come back, we update the documents _proxies. Each time we do this, the whole schema will be
// invalidated and re-rendered. This workaround will inquire all of the document fields before the options button is clicked.
// then by the time the options button is clicked, all of the fields should be in place. If a new field is added while this menu
// is displayed (unlikely) it won't show up until something else changes.
- untracked(() => docs.map(doc => doc.GetAllPrototypes().map(proto => proto._proxies.forEach((val: any, key: string) => keys[key] = false))));
+ //TODO Types
+ untracked(() => docs.map(doc => Doc.GetAllPrototypes(doc).map(proto => proto._proxies.forEach((val: any, key: string) => keys[key] = false))));
- this.columns.forEach(key => keys[key.Id] = true);
+ this.columns.forEach(key => keys[key] = true);
return Array.from(Object.keys(keys)).map(item =>
- (<KeyToggle checked={keys[item]} key={item} keyId={item} toggle={this.toggleKey} />));
+ (<KeyToggle checked={keys[item]} key={item} keyName={item} toggle={this.toggleKey} />));
}
get tableOptionsPanel() {
@@ -331,16 +321,16 @@ export class CollectionSchemaView extends CollectionSubView {
render() {
library.add(faCog);
library.add(faPlus);
- const children = this.props.Document.GetList(this.props.fieldKey, [] as Document[]);
+ const children = Cast(this.props.Document[this.props.fieldKey], listSpec(Doc), []);
return (
<div className="collectionSchemaView-container" onPointerDown={this.onPointerDown} onWheel={this.onWheel}
onDrop={(e: React.DragEvent) => this.onDrop(e, {})} ref={this.createTarget}>
<div className="collectionSchemaView-tableContainer" style={{ width: `${this.tableWidth}px` }}>
<ReactTable data={children} page={0} pageSize={children.length} showPagination={false}
columns={this.columns.map(col => ({
- Header: col.Name,
- accessor: (doc: Document) => [doc, col],
- id: col.Id
+ Header: col,
+ accessor: (doc: Doc) => [doc, col],
+ id: col
}))}
column={{ ...ReactTableDefaults.column, Cell: this.renderCell, }}
getTrProps={this.getTrProps}
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index f9fc7be5a..558a8728f 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -1,28 +1,26 @@
import { action, runInAction } from "mobx";
-import { Document } from "../../../fields/Document";
-import { ListField } from "../../../fields/ListField";
import React = require("react");
-import { KeyStore } from "../../../fields/KeyStore";
-import { FieldWaiting, Opt } from "../../../fields/Field";
import { undoBatch, UndoManager } from "../../util/UndoManager";
import { DragManager } from "../../util/DragManager";
-import { Documents, DocumentOptions } from "../../documents/Documents";
+import { Docs, DocumentOptions } from "../../documents/Documents";
import { RouteStore } from "../../../server/RouteStore";
-import { TupleField } from "../../../fields/TupleField";
import { CurrentUserUtils } from "../../../server/authentication/models/current_user_utils";
-import { NumberField } from "../../../fields/NumberField";
-import { ServerUtils } from "../../../server/ServerUtil";
-import { Server } from "../../Server";
import { FieldViewProps } from "../nodes/FieldView";
import * as rp from 'request-promise';
import { CollectionView } from "./CollectionView";
import { CollectionPDFView } from "./CollectionPDFView";
import { CollectionVideoView } from "./CollectionVideoView";
+import { Doc, ObjectField, Opt } from "../../../new_fields/Doc";
+import { DocComponent } from "../DocComponent";
+import { listSpec } from "../../../new_fields/Schema";
+import { Cast, PromiseValue } from "../../../new_fields/Types";
+import { List } from "../../../new_fields/List";
+import { DocServer } from "../../DocServer";
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,176 +31,193 @@ 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 => {
+ get children() {
+ return Cast(this.props.Document[this.props.fieldKey], listSpec(Doc));
+ }
+
+ @action
+ protected async 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];
+ const proto = await doc.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);
- }
- }));
- });
+ let cursors = await Cast(proto.cursors, listSpec(ObjectField));
+ if (!cursors) {
+ proto.cursors = cursors = new List<ObjectField>();
+ }
+ 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) {
+ ["width", "height", "curPage"].map(key =>
+ de.data.draggedDocuments.map((draggedDocument: Doc, i: number) =>
+ PromiseValue(Cast(draggedDocument[key], "number")).then(f => f && (de.data.droppedDocuments[i][key] = f))));
+ }
+ 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<Doc>> {
+ let ctor: ((path: string, options: DocumentOptions) => (Doc | Promise<Doc | undefined>)) | undefined = undefined;
+ if (type.indexOf("image") !== -1) {
+ ctor = Docs.ImageDocument;
+ }
+ if (type.indexOf("video") !== -1) {
+ ctor = Docs.VideoDocument;
+ }
+ if (type.indexOf("audio") !== -1) {
+ ctor = Docs.AudioDocument;
}
- ctor = Documents.WebDocument;
- options = { height: options.width, ...options, title: path, nativeWidth: undefined };
+ if (type.indexOf("pdf") !== -1) {
+ ctor = Docs.PdfDocument;
+ options.nativeWidth = 1200;
+ }
+ if (type.indexOf("excel") !== -1) {
+ ctor = Docs.DBDocument;
+ options.copyDraggedItems = true;
+ }
+ if (type.indexOf("html") !== -1) {
+ if (path.includes('localhost')) {
+ let s = path.split('/');
+ let id = s[s.length - 1];
+ DocServer.GetRefField(id).then(field => {
+ if (field instanceof Doc) {
+ let alias = Doc.MakeAlias(field);
+ alias.x = options.x || 0;
+ alias.y = options.y || 0;
+ alias.width = options.width || 300;
+ alias.height = options.height || options.width || 300;
+ this.props.addDocument(alias, false);
+ }
+ });
+ return undefined;
+ }
+ ctor = Docs.WebDocument;
+ options = { height: options.width, ...options, title: path, nativeWidth: undefined };
+ }
+ 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 (text && text.startsWith("<div")) {
+ return;
+ }
+ e.stopPropagation();
+ e.preventDefault();
- if (html && html.indexOf("<img") !== 0 && !html.startsWith("<a")) {
- this.props.addDocument(Documents.HtmlDocument(html, { ...options, width: 300, height: 300, documentText: text }), false);
- return;
- }
+ if (html && html.indexOf("<img") !== 0 && !html.startsWith("<a")) {
+ let htmlDoc = Docs.HtmlDocument(html, { ...options, width: 300, height: 300, documentText: text });
+ this.props.addDocument(htmlDoc, false);
+ return;
+ }
- 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["content-type"];
- if (type) {
- this.getDocumentFromType(type, str, { ...options, width: 300, nativeWidth: 300 })
- .then(doc => doc && this.props.addDocument(doc, false));
- }
+ 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(DocServer.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 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(doc => doc && this.props.addDocument(doc));
+ }));
});
- promises.push(prom);
- }
- let type = item.type;
- if (item.kind === "file") {
- let file = item.getAsFile();
- let dropFileName = file ? file.name : "-empty-";
- let formData = new FormData();
- if (file) formData.append('file', file);
-
- promises.push(fetch(upload, {
- method: 'POST',
- body: formData
- }).then(async (res: Response) =>
- (await res.json()).map(action((file: any) =>
- this.getDocumentFromType(type, window.location.origin + file, { ...options, nativeWidth: 600, width: 300, title: dropFileName }).
- then(doc => doc && this.props.addDocument(doc, false))))));
+ 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 56342c84c..80900c450 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1,7 +1,5 @@
import { action, computed } 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";
@@ -12,7 +10,7 @@ import { COLLECTION_BORDER_WIDTH } from "../../../views/globalCssVariables.scss"
import { InkingCanvas } from "../../InkingCanvas";
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";
@@ -20,10 +18,23 @@ import "./CollectionFreeFormView.scss";
import { MarqueeView } from "./MarqueeView";
import React = require("react");
import v5 = require("uuid/v5");
-import { BooleanField } from "../../../../fields/BooleanField";
+import { createSchema, makeInterface, listSpec } from "../../../../new_fields/Schema";
+import { Doc, Id } from "../../../../new_fields/Doc";
+import { FieldValue, Cast, NumCast } from "../../../../new_fields/Types";
+import { pageSchema } from "../../nodes/ImageBox";
+import { List } from "../../../../new_fields/List";
+
+export const panZoomSchema = createSchema({
+ panX: "number",
+ panY: "number",
+ scale: "number"
+});
+
+type PanZoomDocument = makeInterface<[typeof panZoomSchema, typeof positionSchema, typeof pageSchema]>;
+const PanZoomDocument = makeInterface(panZoomSchema, positionSchema, pageSchema);
@observer
-export class CollectionFreeFormView extends CollectionSubView {
+export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
public static RIGHT_BTN_DRAG = false;
private _selectOnLoaded: string = ""; // id of document that should be selected once it's loaded (used for click-to-type)
private _lastX: number = 0;
@@ -31,42 +42,38 @@ export class CollectionFreeFormView extends CollectionSubView {
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 nativeHeight() { return this.props.Document.GetNumber(KeyStore.NativeHeight, 0); }
+ @computed get nativeWidth() { return FieldValue(this.Document.nativeWidth, 0); }
+ @computed get nativeHeight() { return FieldValue(this.Document.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?
- private panX = () => this.props.Document.GetNumber(KeyStore.PanX, 0);
- private panY = () => this.props.Document.GetNumber(KeyStore.PanY, 0);
- private zoomScaling = () => this.props.Document.GetNumber(KeyStore.Scale, 1);
+ private get isAnnotationOverlay() { return this.props.fieldKey && this.props.fieldKey === "annotations"; }
+ private panX = () => FieldValue(this.Document.panX, 0);
+ private panY = () => FieldValue(this.Document.panY, 0);
+ private zoomScaling = () => FieldValue(this.Document.scale, 1);
private centeringShiftX = () => !this.nativeWidth ? this._pwidth / 2 : 0; // shift so pan position is at center of window for non-overlay collections
private centeringShiftY = () => !this.nativeHeight ? this._pheight / 2 : 0;// shift so pan position is at center of window for non-overlay collections
private getTransform = (): Transform => this.props.ScreenToLocalTransform().translate(-this.borderWidth, -this.borderWidth).translate(-this.centeringShiftX(), -this.centeringShiftY()).transform(this.getLocalTransform());
private getContainerTransform = (): Transform => this.props.ScreenToLocalTransform().translate(-this.borderWidth, -this.borderWidth);
private getLocalTransform = (): Transform => Transform.Identity().scale(1 / this.zoomScaling()).translate(this.panX(), this.panY());
- private addLiveTextBox = (newBox: Document) => {
- this._selectOnLoaded = newBox.Id;// track the new text box so we can give it a prop that tells it to focus itself when it's displayed
+ private addLiveTextBox = (newBox: Doc) => {
+ this._selectOnLoaded = newBox[Id];// track the new text box so we can give it a prop that tells it to focus itself when it's displayed
this.addDocument(newBox, false);
}
- @action
- private addDocument = (newBox: Document, allowDuplicates: boolean) => {
+ private addDocument = (newBox: Doc, allowDuplicates: boolean) => {
this.props.addDocument(newBox, false);
this.bringToFront(newBox);
return true;
}
- private selectDocuments = (docs: Document[]) => {
+ private selectDocuments = (docs: Doc[]) => {
SelectionManager.DeselectAll;
docs.map(doc => DocumentManager.Instance.getDocumentView(doc)).filter(dv => dv).map(dv =>
SelectionManager.SelectDoc(dv!, true));
}
public getActiveDocuments = () => {
- var curPage = this.props.Document.GetNumber(KeyStore.CurPage, -1);
- return this.props.Document.GetList(this.props.fieldKey, [] as Document[]).reduce((active, doc) => {
- var page = doc.GetNumber(KeyStore.Page, -1);
- if (page === curPage || page === -1) {
- active.push(doc);
- }
- return active;
- }, [] as Document[]);
+ const curPage = FieldValue(this.Document.curPage, -1);
+ return FieldValue(this.children, [] as Doc[]).filter(doc => {
+ var page = Cast(doc.page, "number", -1);
+ return page === curPage || page === -1;
+ });
}
@undoBatch
@@ -75,22 +82,22 @@ export class CollectionFreeFormView extends CollectionSubView {
if (super.drop(e, de) && de.data instanceof DragManager.DocumentDragData) {
if (de.data.droppedDocuments.length) {
let dragDoc = de.data.droppedDocuments[0];
- let zoom = dragDoc.GetNumber(KeyStore.ZoomBasis, 1);
+ let zoom = NumCast(dragDoc.zoomBasis, 1);
let [xp, yp] = this.getTransform().transformPoint(de.x, de.y);
let x = xp - de.data.xOffset / zoom;
let y = yp - de.data.yOffset / zoom;
- let dropX = de.data.droppedDocuments[0].GetNumber(KeyStore.X, 0);
- let dropY = de.data.droppedDocuments[0].GetNumber(KeyStore.Y, 0);
+ let dropX = NumCast(de.data.droppedDocuments[0].x);
+ let dropY = NumCast(de.data.droppedDocuments[0].y);
de.data.droppedDocuments.map(d => {
- d.SetNumber(KeyStore.X, x + (d.GetNumber(KeyStore.X, 0) - dropX));
- d.SetNumber(KeyStore.Y, y + (d.GetNumber(KeyStore.Y, 0) - dropY));
- if (!d.GetNumber(KeyStore.Width, 0)) {
- d.SetNumber(KeyStore.Width, 300);
+ d.x = x + NumCast(d.x) - dropX;
+ d.y = y + NumCast(d.y) - dropY;
+ if (!NumCast(d.width)) {
+ d.width = 300;
}
- if (!d.GetNumber(KeyStore.Height, 0)) {
- let nw = d.GetNumber(KeyStore.NativeWidth, 0);
- let nh = d.GetNumber(KeyStore.NativeHeight, 0);
- d.SetNumber(KeyStore.Height, nw && nh ? nh / nw * d.Width() : 300);
+ if (!NumCast(d.height)) {
+ let nw = NumCast(d.nativeWidth);
+ let nh = NumCast(d.nativeHeight);
+ d.height = nw && nh ? nh / nw * d.Width() : 300;
}
this.bringToFront(d);
});
@@ -109,7 +116,7 @@ export class CollectionFreeFormView extends CollectionSubView {
@action
onPointerDown = (e: React.PointerEvent): void => {
- let childSelected = this.props.Document.GetList(this.props.fieldKey, [] as Document[]).filter(doc => doc).reduce((childSelected, doc) => {
+ let childSelected = Cast(this.props.Document[this.props.fieldKey], listSpec(Doc), [] as Doc[]).filter(doc => doc).reduce((childSelected, doc) => {
var dv = DocumentManager.Instance.getDocumentView(doc);
return childSelected || (dv && SelectionManager.IsSelected(dv) ? true : false);
}, false);
@@ -133,20 +140,20 @@ export class CollectionFreeFormView extends CollectionSubView {
@action
onPointerMove = (e: PointerEvent): void => {
if (!e.cancelBubble) {
- let x = this.props.Document.GetNumber(KeyStore.PanX, 0);
- let y = this.props.Document.GetNumber(KeyStore.PanY, 0);
- let docs = this.props.Document.GetList(this.props.fieldKey, [] as Document[]);
+ let x = Cast(this.props.Document.panX, "number", 0);
+ let y = Cast(this.props.Document.panY, "number", 0);
+ let docs = this.children || [];
let [dx, dy] = this.getTransform().transformDirection(e.clientX - this._lastX, e.clientY - this._lastY);
if (!this.isAnnotationOverlay) {
- let minx = docs.length ? docs[0].GetNumber(KeyStore.X, 0) : 0;
- let maxx = docs.length ? docs[0].Width() + minx : minx;
- let miny = docs.length ? docs[0].GetNumber(KeyStore.Y, 0) : 0;
- let maxy = docs.length ? docs[0].Height() + miny : miny;
+ let minx = docs.length ? Cast(docs[0].x, "number", 0) : 0;
+ let maxx = docs.length ? Cast(docs[0].width, "number", 0) + minx : minx;
+ let miny = docs.length ? Cast(docs[0].y, "number", 0) : 0;
+ let maxy = docs.length ? Cast(docs[0].height, "number", 0) + miny : miny;
let ranges = docs.filter(doc => doc).reduce((range, doc) => {
- let x = doc.GetNumber(KeyStore.X, 0);
- let xe = x + doc.GetNumber(KeyStore.Width, 0);
- let y = doc.GetNumber(KeyStore.Y, 0);
- let ye = y + doc.GetNumber(KeyStore.Height, 0);
+ let x = Cast(doc.x, "number", 0);
+ let xe = x + Cast(doc.width, "number", 0);
+ let y = Cast(doc.y, "number", 0);
+ let ye = y + Cast(doc.height, "number", 0);
return [[range[0][0] > x ? x : range[0][0], range[0][1] < xe ? xe : range[0][1]],
[range[1][0] > y ? y : range[1][0], range[1][1] < ye ? ye : range[1][1]]];
}, [[minx, maxx], [miny, maxy]]);
@@ -170,10 +177,10 @@ export class CollectionFreeFormView extends CollectionSubView {
// if (!this.props.active()) {
// return;
// }
- let childSelected = this.props.Document.GetList(this.props.fieldKey, [] as Document[]).filter(doc => doc).reduce((childSelected, doc) => {
+ let childSelected = (this.children || []).filter(doc => doc).some(doc => {
var dv = DocumentManager.Instance.getDocumentView(doc);
- return childSelected || (dv && SelectionManager.IsSelected(dv) ? true : false);
- }, false);
+ return dv && SelectionManager.IsSelected(dv) ? true : false;
+ });
if (!this.props.isSelected() && !childSelected && !this.props.isTopMost) {
return;
}
@@ -182,8 +189,8 @@ export class CollectionFreeFormView extends CollectionSubView {
if (e.ctrlKey) {
let deltaScale = (1 - (e.deltaY / coefficient));
- this.props.Document.SetNumber(KeyStore.NativeWidth, this.nativeWidth * deltaScale);
- this.props.Document.SetNumber(KeyStore.NativeHeight, this.nativeHeight * deltaScale);
+ this.props.Document.nativeWidth = this.nativeWidth * deltaScale;
+ this.props.Document.nativeHeight = this.nativeHeight * deltaScale;
e.stopPropagation();
e.preventDefault();
} else {
@@ -198,7 +205,7 @@ export class CollectionFreeFormView extends CollectionSubView {
let localTransform = this.getLocalTransform().inverse().scaleAbout(deltaScale, x, y);
let safeScale = Math.abs(localTransform.Scale);
- this.props.Document.SetNumber(KeyStore.Scale, Math.abs(safeScale));
+ this.props.Document.scale = Math.abs(safeScale);
this.setPan(-localTransform.TranslateX / safeScale, -localTransform.TranslateY / safeScale);
e.stopPropagation();
}
@@ -209,8 +216,8 @@ export class CollectionFreeFormView extends CollectionSubView {
var scale = this.getLocalTransform().inverse().Scale;
const newPanX = Math.min((1 - 1 / scale) * this.nativeWidth, Math.max(0, panX));
const newPanY = Math.min((1 - 1 / scale) * this.nativeHeight, Math.max(0, panY));
- this.props.Document.SetNumber(KeyStore.PanX, this.isAnnotationOverlay ? newPanX : panX);
- this.props.Document.SetNumber(KeyStore.PanY, this.isAnnotationOverlay ? newPanY : panY);
+ this.props.Document.panX = this.isAnnotationOverlay ? newPanX : panX;
+ this.props.Document.panY = this.isAnnotationOverlay ? newPanY : panY;
}
@action
@@ -222,25 +229,25 @@ export class CollectionFreeFormView extends CollectionSubView {
onDragOver = (): void => {
}
- @action
- bringToFront(doc: Document) {
- let docs = this.props.Document.GetList(this.props.fieldKey, [] as Document[]).slice();
- docs.sort((doc1, doc2) => {
+ bringToFront(doc: Doc) {
+ const docs = (this.children || []);
+ docs.slice().sort((doc1, doc2) => {
if (doc1 === doc) return 1;
if (doc2 === doc) return -1;
- return doc1.GetNumber(KeyStore.ZIndex, 0) - doc2.GetNumber(KeyStore.ZIndex, 0);
- }).map((doc, index) => doc.SetNumber(KeyStore.ZIndex, index + 1));
- doc.SetNumber(KeyStore.ZIndex, docs.length + 1);
+ return NumCast(doc1.zIndex) - NumCast(doc2.zIndex);
+ }).forEach((doc, index) => doc.zIndex = index + 1);
+ doc.zIndex = docs.length + 1;
+ return doc;
}
- focusDocument = (doc: Document) => {
+ focusDocument = (doc: Doc) => {
this.setPan(
- doc.GetNumber(KeyStore.X, 0) + doc.Width() / 2,
- doc.GetNumber(KeyStore.Y, 0) + doc.Height() / 2);
+ Cast(doc.x, "number", 0) + Cast(doc.width, "number", 0) / 2,
+ Cast(doc.y, "number", 0) + Cast(doc.height, "number", 0) / 2);
this.props.focus(this.props.Document);
}
- getDocumentViewProps(document: Document): DocumentViewProps {
+ getDocumentViewProps(document: Doc): DocumentViewProps {
return {
Document: document,
toggleMinimized: emptyFunction,
@@ -250,8 +257,8 @@ export class CollectionFreeFormView extends CollectionSubView {
ScreenToLocalTransform: this.getTransform,
isTopMost: false,
selectOnLoad: document.Id === this._selectOnLoaded,
- PanelWidth: document.Width,
- PanelHeight: document.Height,
+ PanelWidth: () => Cast(document.width, "number", 0),//TODO Types These are inline functions
+ PanelHeight: () => Cast(document.height, "number", 0),
ContentScaling: returnOne,
ContainingCollectionView: this.props.CollectionView,
focus: this.focusDocument,
@@ -262,13 +269,14 @@ export class CollectionFreeFormView extends CollectionSubView {
@computed
get views() {
- var curPage = this.props.Document.GetNumber(KeyStore.CurPage, -1);
- let docviews = this.props.Document.GetList(this.props.fieldKey, [] as Document[]).filter(doc => doc).reduce((prev, doc) => {
- var page = doc.GetNumber(KeyStore.Page, -1);
+ let curPage = FieldValue(this.Document.curPage, -1);
+ let docviews = (this.children || []).filter(doc => doc).reduce((prev, doc) => {
+ var page = Cast(doc.page, "number", -1);
if (page === curPage || page === -1) {
- let minim = doc.GetT(KeyStore.IsMinimized, BooleanField);
- if (minim === undefined || (minim && !minim.Data))
- prev.push(<CollectionFreeFormDocumentView key={doc.Id} {...this.getDocumentViewProps(doc)} />);
+ let minim = Cast(doc.isMinimized, "boolean");
+ if (minim === undefined || !minim) {
+ prev.push(<CollectionFreeFormDocumentView key={doc[Id]} {...this.getDocumentViewProps(doc)} />);
+ }
}
return prev;
}, [] as JSX.Element[]);
@@ -317,9 +325,9 @@ export class CollectionFreeFormView extends CollectionSubView {
@observer
class CollectionFreeFormOverlayView extends React.Component<DocumentViewProps> {
@computed get overlayView() {
- let overlayLayout = this.props.Document.GetText(KeyStore.OverlayLayout, "");
+ let overlayLayout = Cast(this.props.Document.overlayLayout, "string", "");
return !overlayLayout ? (null) :
- (<DocumentContentsView {...this.props} layoutKey={KeyStore.OverlayLayout}
+ (<DocumentContentsView {...this.props} layoutKey={"overlayLayout"}
isTopMost={this.props.isTopMost} isSelected={returnFalse} select={emptyFunction} />);
}
render() {
@@ -330,9 +338,9 @@ class CollectionFreeFormOverlayView extends React.Component<DocumentViewProps> {
@observer
class CollectionFreeFormBackgroundView extends React.Component<DocumentViewProps> {
@computed get backgroundView() {
- let backgroundLayout = this.props.Document.GetText(KeyStore.BackgroundLayout, "");
+ let backgroundLayout = Cast(this.props.Document.backgroundLayout, "string", "");
return !backgroundLayout ? (null) :
- (<DocumentContentsView {...this.props} layoutKey={KeyStore.BackgroundLayout}
+ (<DocumentContentsView {...this.props} layoutKey={"backgroundLayout"}
isTopMost={this.props.isTopMost} isSelected={returnFalse} select={emptyFunction} />);
}
render() {
diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx
index 1493ff25b..be12dced3 100644
--- a/src/client/views/nodes/AudioBox.tsx
+++ b/src/client/views/nodes/AudioBox.tsx
@@ -1,36 +1,19 @@
import React = require("react");
import { FieldViewProps, FieldView } from './FieldView';
-import { FieldWaiting } from '../../../fields/Field';
import { observer } from "mobx-react";
-import { ContextMenu } from "../../views/ContextMenu";
-import { observable, action } from 'mobx';
-import { KeyStore } from '../../../fields/KeyStore';
-import { AudioField } from "../../../fields/AudioField";
import "./AudioBox.scss";
-import { NumberField } from "../../../fields/NumberField";
+import { Cast } from "../../../new_fields/Types";
+import { AudioField } from "../../../new_fields/URLField";
+const defaultField: AudioField = new AudioField(new URL("http://techslides.com/demos/samples/sample.mp3"));
@observer
export class AudioBox extends React.Component<FieldViewProps> {
public static LayoutString() { return FieldView.LayoutString(AudioBox); }
- constructor(props: FieldViewProps) {
- super(props);
- }
-
-
-
- componentDidMount() {
- }
-
- componentWillUnmount() {
- }
-
-
render() {
- let field = this.props.Document.Get(this.props.fieldKey);
- let path = field === FieldWaiting ? "http://techslides.com/demos/samples/sample.mp3" :
- field instanceof AudioField ? field.Data.href : "http://techslides.com/demos/samples/sample.mp3";
+ let field = Cast(this.props.Document[this.props.fieldKey], AudioField, defaultField);
+ let path = field.url.href;
return (
<div>
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 4537ce3eb..376af0b36 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -1,23 +1,30 @@
import { computed, trace, action } from "mobx";
import { observer } from "mobx-react";
-import { KeyStore } from "../../../fields/KeyStore";
-import { NumberField } from "../../../fields/NumberField";
-import { Document } from "../../../fields/Document";
import { Transform } from "../../util/Transform";
-import { DocumentView, DocumentViewProps } from "./DocumentView";
+import { DocumentView, DocumentViewProps, positionSchema } from "./DocumentView";
import "./DocumentView.scss";
import React = require("react");
+import { DocComponent } from "../DocComponent";
+import { createSchema, makeInterface, listSpec } from "../../../new_fields/Schema";
+import { FieldValue, Cast, NumCast } from "../../../new_fields/Types";
import { OmitKeys, Utils } from "../../../Utils";
import { SelectionManager } from "../../util/SelectionManager";
-import { ListField } from "../../../fields/ListField";
-import { BooleanField } from "../../../fields/BooleanField";
import { matchedData } from "express-validator/filter";
+import { Doc } from "../../../new_fields/Doc";
export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps {
}
+const schema = createSchema({
+ zoom: "number",
+ zIndex: "number"
+});
+
+type FreeformDocument = makeInterface<[typeof schema, typeof positionSchema]>;
+const FreeformDocument = makeInterface(schema, positionSchema);
+
@observer
-export class CollectionFreeFormDocumentView extends React.Component<CollectionFreeFormDocumentViewProps> {
+export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeFormDocumentViewProps, FreeformDocument>(FreeformDocument) {
private _mainCont = React.createRef<HTMLDivElement>();
private _downX: number = 0;
private _downY: number = 0;
@@ -25,31 +32,33 @@ export class CollectionFreeFormDocumentView extends React.Component<CollectionFr
@computed get transform() {
return `scale(${this.props.ContentScaling()}, ${this.props.ContentScaling()}) translate(${this.X}px, ${this.Y}px) scale(${this.zoom}, ${this.zoom}) `;
}
- @computed get X() { return this.props.Document.GetNumber(KeyStore.X, 0); }
- @computed get Y() { return this.props.Document.GetNumber(KeyStore.Y, 0); }
- @computed get zoom() { return 1 / this.props.Document.GetNumber(KeyStore.ZoomBasis, 1); }
- @computed get nativeWidth() { return this.props.Document.GetNumber(KeyStore.NativeWidth, 0); }
- @computed get nativeHeight() { return this.props.Document.GetNumber(KeyStore.NativeHeight, 0); }
- @computed get width() { return this.props.Document.Width(); }
- @computed get height() { return this.props.Document.Height(); }
- @computed get zIndex() { return this.props.Document.GetNumber(KeyStore.ZIndex, 0); }
+
+ @computed get X() { return FieldValue(this.Document.x, 0); }
+ @computed get Y() { return FieldValue(this.Document.y, 0); }
+ @computed get zoom(): number { return 1 / FieldValue(this.Document.zoom, 1); }
+ @computed get nativeWidth(): number { return FieldValue(this.Document.nativeWidth, 0); }
+ @computed get nativeHeight(): number { return FieldValue(this.Document.nativeHeight, 0); }
+ @computed get width(): number { return FieldValue(this.Document.width, 0); }
+ @computed get height(): number { return FieldValue(this.Document.height, 0); }
+ @computed get zIndex(): number { return FieldValue(this.Document.zIndex, 0); }
+
set width(w: number) {
- this.props.Document.SetData(KeyStore.Width, w, NumberField);
+ this.Document.width = w;
if (this.nativeWidth && this.nativeHeight) {
- this.props.Document.SetNumber(KeyStore.Height, this.nativeHeight / this.nativeWidth * w);
+ this.Document.height = this.nativeHeight / this.nativeWidth * w;
}
}
set height(h: number) {
- this.props.Document.SetData(KeyStore.Height, h, NumberField);
+ this.Document.height = h;
if (this.nativeWidth && this.nativeHeight) {
- this.props.Document.SetNumber(KeyStore.Width, this.nativeWidth / this.nativeHeight * h);
+ this.Document.width = this.nativeWidth / this.nativeHeight * h;
}
}
set zIndex(h: number) {
- this.props.Document.SetData(KeyStore.ZIndex, h, NumberField);
+ this.Document.zIndex = h;
}
- contentScaling = () => (this.nativeWidth > 0 ? this.width / this.nativeWidth : 1);
+ contentScaling = () => this.nativeWidth > 0 ? this.width / this.nativeWidth : 1;
panelWidth = () => this.props.PanelWidth();
panelHeight = () => this.props.PanelHeight();
toggleMinimized = () => this.toggleIcon();
@@ -59,7 +68,7 @@ export class CollectionFreeFormDocumentView extends React.Component<CollectionFr
@computed
get docView() {
- return <DocumentView {...OmitKeys(this.props, ['zoomFade'])}
+ return <DocumentView {...OmitKeys(this.props, ['zoomFade']).omit}
toggleMinimized={this.toggleMinimized}
ContentScaling={this.contentScaling}
ScreenToLocalTransform={this.getTransform}
@@ -68,30 +77,30 @@ export class CollectionFreeFormDocumentView extends React.Component<CollectionFr
/>;
}
- animateBetweenIcon(first: boolean, icon: number[], targ: number[], width: number, height: number, stime: number, target: Document, maximizing: boolean) {
+ animateBetweenIcon(first: boolean, icon: number[], targ: number[], width: number, height: number, stime: number, target: Doc, maximizing: boolean) {
setTimeout(() => {
let now = Date.now();
let progress = Math.min(1, (now - stime) / 200);
let pval = maximizing ?
[icon[0] + (targ[0] - icon[0]) * progress, icon[1] + (targ[1] - icon[1]) * progress] :
[targ[0] + (icon[0] - targ[0]) * progress, targ[1] + (icon[1] - targ[1]) * progress];
- target.SetNumber(KeyStore.Width, maximizing ? 25 + (width - 25) * progress : width + (25 - width) * progress);
- target.SetNumber(KeyStore.Height, maximizing ? 25 + (height - 25) * progress : height + (25 - height) * progress);
- target.SetNumber(KeyStore.X, pval[0]);
- target.SetNumber(KeyStore.Y, pval[1]);
+ target.width = maximizing ? 25 + (width - 25) * progress : width + (25 - width) * progress;
+ target.height = maximizing ? 25 + (height - 25) * progress : height + (25 - height) * progress;
+ target.x = pval[0];
+ target.y = pval[1];
if (first) {
- target.SetBoolean(KeyStore.IsMinimized, false);
+ target.isMinimized = false;
}
if (now < stime + 200) {
this.animateBetweenIcon(false, icon, targ, width, height, stime, target, maximizing);
}
else {
if (!maximizing) {
- target.SetBoolean(KeyStore.IsMinimized, true);
- target.SetNumber(KeyStore.X, targ[0]);
- target.SetNumber(KeyStore.Y, targ[1]);
- target.SetNumber(KeyStore.Width, width);
- target.SetNumber(KeyStore.Height, height);
+ target.isMinimized = true;
+ target.x = targ[0];
+ target.y = targ[1];
+ target.width = width;
+ target.height = height;
}
(target as any).isIconAnimating = false;
}
@@ -102,30 +111,28 @@ export class CollectionFreeFormDocumentView extends React.Component<CollectionFr
public toggleIcon = async (): Promise<void> => {
SelectionManager.DeselectAll();
let isMinimized: boolean | undefined;
- let minimizedDocSet = await this.props.Document.GetTAsync(KeyStore.LinkTags, ListField);
+ let minimizedDocSet = Cast(this.props.Document.linkTags, listSpec(Doc));
if (!minimizedDocSet) return;
- minimizedDocSet.Data.map(async minimizedDoc => {
+ minimizedDocSet.map(async minimizedDoc => {
if (minimizedDoc instanceof Document) {
this.props.addDocument && this.props.addDocument(minimizedDoc, false);
- let maximizedDoc = await minimizedDoc.GetTAsync(KeyStore.MaximizedDoc, Document);
- if (maximizedDoc instanceof Document && !(maximizedDoc as any).isIconAnimating) {
+ let maximizedDoc = await Cast(minimizedDoc.maximizedDoc, Doc);
+ if (maximizedDoc && !(maximizedDoc as any).isIconAnimating) {
(maximizedDoc as any).isIconAnimating = true;
if (isMinimized === undefined) {
- let maximizedDocMinimizedState = await maximizedDoc.GetTAsync(KeyStore.IsMinimized, BooleanField);
- isMinimized = (maximizedDocMinimizedState && maximizedDocMinimizedState.Data) ? true : false;
+ let maximizedDocMinimizedState = Cast(maximizedDoc.isMinimized, "boolean");
+ isMinimized = (maximizedDocMinimizedState) ? true : false;
}
- let minx = await minimizedDoc.GetTAsync(KeyStore.X, NumberField);
- let miny = await minimizedDoc.GetTAsync(KeyStore.Y, NumberField);
- let maxx = await maximizedDoc.GetTAsync(KeyStore.X, NumberField);
- let maxy = await maximizedDoc.GetTAsync(KeyStore.Y, NumberField);
- let maxw = await maximizedDoc.GetTAsync(KeyStore.Width, NumberField);
- let maxh = await maximizedDoc.GetTAsync(KeyStore.Height, NumberField);
+ let minx = NumCast(minimizedDoc.x, undefined);
+ let miny = NumCast(minimizedDoc.y, undefined);
+ let maxx = NumCast(maximizedDoc.x, undefined);
+ let maxy = NumCast(maximizedDoc.y, undefined);
+ let maxw = NumCast(maximizedDoc.width, undefined);
+ let maxh = NumCast(maximizedDoc.height, undefined);
if (minx !== undefined && miny !== undefined && maxx !== undefined && maxy !== undefined &&
- maxw !== undefined && maxh !== undefined)
- this.animateBetweenIcon(
- true,
- [minx.Data, miny.Data], [maxx.Data, maxy.Data], maxw.Data, maxh.Data,
- Date.now(), maximizedDoc, isMinimized);
+ maxw !== undefined && maxh !== undefined) {
+ this.animateBetweenIcon(true, [minx, miny], [maxx, maxy], maxw, maxh, Date.now(), maximizedDoc, isMinimized);
+ }
}
}
@@ -135,30 +142,28 @@ export class CollectionFreeFormDocumentView extends React.Component<CollectionFr
this._downX = e.clientX;
this._downY = e.clientY;
}
- onClick = (e: React.MouseEvent): void => {
+ onClick = async (e: React.MouseEvent) => {
e.stopPropagation();
if (Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD &&
Math.abs(e.clientY - this._downY) < Utils.DRAG_THRESHOLD) {
- this.props.Document.GetTAsync(KeyStore.MaximizedDoc, Document).then(maxdoc => {
- if (maxdoc instanceof Document) { // bcz: need a better way to associate behaviors with click events on widget-documents
- this.props.addDocument && this.props.addDocument(maxdoc, false);
- this.toggleIcon();
- }
- });
+ const maxDoc = await Cast(this.props.Document.maximizedDoc, Doc);
+ if (maxDoc) { // bcz: need a better way to associate behaviors with click events on widget-documents
+ this.props.addDocument && this.props.addDocument(maxDoc, false);
+ this.toggleIcon();
+ }
}
}
borderRounding = () => {
- let br = this.props.Document.GetNumber(KeyStore.BorderRounding, 0);
+ let br = NumCast(this.props.Document.borderRounding);
return br >= 0 ? br :
- this.props.Document.GetNumber(KeyStore.NativeWidth, 0) === 0 ?
+ NumCast(this.props.Document.nativeWidth) === 0 ?
Math.min(this.props.PanelWidth(), this.props.PanelHeight())
- :
- Math.min(this.props.Document.GetNumber(KeyStore.NativeWidth, 0), this.props.Document.GetNumber(KeyStore.NativeHeight, 0));
+ : Math.min(this.Document.nativeWidth || 0, this.Document.nativeHeight || 0);
}
render() {
- let maximizedDoc = this.props.Document.GetT(KeyStore.MaximizedDoc, Document);
+ let maximizedDoc = FieldValue(Cast(this.props.Document.maximizedDoc, Doc));
let zoomFade = 1;
//var zoom = doc.GetNumber(KeyStore.ZoomBasis, 1);
let transform = this.getTransform().scale(this.contentScaling()).inverse();
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index 07599c345..c90ca6d17 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -1,9 +1,5 @@
import { computed } from "mobx";
import { observer } from "mobx-react";
-import { FieldWaiting, Field } from "../../../fields/Field";
-import { Key } from "../../../fields/Key";
-import { KeyStore } from "../../../fields/KeyStore";
-import { ListField } from "../../../fields/ListField";
import { CollectionDockingView } from "../collections/CollectionDockingView";
import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView";
import { CollectionPDFView } from "../collections/CollectionPDFView";
@@ -22,46 +18,30 @@ import { VideoBox } from "./VideoBox";
import { WebBox } from "./WebBox";
import { HistogramBox } from "../../northstar/dash-nodes/HistogramBox";
import React = require("react");
-import { Document } from "../../../fields/Document";
import { FieldViewProps } from "./FieldView";
import { Without, OmitKeys } from "../../../Utils";
+import { Cast } from "../../../new_fields/Types";
const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this?
type BindingProps = Without<FieldViewProps, 'fieldKey'>;
export interface JsxBindings {
props: BindingProps;
- [keyName: string]: BindingProps | Field;
}
@observer
export class DocumentContentsView extends React.Component<DocumentViewProps & {
isSelected: () => boolean,
select: (ctrl: boolean) => void,
- layoutKey: Key
+ layoutKey: string
}> {
- @computed get layout(): string { return this.props.Document.GetText(this.props.layoutKey, "<p>Error loading layout data</p>"); }
- @computed get layoutKeys(): Key[] { return this.props.Document.GetData(KeyStore.LayoutKeys, ListField, new Array<Key>()); }
- @computed get layoutFields(): Key[] { return this.props.Document.GetData(KeyStore.LayoutFields, ListField, new Array<Key>()); }
-
+ @computed get layout(): string { return Cast(this.props.Document[this.props.layoutKey], "string", "<p>Error loading layout data</p>"); }
CreateBindings(): JsxBindings {
- let bindings: JsxBindings = { props: OmitKeys(this.props, ['parentActive'], (obj: any) => obj.active = this.props.parentActive) };
-
- for (const key of this.layoutKeys) {
- bindings[key.Name + "Key"] = key; // this maps string values of the form <keyname>Key to an actual key Kestore.keyname e.g, "DataKey" => KeyStore.Data
- }
- for (const key of this.layoutFields) {
- let field = this.props.Document.Get(key);
- bindings[key.Name] = field && field !== FieldWaiting ? field.GetValue() : field;
- }
+ let bindings: JsxBindings = { props: OmitKeys(this.props, ['parentActive'], (obj: any) => obj.active = this.props.parentActive).omit };
return bindings;
}
render() {
- let lkeys = this.props.Document.GetT(KeyStore.LayoutKeys, ListField);
- if (!lkeys || lkeys === FieldWaiting) {
- return <p>Error loading layout keys</p>;
- }
return <JsxParser
components={{ FormattedTextBox, ImageBox, IconBox, CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, CollectionPDFView, CollectionVideoView, WebBox, KeyValueBox, PDFBox, VideoBox, AudioBox, HistogramBox }}
bindings={this.CreateBindings()}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index aaedd5b1c..aabc1633e 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -1,13 +1,7 @@
import { action, computed, runInAction } from "mobx";
import { observer } from "mobx-react";
-import { Document } from "../../../fields/Document";
-import { Field, Opt } from "../../../fields/Field";
-import { Key } from "../../../fields/Key";
-import { KeyStore } from "../../../fields/KeyStore";
-import { ListField } from "../../../fields/ListField";
-import { ServerUtils } from "../../../server/ServerUtil";
import { emptyFunction, Utils } from "../../../Utils";
-import { Documents } from "../../documents/Documents";
+import { Docs } from "../../documents/Documents";
import { DocumentManager } from "../../util/DocumentManager";
import { DragManager } from "../../util/DragManager";
import { SelectionManager } from "../../util/SelectionManager";
@@ -21,13 +15,30 @@ import { ContextMenu } from "../ContextMenu";
import { DocumentContentsView } from "./DocumentContentsView";
import "./DocumentView.scss";
import React = require("react");
+import { Field, Opt, Doc, Id } from "../../../new_fields/Doc";
+import { DocComponent } from "../DocComponent";
+import { createSchema, makeInterface, listSpec } from "../../../new_fields/Schema";
+import { FieldValue, Cast, PromiseValue } from "../../../new_fields/Types";
+import { List } from "../../../new_fields/List";
import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView";
import { CurrentUserUtils } from "../../../server/authentication/models/current_user_utils";
import { MarqueeView } from "../collections/collectionFreeForm/MarqueeView";
+import { DocServer } from "../../DocServer";
+
+const linkSchema = createSchema({
+ title: "string",
+ linkDescription: "string",
+ linkTags: "string",
+ linkedTo: Doc,
+ linkedFrom: Doc
+});
+
+type LinkDoc = makeInterface<[typeof linkSchema]>;
+const LinkDoc = makeInterface(linkSchema);
export interface DocumentViewProps {
ContainingCollectionView: Opt<CollectionView | CollectionPDFView | CollectionVideoView>;
- Document: Document;
+ Document: Doc;
addDocument?: (doc: Document, allowDuplicates?: boolean) => boolean;
removeDocument?: (doc: Document) => boolean;
moveDocument?: (doc: Document, targetCollection: Document, addDocument: (document: Document) => boolean) => boolean;
@@ -42,48 +53,31 @@ export interface DocumentViewProps {
whenActiveChanged: (isActive: boolean) => void;
toggleMinimized: () => void;
}
-export interface JsxArgs extends DocumentViewProps {
- Keys: { [name: string]: Key };
- Fields: { [name: string]: Field };
-}
-/*
-This function is pretty much a hack that lets us fill out the fields in JsxArgs with something that
-jsx-to-string can recover the jsx from
-Example usage of this function:
- public static LayoutString() {
- let args = FakeJsxArgs(["Data"]);
- return jsxToString(
- <CollectionFreeFormView
- doc={args.Document}
- fieldKey={args.Keys.Data}
- DocumentViewForField={args.DocumentView} />,
- { useFunctionCode: true, functionNameOnly: true }
- )
- }
-*/
-export function FakeJsxArgs(keys: string[], fields: string[] = []): JsxArgs {
- let Keys: { [name: string]: any } = {};
- let Fields: { [name: string]: any } = {};
- for (const key of keys) {
- Object.defineProperty(emptyFunction, "name", { value: key + "Key" });
- Keys[key] = emptyFunction;
- }
- for (const field of fields) {
- Object.defineProperty(emptyFunction, "name", { value: field });
- Fields[field] = emptyFunction;
- }
- let args: JsxArgs = {
- Document: function Document() { },
- DocumentView: function DocumentView() { },
- Keys,
- Fields
- } as any;
- return args;
-}
+const schema = createSchema({
+ layout: "string",
+ nativeWidth: "number",
+ nativeHeight: "number",
+ backgroundColor: "string"
+});
+
+export const positionSchema = createSchema({
+ nativeWidth: "number",
+ nativeHeight: "number",
+ width: "number",
+ height: "number",
+ x: "number",
+ y: "number",
+});
+
+export type PositionDocument = makeInterface<[typeof positionSchema]>;
+export const PositionDocument = makeInterface(positionSchema);
+
+type Document = makeInterface<[typeof schema]>;
+const Document = makeInterface(schema);
@observer
-export class DocumentView extends React.Component<DocumentViewProps> {
+export class DocumentView extends DocComponent<DocumentViewProps, Document>(Document) {
private _downX: number = 0;
private _downY: number = 0;
private _mainCont = React.createRef<HTMLDivElement>();
@@ -92,9 +86,6 @@ export class DocumentView extends React.Component<DocumentViewProps> {
public get ContentDiv() { return this._mainCont.current; }
@computed get active(): boolean { return SelectionManager.IsSelected(this) || this.props.parentActive(); }
@computed get topMost(): boolean { return this.props.isTopMost; }
- @computed get layout(): string { return this.props.Document.GetText(KeyStore.Layout, "<p>Error loading layout data</p>"); }
- @computed get layoutKeys(): Key[] { return this.props.Document.GetData(KeyStore.LayoutKeys, ListField, new Array<Key>()); }
- @computed get layoutFields(): Key[] { return this.props.Document.GetData(KeyStore.LayoutFields, ListField, new Array<Key>()); }
@action
componentDidMount() {
@@ -147,7 +138,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
}
onClick = (e: React.MouseEvent): void => {
- if (CurrentUserUtils.MainDocId != this.props.Document.Id &&
+ if (CurrentUserUtils.MainDocId !== this.props.Document.Id &&
(Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD &&
Math.abs(e.clientY - this._downY) < Utils.DRAG_THRESHOLD)) {
SelectionManager.SelectDoc(this, e.ctrlKey);
@@ -196,11 +187,14 @@ export class DocumentView extends React.Component<DocumentViewProps> {
this.props.removeDocument && this.props.removeDocument(this.props.Document);
}
fieldsClicked = (e: React.MouseEvent): void => {
- let kvp = Documents.KVPDocument(this.props.Document, { width: 300, height: 300 });
+ let kvp = Docs.KVPDocument(this.props.Document, { width: 300, height: 300 });
CollectionDockingView.Instance.AddRightSplit(kvp);
}
fullScreenClicked = (e: React.MouseEvent): void => {
- CollectionDockingView.Instance.OpenFullScreen((this.props.Document.GetPrototype() as Document).MakeDelegate());
+ const doc = Doc.MakeDelegate(FieldValue(this.Document.proto));
+ if (doc) {
+ CollectionDockingView.Instance.OpenFullScreen(doc);
+ }
ContextMenu.Instance.clearItems();
ContextMenu.Instance.addItem({ description: "Close Full Screen", event: this.closeFullScreenClicked });
ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15);
@@ -214,15 +208,14 @@ export class DocumentView extends React.Component<DocumentViewProps> {
@undoBatch
@action
- drop = (e: Event, de: DragManager.DropEvent) => {
+ drop = async (e: Event, de: DragManager.DropEvent) => {
if (de.data instanceof DragManager.LinkDragData) {
let sourceDoc = de.data.linkSourceDocument;
let destDoc = this.props.Document;
- destDoc.GetTAsync(KeyStore.Prototype, Document).then(protoDest =>
- sourceDoc.GetTAsync(KeyStore.Prototype, Document).then(protoSrc =>
- (protoSrc ? protoSrc : sourceDoc).CreateLink(protoDest ? protoDest : destDoc))
- );
+ const protoDest = destDoc.proto;
+ const protoSrc = sourceDoc.proto;
+ Doc.MakeLink(protoSrc ? protoSrc : sourceDoc, protoDest ? protoDest : destDoc);
e.stopPropagation();
}
}
@@ -231,9 +224,9 @@ export class DocumentView extends React.Component<DocumentViewProps> {
onDrop = (e: React.DragEvent) => {
let text = e.dataTransfer.getData("text/plain");
if (!e.isDefaultPrevented() && text && text.startsWith("<div")) {
- let oldLayout = this.props.Document.GetText(KeyStore.Layout, "");
+ let oldLayout = FieldValue(this.Document.layout) || "";
let layout = text.replace("{layout}", oldLayout);
- this.props.Document.SetText(KeyStore.Layout, layout);
+ this.Document.layout = layout;
e.stopPropagation();
e.preventDefault();
}
@@ -253,20 +246,22 @@ export class DocumentView extends React.Component<DocumentViewProps> {
ContextMenu.Instance.addItem({ description: "Fields", event: this.fieldsClicked });
ContextMenu.Instance.addItem({ description: "Center", event: () => this.props.focus(this.props.Document) });
ContextMenu.Instance.addItem({ description: "Open Right", event: () => CollectionDockingView.Instance.AddRightSplit(this.props.Document) });
- ContextMenu.Instance.addItem({ description: "Copy URL", event: () => Utils.CopyText(ServerUtils.prepend("/doc/" + this.props.Document.Id)) });
- ContextMenu.Instance.addItem({ description: "Copy ID", event: () => Utils.CopyText(this.props.Document.Id) });
+ ContextMenu.Instance.addItem({ description: "Copy URL", event: () => Utils.CopyText(DocServer.prepend("/doc/" + this.props.Document.Id)) });
+ ContextMenu.Instance.addItem({ description: "Copy ID", event: () => Utils.CopyText(this.props.Document[Id]) });
//ContextMenu.Instance.addItem({ description: "Docking", event: () => this.props.Document.SetNumber(KeyStore.ViewType, CollectionViewType.Docking) })
ContextMenu.Instance.addItem({ description: "Delete", event: this.deleteClicked });
ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15);
- if (!SelectionManager.IsSelected(this))
+ if (!SelectionManager.IsSelected(this)) {
SelectionManager.SelectDoc(this, false);
+ }
}
isSelected = () => SelectionManager.IsSelected(this);
select = (ctrlPressed: boolean) => SelectionManager.SelectDoc(this, ctrlPressed);
- @computed get nativeWidth() { return this.props.Document.GetNumber(KeyStore.NativeWidth, 0); }
- @computed get nativeHeight() { return this.props.Document.GetNumber(KeyStore.NativeHeight, 0); }
- @computed get contents() { return (<DocumentContentsView {...this.props} isSelected={this.isSelected} select={this.select} layoutKey={KeyStore.Layout} />); }
+
+ @computed get nativeWidth() { return FieldValue(this.Document.nativeWidth, 0); }
+ @computed get nativeHeight() { return FieldValue(this.Document.nativeHeight, 0); }
+ @computed get contents() { return (<DocumentContentsView {...this.props} isSelected={this.isSelected} select={this.select} layoutKey={"layout"} />); }
render() {
var scaling = this.props.ContentScaling();
@@ -278,7 +273,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
ref={this._mainCont}
style={{
borderRadius: "inherit",
- background: this.props.Document.GetText(KeyStore.BackgroundColor, ""),
+ background: FieldValue(this.Document.backgroundColor) || "",
width: nativeWidth, height: nativeHeight,
transform: `scale(${scaling}, ${scaling})`
}}
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 8f360754e..c00c47fc4 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -1,30 +1,21 @@
import React = require("react");
import { observer } from "mobx-react";
import { computed } from "mobx";
-import { Field, FieldWaiting, FieldValue, Opt } from "../../../fields/Field";
-import { Document } from "../../../fields/Document";
-import { TextField } from "../../../fields/TextField";
-import { NumberField } from "../../../fields/NumberField";
-import { RichTextField } from "../../../fields/RichTextField";
-import { ImageField } from "../../../fields/ImageField";
-import { VideoField } from "../../../fields/VideoField";
-import { Key } from "../../../fields/Key";
import { FormattedTextBox } from "./FormattedTextBox";
import { ImageBox } from "./ImageBox";
-import { WebBox } from "./WebBox";
import { VideoBox } from "./VideoBox";
import { AudioBox } from "./AudioBox";
-import { AudioField } from "../../../fields/AudioField";
-import { ListField } from "../../../fields/ListField";
import { DocumentContentsView } from "./DocumentContentsView";
import { Transform } from "../../util/Transform";
-import { KeyStore } from "../../../fields/KeyStore";
-import { returnFalse, emptyDocFunction, emptyFunction, returnOne, returnZero } from "../../../Utils";
+import { returnFalse, emptyFunction } from "../../../Utils";
import { CollectionView } from "../collections/CollectionView";
import { CollectionPDFView } from "../collections/CollectionPDFView";
import { CollectionVideoView } from "../collections/CollectionVideoView";
-import { IconField } from "../../../fields/IconFIeld";
import { IconBox } from "./IconBox";
+import { Opt, Doc, FieldResult } from "../../../new_fields/Doc";
+import { List } from "../../../new_fields/List";
+import { ImageField, VideoField, AudioField } from "../../../new_fields/URLField";
+import { IconField } from "../../../new_fields/IconField";
//
@@ -33,20 +24,20 @@ import { IconBox } from "./IconBox";
// See the LayoutString method on each field view : ImageBox, FormattedTextBox, etc.
//
export interface FieldViewProps {
- fieldKey: Key;
+ fieldKey: string;
ContainingCollectionView: Opt<CollectionView | CollectionPDFView | CollectionVideoView>;
- Document: Document;
+ Document: Doc;
isSelected: () => boolean;
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;
PanelWidth: () => number;
PanelHeight: () => number;
}
@@ -58,17 +49,17 @@ export class FieldView extends React.Component<FieldViewProps> {
}
@computed
- get field(): FieldValue<Field> {
- const { Document: doc, fieldKey } = this.props;
- return doc.Get(fieldKey);
+ get field(): FieldResult {
+ const { Document, fieldKey } = this.props;
+ return Document[fieldKey];
}
render() {
const field = this.field;
if (!field) {
return <p>{'<null>'}</p>;
}
- if (field instanceof TextField) {
- return <p>{field.Data}</p>;
+ if (typeof field === "string") {
+ return <p>{field}</p>;
}
else if (field instanceof RichTextField) {
return <FormattedTextBox {...this.props} />;
@@ -85,7 +76,7 @@ export class FieldView extends React.Component<FieldViewProps> {
else if (field instanceof AudioField) {
return <AudioBox {...this.props} />;
}
- else if (field instanceof Document) {
+ else if (field instanceof Doc) {
return (
<DocumentContentsView Document={field}
addDocument={undefined}
@@ -96,30 +87,30 @@ export class FieldView extends React.Component<FieldViewProps> {
PanelHeight={() => 100}
isTopMost={true} //TODO Why is this top most?
selectOnLoad={false}
- focus={emptyDocFunction}
+ focus={emptyFunction}
isSelected={returnFalse}
select={returnFalse}
- layoutKey={KeyStore.Layout}
+ layoutKey={"layout"}
ContainingCollectionView={this.props.ContainingCollectionView}
parentActive={this.props.active}
toggleMinimized={emptyFunction}
whenActiveChanged={this.props.whenActiveChanged} />
);
}
- else if (field instanceof ListField) {
+ else if (field instanceof List) {
return (<div>
- {(field as ListField<Field>).Data.map(f => f instanceof Document ? f.Title : f.GetValue().toString()).join(", ")}
+ {field.map(f => f instanceof Doc ? f.title : f.toString()).join(", ")}
</div>);
}
// bcz: this belongs here, but it doesn't render well so taking it out for now
// else if (field instanceof HtmlField) {
// return <WebBox {...this.props} />
// }
- else if (field instanceof NumberField) {
- return <p>{field.Data}</p>;
+ else if (typeof field === "number") {
+ return <p>{field}</p>;
}
- else if (field !== FieldWaiting) {
- return <p>{JSON.stringify(field.GetValue())}</p>;
+ else if (!(field instanceof Promise)) {
+ return <p>{JSON.stringify(field)}</p>;
}
else {
return <p> {"Waiting for server..."} </p>;
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index 4e841af98..c65b1ac89 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -4,11 +4,6 @@ import { history } from "prosemirror-history";
import { keymap } from "prosemirror-keymap";
import { EditorState, Plugin, Transaction } from "prosemirror-state";
import { EditorView } from "prosemirror-view";
-import { FieldWaiting, Opt } from "../../../fields/Field";
-import { KeyStore } from "../../../fields/KeyStore";
-import { RichTextField } from "../../../fields/RichTextField";
-import { TextField } from "../../../fields/TextField";
-import { Document } from "../../../fields/Document";
import buildKeymap from "../../util/ProsemirrorKeymap";
import { inpRules } from "../../util/RichTextRules";
import { schema } from "../../util/RichTextSchema";
@@ -20,8 +15,12 @@ import { FieldView, FieldViewProps } from "./FieldView";
import "./FormattedTextBox.scss";
import React = require("react");
import { SelectionManager } from "../../util/SelectionManager";
+import { DocComponent } from "../DocComponent";
+import { createSchema, makeInterface } from "../../../new_fields/Schema";
+import { Opt, Doc } from "../../../new_fields/Doc";
import { observer } from "mobx-react";
import { InkingControl } from "../InkingControl";
+import { StrCast } from "../../../new_fields/Types";
const { buildMenuItems } = require("prosemirror-example-setup");
const { menuBar } = require("prosemirror-menu");
@@ -46,8 +45,15 @@ export interface FormattedTextBoxOverlay {
isOverlay?: boolean;
}
+const richTextSchema = createSchema({
+ documentText: "string"
+});
+
+type RichTextDocument = makeInterface<[typeof richTextSchema]>;
+const RichTextDocument = makeInterface(richTextSchema);
+
@observer
-export class FormattedTextBox extends React.Component<(FieldViewProps & FormattedTextBoxOverlay)> {
+export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTextBoxOverlay), RichTextDocument>(RichTextDocument) {
public static LayoutString(fieldStr: string = "DataKey") {
return FieldView.LayoutString(FormattedTextBox, fieldStr);
}
@@ -62,7 +68,6 @@ export class FormattedTextBox extends React.Component<(FieldViewProps & Formatte
super(props);
this._ref = React.createRef();
- this.onChange = this.onChange.bind(this);
}
_applyingChange: boolean = false;
@@ -77,7 +82,7 @@ export class FormattedTextBox extends React.Component<(FieldViewProps & Formatte
JSON.stringify(state.toJSON()),
RichTextField
);
- this.props.Document.SetDataOnPrototype(KeyStore.DocumentText, state.doc.textBetween(0, state.doc.content.size, "\n\n"), TextField);
+ Doc.SetOnPrototype(this.props.Document, "documentText", state.doc.textBetween(0, state.doc.content.size, "\n\n"));
this._applyingChange = false;
// doc.SetData(fieldKey, JSON.stringify(state.toJSON()), RichTextField);
}
@@ -161,13 +166,6 @@ export class FormattedTextBox extends React.Component<(FieldViewProps & Formatte
}
}
- @action
- onChange(e: React.ChangeEvent<HTMLInputElement>) {
- const { fieldKey, Document } = this.props;
- Document.SetOnPrototype(fieldKey, new RichTextField(e.target.value));
- // doc.SetData(fieldKey, e.target.value, RichTextField);
- }
- @action
onPointerDown = (e: React.PointerEvent): void => {
if (e.button === 1 && this.props.isSelected() && !e.altKey && !e.ctrlKey && !e.metaKey) {
console.log("first");
@@ -188,7 +186,7 @@ export class FormattedTextBox extends React.Component<(FieldViewProps & Formatte
onFocused = (e: React.FocusEvent): void => {
if (!this.props.isOverlay) {
- if (MainOverlayTextBox.Instance.TextDoc != this.props.Document) {
+ if (MainOverlayTextBox.Instance.TextDoc !== this.props.Document) {
MainOverlayTextBox.Instance.SetTextDoc(this.props.Document, this.props.fieldKey, this._ref.current!, this.props.ScreenToLocalTransform);
}
} else {
@@ -253,9 +251,8 @@ export class FormattedTextBox extends React.Component<(FieldViewProps & Formatte
});
}
- @action
onKeyPress = (e: React.KeyboardEvent) => {
- if (e.key == "Escape") {
+ if (e.key === "Escape") {
SelectionManager.DeselectAll();
}
e.stopPropagation();
@@ -263,15 +260,15 @@ export class FormattedTextBox extends React.Component<(FieldViewProps & Formatte
// stop propagation doesn't seem to stop propagation of native keyboard events.
// so we set a flag on the native event that marks that the event's been handled.
(e.nativeEvent as any).DASHFormattedTextBoxHandled = true;
- if (this.props.Document.Title.startsWith("-") && this._editorView) {
+ if (StrCast(this.props.Document.title).startsWith("-") && this._editorView) {
let str = this._editorView.state.doc.textContent;
let titlestr = str.substr(0, Math.min(40, str.length));
- this.props.Document.SetText(KeyStore.Title, "-" + titlestr + (str.length > 40 ? "..." : ""));
- };
+ this.props.Document.title = "-" + titlestr + (str.length > 40 ? "..." : "");
+ }
}
render() {
let style = this.props.isOverlay ? "scroll" : "hidden";
- let color = this.props.Document.GetText(KeyStore.BackgroundColor, "");
+ let color = StrCast(this.props.Document.backgroundColor);
let interactive = InkingControl.Instance.selectedTool ? "" : "interactive";
return (
<div className={`formattedTextBox-cont-${style}`}
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 2cbb0fa90..0e9e904a8 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -3,11 +3,6 @@ import { action, observable } from 'mobx';
import { observer } from "mobx-react";
import Lightbox from 'react-image-lightbox';
import 'react-image-lightbox/style.css'; // This only needs to be imported once in your app
-import { Document } from '../../../fields/Document';
-import { FieldWaiting } from '../../../fields/Field';
-import { ImageField } from '../../../fields/ImageField';
-import { KeyStore } from '../../../fields/KeyStore';
-import { ListField } from '../../../fields/ListField';
import { Utils } from '../../../Utils';
import { DragManager } from '../../util/DragManager';
import { undoBatch } from '../../util/UndoManager';
@@ -15,14 +10,27 @@ import { ContextMenu } from "../../views/ContextMenu";
import { FieldView, FieldViewProps } from './FieldView';
import "./ImageBox.scss";
import React = require("react");
+import { createSchema, makeInterface, listSpec } from '../../../new_fields/Schema';
+import { DocComponent } from '../DocComponent';
+import { positionSchema } from './DocumentView';
+import { FieldValue, Cast, StrCast } from '../../../new_fields/Types';
+import { ImageField } from '../../../new_fields/URLField';
+import { List } from '../../../new_fields/List';
import { InkingControl } from '../InkingControl';
-import { NumberField } from '../../../fields/NumberField';
+import { Doc } from '../../../new_fields/Doc';
+
+export const pageSchema = createSchema({
+ curPage: "number"
+});
+
+type ImageDocument = makeInterface<[typeof pageSchema, typeof positionSchema]>;
+const ImageDocument = makeInterface(pageSchema, positionSchema);
@observer
-export class ImageBox extends React.Component<FieldViewProps> {
+export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageDocument) {
public static LayoutString() { return FieldView.LayoutString(ImageBox); }
- private _imgRef: React.RefObject<HTMLImageElement>;
+ private _imgRef: React.RefObject<HTMLImageElement> = React.createRef();
private _downX: number = 0;
private _downY: number = 0;
private _lastTap: number = 0;
@@ -30,20 +38,13 @@ export class ImageBox extends React.Component<FieldViewProps> {
@observable private _isOpen: boolean = false;
private dropDisposer?: DragManager.DragDropDisposer;
- constructor(props: FieldViewProps) {
- super(props);
-
- this._imgRef = React.createRef();
- }
-
@action
onLoad = (target: any) => {
var h = this._imgRef.current!.naturalHeight;
var w = this._imgRef.current!.naturalWidth;
if (this._photoIndex === 0) {
- this.props.Document.SetNumber(KeyStore.NativeHeight, this.props.Document.GetNumber(KeyStore.NativeWidth, 0) * h / w);
- this.props.Document.GetTAsync(KeyStore.Width, NumberField, field =>
- field && this.props.Document.SetNumber(KeyStore.Height, field.Data * h / w));
+ this.Document.nativeHeight = FieldValue(this.Document.nativeWidth, 0) * h / w;
+ this.Document.height = FieldValue(this.Document.width, 0) * h / w;
}
}
@@ -66,19 +67,18 @@ export class ImageBox extends React.Component<FieldViewProps> {
@undoBatch
drop = (e: Event, de: DragManager.DropEvent) => {
if (de.data instanceof DragManager.DocumentDragData) {
- de.data.droppedDocuments.map(action((drop: Document) => {
- let layout = drop.GetText(KeyStore.BackgroundLayout, "");
+ de.data.droppedDocuments.forEach(action((drop: Doc) => {
+ let layout = StrCast(drop.backgroundLayout);
if (layout.indexOf(ImageBox.name) !== -1) {
- let imgData = this.props.Document.Get(KeyStore.Data);
- if (imgData instanceof ImageField && imgData) {
- this.props.Document.SetOnPrototype(KeyStore.Data, new ListField([imgData]));
+ let imgData = this.props.Document[this.props.fieldKey];
+ if (imgData instanceof ImageField) {
+ Doc.SetOnPrototype(this.props.Document, "data", new List([imgData]));
}
- let imgList = this.props.Document.GetList(KeyStore.Data, [] as any[]);
+ let imgList = Cast(this.props.Document[this.props.fieldKey], listSpec(ImageField), [] as any[]);
if (imgList) {
- let field = drop.Get(KeyStore.Data);
- if (field === FieldWaiting) { }
- else if (field instanceof ImageField) imgList.push(field);
- else if (field instanceof ListField) imgList.push(field.Data);
+ let field = drop.data;
+ if (field instanceof ImageField) imgList.push(field);
+ else if (field instanceof List) imgList.concat(field);
}
e.stopPropagation();
}
@@ -128,9 +128,9 @@ export class ImageBox extends React.Component<FieldViewProps> {
}
specificContextMenu = (e: React.MouseEvent): void => {
- let field = this.props.Document.GetT(this.props.fieldKey, ImageField);
- if (field && field !== FieldWaiting) {
- let url = field.Data.href;
+ let field = Cast(this.Document[this.props.fieldKey], ImageField);
+ if (field) {
+ let url = field.url.href;
ContextMenu.Instance.addItem({
description: "Copy path", event: () => {
Utils.CopyText(url);
@@ -142,27 +142,26 @@ export class ImageBox extends React.Component<FieldViewProps> {
@action
onDotDown(index: number) {
this._photoIndex = index;
- this.props.Document.SetNumber(KeyStore.CurPage, index);
+ this.Document.curPage = index;
}
dots(paths: string[]) {
- let nativeWidth = this.props.Document.GetNumber(KeyStore.NativeWidth, 1);
+ let nativeWidth = FieldValue(this.Document.nativeWidth, 1);
let dist = Math.min(nativeWidth / paths.length, 40);
let left = (nativeWidth - paths.length * dist) / 2;
return paths.map((p, i) =>
<div className="imageBox-placer" key={i} >
- <div className="imageBox-dot" style={{ background: (i == this._photoIndex ? "black" : "gray"), transform: `translate(${i * dist + left}px, 0px)` }} onPointerDown={(e: React.PointerEvent) => { e.stopPropagation(); this.onDotDown(i); }} />
+ <div className="imageBox-dot" style={{ background: (i === this._photoIndex ? "black" : "gray"), transform: `translate(${i * dist + left}px, 0px)` }} onPointerDown={(e: React.PointerEvent) => { e.stopPropagation(); this.onDotDown(i); }} />
</div>
);
}
render() {
- let field = this.props.Document.Get(this.props.fieldKey);
+ let field = this.Document[this.props.fieldKey];
let paths: string[] = ["http://www.cs.brown.edu/~bcz/face.gif"];
- if (field === FieldWaiting) paths = ["https://image.flaticon.com/icons/svg/66/66163.svg"];
- else if (field instanceof ImageField) paths = [field.Data.href];
- else if (field instanceof ListField) paths = field.Data.filter(val => val as ImageField).map(p => (p as ImageField).Data.href);
- let nativeWidth = this.props.Document.GetNumber(KeyStore.NativeWidth, 1);
+ if (field instanceof ImageField) paths = [field.url.href];
+ else if (field instanceof List) paths = field.filter(val => val instanceof ImageField).map(p => (p as ImageField).url.href);
+ let nativeWidth = FieldValue(this.Document.nativeWidth, 1);
let interactive = InkingControl.Instance.selectedTool ? "" : "-interactive";
return (
<div className={`imageBox-cont${interactive}`} onPointerDown={this.onPointerDown} onDrop={this.onDrop} ref={this.createDropTarget} onContextMenu={this.specificContextMenu}>
diff --git a/src/client/views/nodes/LinkMenu.tsx b/src/client/views/nodes/LinkMenu.tsx
index ac09da305..3ecc8555d 100644
--- a/src/client/views/nodes/LinkMenu.tsx
+++ b/src/client/views/nodes/LinkMenu.tsx
@@ -1,15 +1,13 @@
import { action, observable } from "mobx";
import { observer } from "mobx-react";
-import { Document } from "../../../fields/Document";
-import { FieldWaiting } from "../../../fields/Field";
-import { Key } from "../../../fields/Key";
-import { KeyStore } from '../../../fields/KeyStore';
-import { ListField } from "../../../fields/ListField";
import { DocumentView } from "./DocumentView";
import { LinkBox } from "./LinkBox";
import { LinkEditor } from "./LinkEditor";
import './LinkMenu.scss';
import React = require("react");
+import { Doc, Id } from "../../../new_fields/Doc";
+import { Cast, FieldValue } from "../../../new_fields/Types";
+import { listSpec } from "../../../new_fields/Schema";
interface Props {
docView: DocumentView;
@@ -19,28 +17,28 @@ interface Props {
@observer
export class LinkMenu extends React.Component<Props> {
- @observable private _editingLink?: Document;
+ @observable private _editingLink?: Doc;
- renderLinkItems(links: Document[], key: Key, type: string) {
+ renderLinkItems(links: Doc[], key: string, type: string) {
return links.map(link => {
- let doc = link.GetT(key, Document);
- if (doc && doc !== FieldWaiting) {
- return <LinkBox key={doc.Id} linkDoc={link} linkName={link.Title} pairedDoc={doc} showEditor={action(() => this._editingLink = link)} type={type} />;
+ let doc = FieldValue(Cast(link[key], Doc));
+ if (doc) {
+ return <LinkBox key={doc[Id]} linkDoc={link} linkName={Cast(link.title, "string", "")} pairedDoc={doc} showEditor={action(() => this._editingLink = link)} type={type} />;
}
});
}
render() {
//get list of links from document
- let linkFrom: Document[] = this.props.docView.props.Document.GetData(KeyStore.LinkedFromDocs, ListField, []);
- let linkTo: Document[] = this.props.docView.props.Document.GetData(KeyStore.LinkedToDocs, ListField, []);
+ let linkFrom: Doc[] = Cast(this.props.docView.props.Document.linkedFromDocs, listSpec(Doc), []);
+ let linkTo: Doc[] = Cast(this.props.docView.props.Document.linkedToDocs, listSpec(Doc), []);
if (this._editingLink === undefined) {
return (
<div id="linkMenu-container">
<input id="linkMenu-searchBar" type="text" placeholder="Search..."></input>
<div id="linkMenu-list">
- {this.renderLinkItems(linkTo, KeyStore.LinkedToDocs, "Destination: ")}
- {this.renderLinkItems(linkFrom, KeyStore.LinkedFromDocs, "Source: ")}
+ {this.renderLinkItems(linkTo, "linkedTo", "Destination: ")}
+ {this.renderLinkItems(linkFrom, "linkedFrom", "Source: ")}
</div>
</div>
);
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 81ceb37f6..7fbfed1c0 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -6,10 +6,6 @@ import Measure from "react-measure";
//@ts-ignore
import { Document, Page } from "react-pdf";
import 'react-pdf/dist/Page/AnnotationLayer.css';
-import { FieldWaiting, Opt } from '../../../fields/Field';
-import { ImageField } from '../../../fields/ImageField';
-import { KeyStore } from '../../../fields/KeyStore';
-import { PDFField } from '../../../fields/PDFField';
import { RouteStore } from "../../../server/RouteStore";
import { Utils } from '../../../Utils';
import { Annotation } from './Annotation';
@@ -19,6 +15,13 @@ import "./PDFBox.scss";
import { Sticky } from './Sticky'; //you should look at sticky and annotation, because they are used here
import React = require("react");
import { SelectionManager } from "../../util/SelectionManager";
+import { Cast, FieldValue } from "../../../new_fields/Types";
+import { Opt } from "../../../new_fields/Doc";
+import { DocComponent } from "../DocComponent";
+import { makeInterface } from "../../../new_fields/Schema";
+import { positionSchema } from "./DocumentView";
+import { pageSchema } from "./ImageBox";
+import { ImageField, PdfField } from "../../../new_fields/URLField";
/** ALSO LOOK AT: Annotation.tsx, Sticky.tsx
* This method renders PDF and puts all kinds of functionalities such as annotation, highlighting,
@@ -51,8 +54,12 @@ import { SelectionManager } from "../../util/SelectionManager";
*
* written by: Andrew Kim
*/
+
+type PdfDocument = makeInterface<[typeof positionSchema, typeof pageSchema]>;
+const PdfDocument = makeInterface(positionSchema, pageSchema);
+
@observer
-export class PDFBox extends React.Component<FieldViewProps> {
+export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocument) {
public static LayoutString() { return FieldView.LayoutString(PDFBox); }
private _mainDiv = React.createRef<HTMLDivElement>();
@@ -89,8 +96,8 @@ export class PDFBox extends React.Component<FieldViewProps> {
@observable private _interactive: boolean = false;
@observable private _loaded: boolean = false;
- @computed private get curPage() { return this.props.Document.GetNumber(KeyStore.CurPage, 1); }
- @computed private get thumbnailPage() { return this.props.Document.GetNumber(KeyStore.ThumbnailPage, -1); }
+ @computed private get curPage() { return FieldValue(this.Document.curPage, 1); }
+ @computed private get thumbnailPage() { return Cast(this.props.Document.thumbnailPage, "number", -1); }
componentDidMount() {
this._reactionDisposer = reaction(
@@ -380,14 +387,13 @@ export class PDFBox extends React.Component<FieldViewProps> {
saveThumbnail = () => {
this._renderAsSvg = false;
setTimeout(() => {
- var me = this;
- let nwidth = me.props.Document.GetNumber(KeyStore.NativeWidth, 0);
- let nheight = me.props.Document.GetNumber(KeyStore.NativeHeight, 0);
+ let nwidth = FieldValue(this.Document.nativeWidth, 0);
+ let nheight = FieldValue(this.Document.nativeHeight, 0);
htmlToImage.toPng(this._mainDiv.current!, { width: nwidth, height: nheight, quality: 1 })
.then(action((dataUrl: string) => {
- me.props.Document.SetData(KeyStore.Thumbnail, new URL(dataUrl), ImageField);
- me.props.Document.SetNumber(KeyStore.ThumbnailPage, me.props.Document.GetNumber(KeyStore.CurPage, -1));
- me._renderAsSvg = true;
+ this.props.Document.thumbnail = new ImageField(new URL(dataUrl));
+ this.props.Document.thumbnailPage = FieldValue(this.Document.curPage, -1);
+ this._renderAsSvg = true;
}))
.catch(function (error: any) {
console.error('oops, something went wrong!', error);
@@ -414,7 +420,7 @@ export class PDFBox extends React.Component<FieldViewProps> {
}
// bcz: the number of pages should really be set when the document is imported.
- this.props.Document.SetNumber(KeyStore.NumPages, page._transport.numPages);
+ this.props.Document.numPages = page._transport.numPages;
if (this._perPageInfo.length === 0) { //Makes sure it only runs once
this._perPageInfo = [...Array(page._transport.numPages)];
}
@@ -426,11 +432,11 @@ export class PDFBox extends React.Component<FieldViewProps> {
// bcz: the nativeHeight should really be set when the document is imported.
// also, the native dimensions could be different for different pages of the PDF
// so this design is flawed.
- var nativeWidth = this.props.Document.GetNumber(KeyStore.NativeWidth, 0);
- if (!this.props.Document.GetNumber(KeyStore.NativeHeight, 0)) {
+ var nativeWidth = FieldValue(this.Document.nativeWidth, 0);
+ if (!FieldValue(this.Document.nativeHeight, 0)) {
var nativeHeight = nativeWidth * r.entry.height / r.entry.width;
- this.props.Document.SetNumber(KeyStore.Height, nativeHeight / nativeWidth * this.props.Document.GetNumber(KeyStore.Width, 0));
- this.props.Document.SetNumber(KeyStore.NativeHeight, nativeHeight);
+ this.props.Document.height = nativeHeight / nativeWidth * FieldValue(this.Document.width, 0);
+ this.props.Document.nativeHeight = nativeHeight;
}
}
@@ -438,8 +444,8 @@ export class PDFBox extends React.Component<FieldViewProps> {
get pdfContent() {
let page = this.curPage;
const renderHeight = 2400;
- let pdfUrl = this.props.Document.GetT(this.props.fieldKey, PDFField);
- let xf = this.props.Document.GetNumber(KeyStore.NativeHeight, 0) / renderHeight;
+ let pdfUrl = Cast(this.props.Document[this.props.fieldKey], PdfField);
+ let xf = FieldValue(this.Document.nativeHeight, 0) / renderHeight;
return <div className="pdfBox-contentContainer" key="container" style={{ transform: `scale(${xf}, ${xf})` }}>
<Document file={window.origin + RouteStore.corsProxy + `/${pdfUrl}`} renderMode={this._renderAsSvg ? "svg" : ""}>
<Measure onResize={this.setScaling}>
@@ -456,8 +462,8 @@ export class PDFBox extends React.Component<FieldViewProps> {
@computed
get pdfRenderer() {
let proxy = this._loaded ? (null) : this.imageProxyRenderer;
- let pdfUrl = this.props.Document.GetT(this.props.fieldKey, PDFField);
- if ((!this._interactive && proxy) || !pdfUrl || pdfUrl === FieldWaiting) {
+ let pdfUrl = Cast(this.props.Document[this.props.fieldKey], PdfField);
+ if ((!this._interactive && proxy) || !pdfUrl) {
return proxy;
}
return [
@@ -470,10 +476,10 @@ export class PDFBox extends React.Component<FieldViewProps> {
@computed
get imageProxyRenderer() {
- let thumbField = this.props.Document.Get(KeyStore.Thumbnail);
+ let thumbField = this.props.Document.thumbnail;
if (thumbField) {
- let path = thumbField === FieldWaiting || this.thumbnailPage !== this.curPage ? "https://image.flaticon.com/icons/svg/66/66163.svg" :
- thumbField instanceof ImageField ? thumbField.Data.href : "http://cs.brown.edu/people/bcz/prairie.jpg";
+ let path = this.thumbnailPage !== this.curPage ? "https://image.flaticon.com/icons/svg/66/66163.svg" :
+ thumbField instanceof ImageField ? thumbField.url.href : "http://cs.brown.edu/people/bcz/prairie.jpg";
return <img src={path} width="100%" />;
}
return (null);
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index 1e6bc58c9..86276660a 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -1,17 +1,23 @@
import React = require("react");
-import { action, computed, IReactionDisposer, trace } from "mobx";
import { observer } from "mobx-react";
-import Measure from "react-measure";
-import { FieldWaiting, Opt } from '../../../fields/Field';
-import { KeyStore } from "../../../fields/KeyStore";
-import { VideoField } from '../../../fields/VideoField';
import { FieldView, FieldViewProps } from './FieldView';
import "./VideoBox.scss";
+import { action, computed } from "mobx";
+import { DocComponent } from "../DocComponent";
+import { positionSchema } from "./DocumentView";
+import { makeInterface } from "../../../new_fields/Schema";
+import { pageSchema } from "./ImageBox";
+import { Cast, FieldValue } from "../../../new_fields/Types";
+import { VideoField } from "../../../new_fields/URLField";
+import Measure from "react-measure";
+import "./VideoBox.scss";
+
+type VideoDocument = makeInterface<[typeof positionSchema, typeof pageSchema]>;
+const VideoDocument = makeInterface(positionSchema, pageSchema);
@observer
-export class VideoBox extends React.Component<FieldViewProps> {
+export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoDocument) {
- private _reactionDisposer: Opt<IReactionDisposer>;
private _videoRef = React.createRef<HTMLVideoElement>();
public static LayoutString() { return FieldView.LayoutString(VideoBox); }
@@ -19,7 +25,7 @@ export class VideoBox extends React.Component<FieldViewProps> {
super(props);
}
- @computed private get curPage() { return this.props.Document.GetNumber(KeyStore.CurPage, -1); }
+ @computed private get curPage() { return FieldValue(this.Document.curPage, -1); }
_loaded: boolean = false;
@@ -30,12 +36,12 @@ export class VideoBox extends React.Component<FieldViewProps> {
// bcz: the nativeHeight should really be set when the document is imported.
// also, the native dimensions could be different for different pages of the PDF
// so this design is flawed.
- var nativeWidth = this.props.Document.GetNumber(KeyStore.NativeWidth, 0);
- var nativeHeight = this.props.Document.GetNumber(KeyStore.NativeHeight, 0);
+ var nativeWidth = FieldValue(this.Document.nativeWidth, 0);
+ var nativeHeight = FieldValue(this.Document.nativeHeight, 0);
var newNativeHeight = nativeWidth * r.entry.height / r.entry.width;
if (!nativeHeight && newNativeHeight !== nativeHeight && !isNaN(newNativeHeight)) {
- this.props.Document.SetNumber(KeyStore.Height, newNativeHeight / nativeWidth * this.props.Document.GetNumber(KeyStore.Width, 0));
- this.props.Document.SetNumber(KeyStore.NativeHeight, newNativeHeight);
+ this.Document.height = newNativeHeight / nativeWidth * FieldValue(this.Document.width, 0);
+ this.Document.nativeHeight = newNativeHeight;
}
} else {
this._loaded = true;
@@ -55,12 +61,11 @@ export class VideoBox extends React.Component<FieldViewProps> {
}
render() {
- let field = this.props.Document.GetT(this.props.fieldKey, VideoField);
- if (!field || field === FieldWaiting) {
+ let field = FieldValue(Cast(this.Document[this.props.fieldKey], VideoField));
+ if (!field) {
return <div>Loading</div>;
}
- let path = field.Data.href;
- trace();
+ let path = field.url.href;
return (
<Measure onResize={this.setScaling}>
{({ measureRef }) =>
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index f7f5e31e6..c12fd5655 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -1,11 +1,10 @@
import "./WebBox.scss";
import React = require("react");
-import { WebField } from '../../../fields/WebField';
import { FieldViewProps, FieldView } from './FieldView';
-import { FieldWaiting, Opt } from '../../../fields/Field';
+import { HtmlField } from "../../../new_fields/HtmlField";
+import { WebField } from "../../../new_fields/URLField";
import { observer } from "mobx-react";
import { computed, reaction, IReactionDisposer } from 'mobx';
-import { KeyStore } from '../../../fields/KeyStore';
import { DocumentDecorations } from "../DocumentDecorations";
@observer
@@ -13,8 +12,6 @@ export class WebBox extends React.Component<FieldViewProps> {
public static LayoutString() { return FieldView.LayoutString(WebBox); }
- @computed get html(): string { return this.props.Document.GetHtml(KeyStore.Data, ""); }
-
_ignore = 0;
onPreWheel = (e: React.WheelEvent) => {
this._ignore = e.timeStamp;
@@ -33,14 +30,18 @@ export class WebBox extends React.Component<FieldViewProps> {
}
}
render() {
- let field = this.props.Document.Get(this.props.fieldKey);
- let path = field === FieldWaiting ? "https://image.flaticon.com/icons/svg/66/66163.svg" :
- field instanceof WebField ? field.Data.href : "https://crossorigin.me/" + "https://cs.brown.edu";
-
+ let field = this.props.Document[this.props.fieldKey];
+ let view;
+ if (field instanceof HtmlField) {
+ view = <span id="webBox-htmlSpan" dangerouslySetInnerHTML={{ __html: field.html }} />;
+ } else if (field instanceof WebField) {
+ view = <iframe src={field.url.href} style={{ position: "absolute", width: "100%", height: "100%" }} />;
+ } else {
+ view = <iframe src={"https://crossorigin.me/https://cs.brown.edu"} style={{ position: "absolute", width: "100%", height: "100%" }} />;
+ }
let content =
<div style={{ width: "100%", height: "100%", position: "absolute" }} onWheel={this.onPostWheel} onPointerDown={this.onPostPointer} onPointerMove={this.onPostPointer} onPointerUp={this.onPostPointer}>
- {this.html ? <span id="webBox-htmlSpan" dangerouslySetInnerHTML={{ __html: this.html }} /> :
- <iframe src={path} style={{ position: "absolute", width: "100%", height: "100%" }} />}
+ {view}
</div>;
let frozen = !this.props.isSelected() || DocumentDecorations.Instance.Interacting;