aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/.DS_Storebin0 -> 6148 bytes
-rw-r--r--src/client/documents/Documents.ts225
-rw-r--r--src/client/util/DragManager.ts30
-rw-r--r--src/client/views/Main.scss22
-rw-r--r--src/client/views/Main.tsx219
-rw-r--r--src/client/views/collections/CollectionDockingView.tsx44
-rw-r--r--src/client/views/collections/CollectionFreeFormView.scss28
-rw-r--r--src/client/views/collections/CollectionFreeFormView.tsx74
-rw-r--r--src/client/views/collections/CollectionSchemaView.scss9
-rw-r--r--src/client/views/collections/CollectionSchemaView.tsx8
-rw-r--r--src/client/views/collections/CollectionTreeView.tsx2
-rw-r--r--src/client/views/collections/CollectionView.tsx8
-rw-r--r--src/client/views/collections/CollectionViewBase.tsx2
-rw-r--r--src/client/views/nodes/DocumentView.tsx10
-rw-r--r--src/client/views/nodes/FieldView.tsx19
-rw-r--r--src/client/views/nodes/FormattedTextBox.scss2
-rw-r--r--src/client/views/nodes/FormattedTextBox.tsx25
-rw-r--r--src/client/views/nodes/ImageBox.tsx1
-rw-r--r--src/client/views/nodes/WebBox.scss14
-rw-r--r--src/client/views/nodes/WebBox.tsx38
-rw-r--r--src/client/views/nodes/WebView.tsx22
-rw-r--r--src/fields/Document.ts5
-rw-r--r--src/fields/Key.ts1
-rw-r--r--src/fields/WebField.ts30
-rw-r--r--src/server/Message.ts2
-rw-r--r--src/server/ServerUtil.ts3
26 files changed, 436 insertions, 407 deletions
diff --git a/src/.DS_Store b/src/.DS_Store
new file mode 100644
index 000000000..4d6acb95a
--- /dev/null
+++ b/src/.DS_Store
Binary files differ
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 9770e5cdc..ba13cc31b 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -7,10 +7,12 @@ import { ListField } from "../../fields/ListField";
import { FormattedTextBox } from "../views/nodes/FormattedTextBox";
import { ImageField } from "../../fields/ImageField";
import { ImageBox } from "../views/nodes/ImageBox";
+import { WebField } from "../../fields/WebField";
+import { WebBox } from "../views/nodes/WebBox";
import { CollectionView, CollectionViewType } from "../views/collections/CollectionView";
-import { FieldView } from "../views/nodes/FieldView";
import { HtmlField } from "../../fields/HtmlField";
-import { WebView } from "../views/nodes/WebView";
+import { Key } from "../../fields/Key"
+import { Field } from "../../fields/Field";
export interface DocumentOptions {
x?: number;
@@ -20,109 +22,110 @@ export interface DocumentOptions {
nativeWidth?: number;
nativeHeight?: number;
title?: string;
+ panx?: number;
+ pany?: number;
+ scale?: number;
+ layout?: string;
+ layoutKeys?: Key[];
+ viewType?: number;
}
export namespace Documents {
- export function initProtos(callback: () => void) {
- Server.GetFields([collectionProtoId, textProtoId, imageProtoId], (fields) => {
- collectionProto = fields[collectionProtoId] as Document;
+ let textProto: Document;
+ let imageProto: Document;
+ let webProto: Document;
+ let collProto: Document;
+ const textProtoId = "textProto";
+ const imageProtoId = "imageProto";
+ const webProtoId = "webProto";
+ const collProtoId = "collectionProto";
+
+ export function initProtos(mainDocId: string, callback: (mainDoc?: Document) => void) {
+ Server.GetFields([collProtoId, textProtoId, imageProtoId, mainDocId], (fields) => {
+ collProto = fields[collProtoId] as Document;
imageProto = fields[imageProtoId] as Document;
textProto = fields[textProtoId] as Document;
- callback()
+ webProto = fields[webProtoId] as Document;
+ callback(fields[mainDocId] as Document)
});
}
+ function assignOptions(doc: Document, options: DocumentOptions): Document {
+ if (options.x !== undefined) { doc.SetNumber(KeyStore.X, options.x); }
+ if (options.y !== undefined) { doc.SetNumber(KeyStore.Y, options.y); }
+ if (options.width !== undefined) { doc.SetNumber(KeyStore.Width, options.width); }
+ if (options.height !== undefined) { doc.SetNumber(KeyStore.Height, options.height); }
+ if (options.nativeWidth !== undefined) { doc.SetNumber(KeyStore.NativeWidth, options.nativeWidth); }
+ if (options.nativeHeight !== undefined) { doc.SetNumber(KeyStore.NativeHeight, options.nativeHeight); }
+ if (options.title !== undefined) { doc.SetText(KeyStore.Title, options.title); }
+ if (options.panx !== undefined) { doc.SetNumber(KeyStore.PanX, options.panx); }
+ if (options.pany !== undefined) { doc.SetNumber(KeyStore.PanY, options.pany); }
+ if (options.scale !== undefined) { doc.SetNumber(KeyStore.Scale, options.scale); }
+ if (options.viewType !== undefined) { doc.SetNumber(KeyStore.ViewType, options.viewType); }
+ if (options.layout !== undefined) { doc.SetText(KeyStore.Layout, options.layout); }
+ if (options.layoutKeys !== undefined) { doc.Set(KeyStore.LayoutKeys, new ListField(options.layoutKeys)); }
+ return doc;
+ }
+ function setupPrototypeOptions(protoId: string, title: string, layout: string, options: DocumentOptions): Document {
+ return assignOptions(new Document(protoId), { ...options, title: title, layout: layout });
+ }
+ function SetInstanceOptions<T, U extends Field & { Data: T }>(doc: Document, options: DocumentOptions, value: T, ctor: { new(): U }, id?: string) {
+ var deleg = doc.MakeDelegate(id);
+ deleg.SetData(KeyStore.Data, value, ctor);
+ return assignOptions(deleg, options);
+ }
- function setupOptions(doc: Document, options: DocumentOptions): void {
- if (options.x !== undefined) {
- doc.SetData(KeyStore.X, options.x, NumberField);
- }
- if (options.y !== undefined) {
- doc.SetData(KeyStore.Y, options.y, NumberField);
- }
- if (options.width !== undefined) {
- doc.SetData(KeyStore.Width, options.width, NumberField);
- }
- if (options.height !== undefined) {
- doc.SetData(KeyStore.Height, options.height, NumberField);
- }
- if (options.nativeWidth !== undefined) {
- doc.SetData(KeyStore.NativeWidth, options.nativeWidth, NumberField);
- }
- if (options.nativeHeight !== undefined) {
- doc.SetData(KeyStore.NativeHeight, options.nativeHeight, NumberField);
- }
- if (options.title !== undefined) {
- doc.SetData(KeyStore.Title, options.title, TextField);
+ function GetImagePrototype(): Document {
+ if (!imageProto) {
+ imageProto = setupPrototypeOptions(imageProtoId, "IMAGE_PROTO", CollectionView.LayoutString("AnnotationsKey"),
+ { x: 0, y: 0, nativeWidth: 300, width: 300, layoutKeys: [KeyStore.Data, KeyStore.Annotations] });
+ imageProto.SetText(KeyStore.BackgroundLayout, ImageBox.LayoutString());
}
- doc.SetData(KeyStore.Scale, 1, NumberField);
- doc.SetData(KeyStore.PanX, 0, NumberField);
- doc.SetData(KeyStore.PanY, 0, NumberField);
+ return imageProto;
}
-
- let textProto: Document;
- const textProtoId = "textProto";
function GetTextPrototype(): Document {
- if (!textProto) {
- textProto = new Document(textProtoId);
- textProto.Set(KeyStore.X, new NumberField(0));
- textProto.Set(KeyStore.Y, new NumberField(0));
- textProto.Set(KeyStore.Width, new NumberField(300));
- textProto.Set(KeyStore.Height, new NumberField(150));
- textProto.Set(KeyStore.Layout, new TextField(FormattedTextBox.LayoutString()));
- textProto.Set(KeyStore.LayoutKeys, new ListField([KeyStore.Data]));
- }
- return textProto;
+ return textProto ? textProto :
+ textProto = setupPrototypeOptions(textProtoId, "TEXT_PROTO", FormattedTextBox.LayoutString(),
+ { x: 0, y: 0, width: 300, height: 150, layoutKeys: [KeyStore.Data] });
}
-
- export function TextDocument(options: DocumentOptions = {}): Document {
- let doc = GetTextPrototype().MakeDelegate();
- setupOptions(doc, options);
- // doc.SetField(KeyStore.Data, new RichTextField());
- return doc;
+ function GetWebPrototype(): Document {
+ return webProto ? webProto :
+ webProto = setupPrototypeOptions(webProtoId, "WEB_PROTO", WebBox.LayoutString(),
+ { x: 0, y: 0, width: 300, height: 300, layoutKeys: [KeyStore.Data] });
}
-
- let htmlProto: Document;
- const htmlProtoId = "htmlProto";
- function GetHtmlPrototype(): Document {
- if (!htmlProto) {
- htmlProto = new Document(htmlProtoId);
- htmlProto.Set(KeyStore.X, new NumberField(0));
- htmlProto.Set(KeyStore.Y, new NumberField(0));
- htmlProto.Set(KeyStore.Width, new NumberField(300));
- htmlProto.Set(KeyStore.Height, new NumberField(150));
- htmlProto.Set(KeyStore.Layout, new TextField(WebView.LayoutString()));
- htmlProto.Set(KeyStore.LayoutKeys, new ListField([KeyStore.Data]));
- }
- return htmlProto;
+ function GetCollectionPrototype(): Document {
+ return collProto ? collProto :
+ collProto = setupPrototypeOptions(collProtoId, "COLLECTION_PROTO", CollectionView.LayoutString("DataKey"),
+ { panx: 0, pany: 0, scale: 1, layoutKeys: [KeyStore.Data] });
}
- export function HtmlDocument(html: string, options: DocumentOptions = {}): Document {
- let doc = GetHtmlPrototype().MakeDelegate();
- setupOptions(doc, options);
- doc.Set(KeyStore.Data, new HtmlField(html));
+ export function ImageDocument(url: string, options: DocumentOptions = {}) {
+ let doc = SetInstanceOptions(GetImagePrototype(), { ...options, layoutKeys: [KeyStore.Data, KeyStore.Annotations, KeyStore.Caption] },
+ new URL(url), ImageField);
+ doc.SetText(KeyStore.Caption, "my caption...");
+ doc.SetText(KeyStore.BackgroundLayout, EmbeddedCaption());
+ doc.SetText(KeyStore.OverlayLayout, FixedCaption());
return doc;
}
+ export function TextDocument(options: DocumentOptions = {}) {
+ return SetInstanceOptions(GetTextPrototype(), options, "", TextField);
+ }
+ export function WebDocument(url: string, options: DocumentOptions = {}) {
+ return SetInstanceOptions(GetWebPrototype(), options, new URL(url), WebField);
+ }
+ export function HtmlDocument(html: string, options: DocumentOptions = {}) {
+ return SetInstanceOptions(GetWebPrototype(), options, html, HtmlField);
+ }
+ export function FreeformDocument(documents: Array<Document>, options: DocumentOptions, id?: string) {
+ return SetInstanceOptions(GetCollectionPrototype(), { ...options, viewType: CollectionViewType.Freeform }, documents, ListField, id)
+ }
+ export function SchemaDocument(documents: Array<Document>, options: DocumentOptions, id?: string) {
+ return SetInstanceOptions(GetCollectionPrototype(), { ...options, viewType: CollectionViewType.Schema }, documents, ListField, id)
+ }
+ export function DockDocument(config: string, options: DocumentOptions, id?: string) {
+ return SetInstanceOptions(GetCollectionPrototype(), { ...options, viewType: CollectionViewType.Docking }, config, TextField, id)
+ }
- let imageProto: Document;
- const imageProtoId = "imageProto";
- function GetImagePrototype(): Document {
- if (!imageProto) {
- imageProto = new Document(imageProtoId);
- imageProto.Set(KeyStore.Title, new TextField("IMAGE PROTO"));
- imageProto.Set(KeyStore.X, new NumberField(0));
- imageProto.Set(KeyStore.Y, new NumberField(0));
- imageProto.Set(KeyStore.NativeWidth, new NumberField(300));
- imageProto.Set(KeyStore.Width, new NumberField(300));
- imageProto.Set(KeyStore.Layout, new TextField(CollectionView.LayoutString("AnnotationsKey")));
- imageProto.SetNumber(KeyStore.ViewType, CollectionViewType.Freeform)
- imageProto.Set(KeyStore.BackgroundLayout, new TextField(ImageBox.LayoutString()));
- // imageProto.SetField(KeyStore.Layout, new TextField('<div style={"background-image: " + {Data}} />'));
- imageProto.Set(KeyStore.LayoutKeys, new ListField([KeyStore.Data, KeyStore.Annotations]));
- return imageProto;
- }
- return imageProto;
- }
// example of custom display string for an image that shows a caption.
function EmbeddedCaption() {
@@ -140,54 +143,4 @@ export namespace Documents {
+ FormattedTextBox.LayoutString("CaptionKey") +
`</div>
</div>` };
-
- export function ImageDocument(url: string, options: DocumentOptions = {}): Document {
- let doc = GetImagePrototype().MakeDelegate();
- setupOptions(doc, options);
- doc.Set(KeyStore.Data, new ImageField(new URL(url)));
- doc.Set(KeyStore.Caption, new TextField("my caption..."));
- doc.Set(KeyStore.BackgroundLayout, new TextField(EmbeddedCaption()));
- doc.Set(KeyStore.OverlayLayout, new TextField(FixedCaption()));
- doc.Set(KeyStore.LayoutKeys, new ListField([KeyStore.Data, KeyStore.Annotations, KeyStore.Caption]));
- console.log("" + doc.GetNumber(KeyStore.Height, 311));
- return doc;
- }
-
- let collectionProto: Document;
- const collectionProtoId = "collectionProto";
- function GetCollectionPrototype(): Document {
- if (!collectionProto) {
- collectionProto = new Document(collectionProtoId);
- collectionProto.Set(KeyStore.Scale, new NumberField(1));
- collectionProto.Set(KeyStore.PanX, new NumberField(0));
- collectionProto.Set(KeyStore.PanY, new NumberField(0));
- collectionProto.Set(KeyStore.Layout, new TextField(CollectionView.LayoutString("DataKey")));
- collectionProto.Set(KeyStore.LayoutKeys, new ListField([KeyStore.Data]));
- }
- return collectionProto;
- }
-
- export function CollectionDocument(data: Array<Document> | string, viewType: CollectionViewType, options: DocumentOptions = {}, id?: string): Document {
- let doc = GetCollectionPrototype().MakeDelegate(id);
- setupOptions(doc, options);
- if (typeof data === "string") {
- doc.SetText(KeyStore.Data, data);
- } else {
- doc.SetData(KeyStore.Data, data, ListField);
- }
- doc.SetNumber(KeyStore.ViewType, viewType);
- return doc;
- }
-
- export function FreeformDocument(documents: Array<Document>, options: DocumentOptions, id?: string) {
- return CollectionDocument(documents, CollectionViewType.Freeform, options, id)
- }
-
- export function SchemaDocument(documents: Array<Document>, options: DocumentOptions, id?: string) {
- return CollectionDocument(documents, CollectionViewType.Schema, options, id)
- }
-
- export function DockDocument(config: string, options: DocumentOptions, id?: string) {
- return CollectionDocument(config, CollectionViewType.Docking, options, id)
- }
} \ No newline at end of file
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 8adadee0f..60910a40b 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -2,15 +2,16 @@ import { DocumentDecorations } from "../views/DocumentDecorations";
import { CollectionDockingView } from "../views/collections/CollectionDockingView";
import { Document } from "../../fields/Document"
import { action } from "mobx";
+import { DocumentView } from "../views/nodes/DocumentView";
-export function setupDrag(_reference: React.RefObject<HTMLDivElement>, _dragDocument: Document) {
+export function setupDrag(_reference: React.RefObject<HTMLDivElement>, docFunc: () => Document) {
let onRowMove = action((e: PointerEvent): void => {
e.stopPropagation();
e.preventDefault();
document.removeEventListener("pointermove", onRowMove);
document.removeEventListener('pointerup', onRowUp);
- DragManager.StartDrag(_reference.current!, { document: _dragDocument });
+ DragManager.StartDrag(_reference.current!, { document: docFunc() });
});
let onRowUp = action((e: PointerEvent): void => {
document.removeEventListener("pointermove", onRowMove);
@@ -20,9 +21,8 @@ export function setupDrag(_reference: React.RefObject<HTMLDivElement>, _dragDocu
// if (this.props.isSelected() || this.props.isTopMost) {
if (e.button == 0) {
e.stopPropagation();
- e.preventDefault();
if (e.shiftKey) {
- CollectionDockingView.Instance.StartOtherDrag(_reference.current!, _dragDocument);
+ CollectionDockingView.Instance.StartOtherDrag(docFunc(), e);
} else {
document.addEventListener("pointermove", onRowMove);
document.addEventListener('pointerup', onRowUp);
@@ -133,29 +133,43 @@ export namespace DragManager {
if (hideSource) {
ele.hidden = true;
}
-
const moveHandler = (e: PointerEvent) => {
e.stopPropagation();
e.preventDefault();
x += e.movementX;
y += e.movementY;
+ if (e.shiftKey) {
+ abortDrag();
+ const docView: DocumentView = dragData["documentView"];
+ const doc: Document = docView ? docView.props.Document : dragData["document"];
+ CollectionDockingView.Instance.StartOtherDrag(doc, { pageX: e.pageX, pageY: e.pageY, preventDefault: () => { }, button: 0 });
+ }
dragElement.style.transform = `translate(${x}px, ${y}px) scale(${scaleX}, ${scaleY})`;
};
- const upHandler = (e: PointerEvent) => {
+
+ const abortDrag = () => {
document.removeEventListener("pointermove", moveHandler, true);
document.removeEventListener("pointerup", upHandler);
- FinishDrag(dragElement, e, dragData, options);
+ dragDiv.removeChild(dragElement);
if (hideSource && !wasHidden) {
ele.hidden = false;
}
+ }
+ const upHandler = (e: PointerEvent) => {
+ abortDrag();
+ FinishDrag(ele, e, dragData, options);
};
document.addEventListener("pointermove", moveHandler, true);
document.addEventListener("pointerup", upHandler);
}
function FinishDrag(dragEle: HTMLElement, e: PointerEvent, dragData: { [index: string]: any }, options?: DragOptions) {
- dragDiv.removeChild(dragEle);
+ let parent = dragEle.parentElement;
+ if (parent)
+ parent.removeChild(dragEle);
const target = document.elementFromPoint(e.x, e.y);
+ if (parent)
+ parent.appendChild(dragEle);
if (!target) {
return;
}
diff --git a/src/client/views/Main.scss b/src/client/views/Main.scss
index e73f62904..4334ed299 100644
--- a/src/client/views/Main.scss
+++ b/src/client/views/Main.scss
@@ -28,4 +28,24 @@ h1 {
p {
margin: 0px;
padding: 0px;
-} \ No newline at end of file
+}
+::-webkit-scrollbar {
+ -webkit-appearance: none;
+ height:5px;
+ width:5px;
+}
+::-webkit-scrollbar-thumb {
+ border-radius: 2px;
+ background-color: rgba(0,0,0,.5);
+}
+
+.main-buttonDiv {
+ position: absolute;
+ width: 150px;
+ left: 0px;
+}
+.main-undoButtons {
+ position: absolute;
+ width: 150px;
+ right: 0px;
+}
diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index 61ad66c72..16e95ad82 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -1,34 +1,26 @@
-import { action, configure, reaction, computed } from 'mobx';
+import { action, configure } from 'mobx';
import "normalize.css";
import * as React from 'react';
import * as ReactDOM from 'react-dom';
-import { DocumentDecorations } from './DocumentDecorations';
-import { Documents } from '../documents/Documents';
import { Document } from '../../fields/Document';
import { KeyStore } from '../../fields/KeyStore';
-import "./Main.scss";
-import { ContextMenu } from './ContextMenu';
-import { DocumentView } from './nodes/DocumentView';
-import { Server } from '../Server';
+import { DocumentTransfer, MessageStore } from '../../server/Message';
import { Utils } from '../../Utils';
-import { ServerUtils } from '../../server/ServerUtil';
-import { MessageStore, DocumentTransfer } from '../../server/Message';
+import { Documents } from '../documents/Documents';
+import { Server } from '../Server';
+import { setupDrag } from '../util/DragManager';
import { Transform } from '../util/Transform';
-import { CollectionDockingView } from './collections/CollectionDockingView';
-import { FieldWaiting } from '../../fields/Field';
import { UndoManager } from '../util/UndoManager';
-import { DragManager } from '../util/DragManager';
+import { CollectionDockingView } from './collections/CollectionDockingView';
+import { ContextMenu } from './ContextMenu';
+import { DocumentDecorations } from './DocumentDecorations';
+import { DocumentView } from './nodes/DocumentView';
+import "./Main.scss";
-configure({
- enforceActions: "observed"
-});
-window.addEventListener("drop", function (e) {
- e.preventDefault();
-}, false)
-window.addEventListener("dragover", function (e) {
- e.preventDefault();
-}, false)
+configure({ enforceActions: "observed" }); // causes errors to be generated when modifying an observable outside of an action
+window.addEventListener("drop", (e) => e.preventDefault(), false)
+window.addEventListener("dragover", (e) => e.preventDefault(), false)
document.addEventListener("pointerdown", action(function (e: PointerEvent) {
if (!ContextMenu.Instance.intersects(e.pageX, e.pageY)) {
ContextMenu.Instance.clearItems()
@@ -36,131 +28,74 @@ document.addEventListener("pointerdown", action(function (e: PointerEvent) {
}), true)
-//runInAction(() =>
-// let doc1 = Documents.TextDocument({ title: "hello" });
-// let doc2 = doc1.MakeDelegate();
-// doc2.Set(KS.X, new NumberField(150));
-// doc2.Set(KS.Y, new NumberField(20));
-// let doc3 = Documents.ImageDocument("https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg", {
-// x: 450, y: 100, title: "cat 1"
-// });
-// doc3.Set(KeyStore.Data, new ImageField);
-// const schemaDocs = Array.from(Array(5).keys()).map(v => Documents.ImageDocument("https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg", {
-// x: 50 + 100 * v, y: 50, width: 100, height: 100, title: "cat" + v
-// }));
-// schemaDocs[0].SetData(KS.Author, "Tyler", TextField);
-// schemaDocs[4].SetData(KS.Author, "Bob", TextField);
-// schemaDocs.push(doc2);
-// const doc7 = Documents.SchemaDocument(schemaDocs)
-
const mainDocId = "mainDoc";
-Documents.initProtos(() => {
- Utils.EmitCallback(Server.Socket, MessageStore.GetField, mainDocId, (res: any) => {
- console.log("HELLO WORLD")
- console.log("RESPONSE: " + res)
- let mainContainer: Document;
- let mainfreeform: Document;
- if (res) {
- mainContainer = ServerUtils.FromJson(res) as Document;
- mainContainer.GetAsync(KeyStore.ActiveFrame, field => mainfreeform = field as Document);
- }
- else {
- mainContainer = Documents.DockDocument(JSON.stringify({ content: [{ type: 'row', content: [] }] }), { title: "main container" }, mainDocId);
- Utils.Emit(Server.Socket, MessageStore.AddDocument, new DocumentTransfer(mainContainer.ToJson()))
+let mainContainer: Document;
+let mainfreeform: Document;
+console.log("HELLO WORLD")
+Documents.initProtos(mainDocId, (res?: Document) => {
+ console.log("Response => " + JSON.stringify(res as Document))
+ if (res instanceof Document) {
+ mainContainer = res;
+ mainContainer.GetAsync(KeyStore.ActiveFrame, field => mainfreeform = field as Document);
+ }
+ else {
+ mainContainer = Documents.DockDocument(JSON.stringify({ content: [{ type: 'row', content: [] }] }), { title: "main container" }, mainDocId);
+ Utils.Emit(Server.Socket, MessageStore.AddDocument, new DocumentTransfer(mainContainer.ToJson()))
- setTimeout(() => {
- mainfreeform = Documents.FreeformDocument([], { x: 0, y: 400, title: "mini collection" });
- Utils.Emit(Server.Socket, MessageStore.AddDocument, new DocumentTransfer(mainfreeform.ToJson()));
+ // bcz: strangely, we need a timeout to prevent exceptions/issues initializing GoldenLayout (the rendering engine for Main Container)
+ setTimeout(() => {
+ mainfreeform = Documents.FreeformDocument([], { x: 0, y: 400, title: "mini collection" });
+ Utils.Emit(Server.Socket, MessageStore.AddDocument, new DocumentTransfer(mainfreeform.ToJson()));
- var docs = [mainfreeform].map(doc => CollectionDockingView.makeDocumentConfig(doc));
- mainContainer.SetText(KeyStore.Data, JSON.stringify({ content: [{ type: 'row', content: docs }] }));
- mainContainer.Set(KeyStore.ActiveFrame, mainfreeform);
- }, 0);
- }
+ var dockingLayout = { content: [{ type: 'row', content: [CollectionDockingView.makeDocumentConfig(mainfreeform)] }] };
+ mainContainer.SetText(KeyStore.Data, JSON.stringify(dockingLayout));
+ mainContainer.Set(KeyStore.ActiveFrame, mainfreeform);
+ }, 0);
+ }
- let clearDatabase = action(() => Utils.Emit(Server.Socket, MessageStore.DeleteAll, {}))
- let addTextNode = action(() => Documents.TextDocument({ width: 200, height: 200, title: "a text note" }))
- let addColNode = action(() => Documents.FreeformDocument([], { width: 200, height: 200, title: "a feeform collection" }));
- let addSchemaNode = action(() => Documents.SchemaDocument([Documents.TextDocument()], { width: 200, height: 200, title: "a schema collection" }));
- let addImageNode = action(() => Documents.ImageDocument("https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg", {
- width: 200, height: 200, title: "an image of a cat"
- }));
+ let imgurl = "https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg";
+ let weburl = "https://cs.brown.edu/courses/cs166/";
+ let clearDatabase = action(() => Utils.Emit(Server.Socket, MessageStore.DeleteAll, {}))
+ let addTextNode = action(() => Documents.TextDocument({ width: 200, height: 200, title: "a text note" }))
+ let addColNode = action(() => Documents.FreeformDocument([], { width: 200, height: 200, title: "a feeform collection" }));
+ let addSchemaNode = action(() => Documents.SchemaDocument([Documents.TextDocument()], { width: 200, height: 200, title: "a schema collection" }));
+ let addImageNode = action(() => Documents.ImageDocument(imgurl, { width: 200, height: 200, title: "an image of a cat" }));
+ let addWebNode = action(() => Documents.WebDocument(weburl, { width: 200, height: 200, title: "a sample web page" }));
- let addClick = (creator: any) => action(() => {
- var img = creator();
- img.SetNumber(KeyStore.X, 0);
- img.SetNumber(KeyStore.Y, 0);
- mainfreeform.GetList<Document>(KeyStore.Data, []).push(img);
- });
+ let addClick = (creator: () => Document) => action(() => mainfreeform.GetList<Document>(KeyStore.Data, []).push(creator()));
- let imgRef = React.createRef<HTMLDivElement>();
- let textRef = React.createRef<HTMLDivElement>();
- let schemaRef = React.createRef<HTMLDivElement>();
- let colRef = React.createRef<HTMLDivElement>();
- let curMoveListener: any = null
- let onRowMove = (creator: any, dragRef: any) => action((e: PointerEvent): void => {
- e.stopPropagation();
- e.preventDefault();
+ let imgRef = React.createRef<HTMLDivElement>();
+ let webRef = React.createRef<HTMLDivElement>();
+ let textRef = React.createRef<HTMLDivElement>();
+ let schemaRef = React.createRef<HTMLDivElement>();
+ let colRef = React.createRef<HTMLDivElement>();
- document.removeEventListener("pointermove", curMoveListener);
- document.removeEventListener('pointerup', onRowUp);
- DragManager.StartDrag(dragRef.current!, { document: creator() });
- });
- let onRowUp = action((e: PointerEvent): void => {
- document.removeEventListener("pointermove", curMoveListener);
- document.removeEventListener('pointerup', onRowUp);
- });
- let onRowDown = (creator: any, dragRef: any) => (e: React.PointerEvent) => {
- if (e.shiftKey) {
- CollectionDockingView.Instance.StartOtherDrag(dragRef.current!, creator());
- e.stopPropagation();
- } else {
- document.addEventListener("pointermove", curMoveListener = onRowMove(creator, dragRef));
- document.addEventListener('pointerup', onRowUp);
- }
- }
- ReactDOM.render((
- <div style={{ position: "absolute", width: "100%", height: "100%" }}>
- <DocumentView Document={mainContainer}
- AddDocument={undefined} RemoveDocument={undefined} ScreenToLocalTransform={() => Transform.Identity}
- ContentScaling={() => 1}
- PanelWidth={() => 0}
- PanelHeight={() => 0}
- isTopMost={true}
- ContainingCollectionView={undefined} />
- <DocumentDecorations />
- <ContextMenu />
- <div style={{ position: 'absolute', bottom: '0px', left: '0px', width: '150px' }} ref={imgRef} >
- <button onPointerDown={onRowDown(addImageNode, imgRef)} onClick={addClick(addImageNode)}>Add Image</button></div>
- <div style={{ position: 'absolute', bottom: '25px', left: '0px', width: '150px' }} ref={textRef}>
- <button onPointerDown={onRowDown(addTextNode, textRef)} onClick={addClick(addTextNode)}>Add Text</button></div>
- <div style={{ position: 'absolute', bottom: '50px', left: '0px', width: '150px' }} ref={colRef}>
- <button onPointerDown={onRowDown(addColNode, colRef)} onClick={addClick(addColNode)}>Add Collection</button></div>
- <div style={{ position: 'absolute', bottom: '75px', left: '0px', width: '150px' }} ref={schemaRef}>
- <button onPointerDown={onRowDown(addSchemaNode, schemaRef)} onClick={addClick(addSchemaNode)}>Add Schema</button></div>
- <button style={{ position: 'absolute', bottom: '100px', left: '0px', width: '150px' }} onClick={clearDatabase}>Clear Database</button>
- <button style={{ position: 'absolute', bottom: '25', right: '0px', width: '150px' }} onClick={() => UndoManager.Undo()}>Undo</button>
- <button style={{ position: 'absolute', bottom: '0', right: '0px', width: '150px' }} onClick={() => UndoManager.Redo()}>Redo</button>
- </div>),
- document.getElementById('root'));
- })
-});
-// let doc5 = Documents.ImageDocument("https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg", {
-// x: 650, y: 500, width: 600, height: 600, title: "cat 2"
-// });
-// let docset2 = new Array<Document>(doc4);//, doc1, doc3);
-// let doc6 = Documents.CollectionDocument(docset2, {
-// x: 350, y: 100, width: 600, height: 600, title: "docking collection"
-// });
-// let mainNodes = mainContainer.GetOrCreate(KeyStore.Data, ListField);
-// mainNodes.Data.push(doc6);
-// mainNodes.Data.push(doc2);
-// mainNodes.Data.push(doc4);
-// mainNodes.Data.push(doc3);
-// mainNodes.Data.push(doc5);
-// mainNodes.Data.push(doc1);
-//mainNodes.Data.push(doc2);
-//mainNodes.Data.push(doc6);
-// mainContainer.Set(KeyStore.Data, mainNodes);
-//}
-//);
+ ReactDOM.render((
+ <div style={{ position: "absolute", width: "100%", height: "100%" }}>
+ <DocumentView Document={mainContainer}
+ AddDocument={undefined} RemoveDocument={undefined} ScreenToLocalTransform={() => Transform.Identity}
+ ContentScaling={() => 1}
+ PanelWidth={() => 0}
+ PanelHeight={() => 0}
+ isTopMost={true}
+ SelectOnLoad={false}
+ ContainingCollectionView={undefined} />
+ <DocumentDecorations />
+ <ContextMenu />
+ <div className="main-buttonDiv" style={{ bottom: '0px' }} ref={imgRef} >
+ <button onPointerDown={setupDrag(imgRef, addImageNode)} onClick={addClick(addImageNode)}>Add Image</button></div>
+ <div className="main-buttonDiv" style={{ bottom: '25px' }} ref={webRef} >
+ <button onPointerDown={setupDrag(webRef, addWebNode)} onClick={addClick(addWebNode)}>Add Web</button></div>
+ <div className="main-buttonDiv" style={{ bottom: '50px' }} ref={textRef}>
+ <button onPointerDown={setupDrag(textRef, addTextNode)} onClick={addClick(addTextNode)}>Add Text</button></div>
+ <div className="main-buttonDiv" style={{ bottom: '75px' }} ref={colRef}>
+ <button onPointerDown={setupDrag(colRef, addColNode)} onClick={addClick(addColNode)}>Add Collection</button></div>
+ <div className="main-buttonDiv" style={{ bottom: '100px' }} ref={schemaRef}>
+ <button onPointerDown={setupDrag(schemaRef, addSchemaNode)} onClick={addClick(addSchemaNode)}>Add Schema</button></div>
+ <div className="main-buttonDiv" style={{ bottom: '125px' }} >
+ <button onClick={clearDatabase}>Clear Database</button></div>
+ <button className="main-undoButtons" style={{ bottom: '25px' }} onClick={() => UndoManager.Undo()}>Undo</button>
+ <button className="main-undoButtons" style={{ bottom: '0px' }} onClick={() => UndoManager.Redo()}>Redo</button>
+ </div>),
+ document.getElementById('root'));
+})
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index cb978c1ba..915e33533 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -10,7 +10,6 @@ import { FieldId, Opt, Field } from "../../../fields/Field";
import { KeyStore } from "../../../fields/KeyStore";
import { Utils } from "../../../Utils";
import { Server } from "../../Server";
-import { DragManager } from "../../util/DragManager";
import { undoBatch } from "../../util/UndoManager";
import { DocumentView } from "../nodes/DocumentView";
import "./CollectionDockingView.scss";
@@ -34,10 +33,6 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
}
private _goldenLayout: any = null;
- private _dragDiv: any = null;
- private _dragParent: HTMLElement | null = null;
- private _dragElement: HTMLElement | undefined;
- private _dragFakeElement: HTMLElement | undefined;
private _containerRef = React.createRef<HTMLDivElement>();
private _fullScreen: any = null;
@@ -47,28 +42,8 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
(window as any).React = React;
(window as any).ReactDOM = ReactDOM;
}
-
- public StartOtherDrag(dragElement: HTMLElement, dragDoc: Document) {
- this._dragElement = dragElement;
- this._dragParent = dragElement.parentElement;
- // bcz: we want to copy this document into the header, not move it there.
- // However, GoldenLayout is setup to move things, so we have to do some kludgy stuff:
-
- // - create a temporary invisible div and register that as a DragSource with GoldenLayout
- this._dragDiv = document.createElement("div");
- this._dragDiv.style.opacity = 0;
- DragManager.Root().appendChild(this._dragDiv);
- this._goldenLayout.createDragSource(this._dragDiv, CollectionDockingView.makeDocumentConfig(dragDoc));
-
- // - add our document to that div so that GoldenLayout will get the move events its listening for
- this._dragDiv.appendChild(this._dragElement);
-
- // - add a duplicate of our document to the original document's container
- // (GoldenLayout will be removing our original one)
- this._dragFakeElement = dragElement.cloneNode(true) as HTMLElement;
- this._dragParent!.appendChild(this._dragFakeElement);
-
- // all of this must be undone when the document has been dropped (see tabCreated)
+ public StartOtherDrag(dragDoc: Document, e: any) {
+ this.AddRightSplit(dragDoc, true).contentItems[0].tab._dragListener.onMouseDown({ pageX: e.pageX, pageY: e.pageY, preventDefault: () => { }, button: e.button })
}
@action
@@ -98,7 +73,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) {
+ public AddRightSplit(document: Document, minimize: boolean = false) {
this._goldenLayout.emit('stateChanged');
let newItemStackConfig = {
type: 'stack',
@@ -121,10 +96,15 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
collayout.config["width"] = 50;
newContentItem.config["width"] = 50;
}
+ if (minimize) {
+ newContentItem.config["width"] = 10;
+ newContentItem.config["height"] = 10;
+ }
newContentItem.callDownwards('_$init');
this._goldenLayout.root.callDownwards('setSize', [this._goldenLayout.width, this._goldenLayout.height]);
this._goldenLayout.emit('stateChanged');
this.stateChanged();
+ return newContentItem;
}
setupGoldenLayout() {
@@ -218,13 +198,6 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
this.stateChanged();
}
tabCreated = (tab: any) => {
- if (this._dragDiv) {
- this._dragDiv.removeChild(this._dragElement);
- this._dragParent!.removeChild(this._dragFakeElement!);
- this._dragParent!.appendChild(this._dragElement!);
- DragManager.Root().removeChild(this._dragDiv);
- this._dragDiv = null;
- }
tab.closeElement.off('click') //unbind the current click handler
.click(function () {
tab.contentItem.remove();
@@ -295,6 +268,7 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
PanelHeight={this._nativeHeight}
ScreenToLocalTransform={this.ScreenToLocalTransform}
isTopMost={true}
+ SelectOnLoad={false}
ContainingCollectionView={undefined} />
</div>
diff --git a/src/client/views/collections/CollectionFreeFormView.scss b/src/client/views/collections/CollectionFreeFormView.scss
index 737f28318..f432e8cc3 100644
--- a/src/client/views/collections/CollectionFreeFormView.scss
+++ b/src/client/views/collections/CollectionFreeFormView.scss
@@ -1,15 +1,5 @@
.collectionfreeformview-container {
- ::-webkit-scrollbar {
- -webkit-appearance: none;
- height:5px;
- width:5px;
- }
- ::-webkit-scrollbar-thumb {
- border-radius: 2px;
- background-color: rgba(0,0,0,.5);
- }
-
.collectionfreeformview > .jsx-parser{
position:absolute;
height: 100%;
@@ -30,4 +20,22 @@
width:100%;
height: 100%
}
+}
+
+.border {
+ border-style: solid;
+ box-sizing: border-box;
+ width: 100%;
+ height: 100%;
+}
+
+//this is an animation for the blinking cursor!
+@keyframes blink {
+ 0% {opacity: 0}
+ 49%{opacity: 0}
+ 50% {opacity: 1}
+}
+
+#prevCursor {
+ animation: blink 1s infinite;
} \ No newline at end of file
diff --git a/src/client/views/collections/CollectionFreeFormView.tsx b/src/client/views/collections/CollectionFreeFormView.tsx
index 1f8d76432..63255dd90 100644
--- a/src/client/views/collections/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/CollectionFreeFormView.tsx
@@ -1,4 +1,4 @@
-import { action, computed } from "mobx";
+import { observable, action, computed } from "mobx";
import { observer } from "mobx-react";
import { Document } from "../../../fields/Document";
import { FieldWaiting } from "../../../fields/Field";
@@ -10,16 +10,16 @@ import { Transform } from "../../util/Transform";
import { undoBatch } from "../../util/UndoManager";
import { CollectionDockingView } from "../collections/CollectionDockingView";
import { CollectionSchemaView } from "../collections/CollectionSchemaView";
-import { CollectionTreeView } from "../collections/CollectionTreeView";
import { CollectionView } from "../collections/CollectionView";
import { CollectionFreeFormDocumentView } from "../nodes/CollectionFreeFormDocumentView";
import { DocumentView } from "../nodes/DocumentView";
-import { WebView } from "../nodes/WebView";
import { FormattedTextBox } from "../nodes/FormattedTextBox";
import { ImageBox } from "../nodes/ImageBox";
+import { WebBox } from "../nodes/WebBox";
import "./CollectionFreeFormView.scss";
import { COLLECTION_BORDER_WIDTH } from "./CollectionView";
import { CollectionViewBase } from "./CollectionViewBase";
+import { Documents } from "../../documents/Documents";
import React = require("react");
const JsxParser = require('react-jsx-parser').default;//TODO Why does this need to be imported like this?
@@ -28,9 +28,17 @@ export class CollectionFreeFormView extends CollectionViewBase {
private _canvasRef = React.createRef<HTMLDivElement>();
private _lastX: number = 0;
private _lastY: number = 0;
+ private _selectOnLoaded: string = ""; // id of document that should be selected once it's loaded (used for click-to-type)
+
+ @observable
private _downX: number = 0;
+ @observable
private _downY: number = 0;
+ //determines whether the blinking cursor for indicating whether a text will be made on key down is visible
+ @observable
+ private _previewCursorVisible: boolean = false;
+
@computed get panX(): number { return this.props.Document.GetNumber(KeyStore.PanX, 0) }
@computed get panY(): number { return this.props.Document.GetNumber(KeyStore.PanY, 0) }
@computed get scale(): number { return this.props.Document.GetNumber(KeyStore.Scale, 1); }
@@ -63,8 +71,10 @@ export class CollectionFreeFormView extends CollectionViewBase {
document.addEventListener("pointermove", this.onPointerMove);
document.removeEventListener("pointerup", this.onPointerUp);
document.addEventListener("pointerup", this.onPointerUp);
- this._downX = this._lastX = e.pageX;
- this._downY = this._lastY = e.pageY;
+ this._lastX = e.pageX;
+ this._lastY = e.pageY;
+ this._downX = e.pageX;
+ this._downY = e.pageY;
}
}
@@ -74,10 +84,14 @@ export class CollectionFreeFormView extends CollectionViewBase {
document.removeEventListener("pointerup", this.onPointerUp);
e.stopPropagation();
if (Math.abs(this._downX - e.clientX) < 3 && Math.abs(this._downY - e.clientY) < 3) {
+ //show preview text cursor on tap
+ this._previewCursorVisible = true;
+ //select is not already selected
if (!this.props.isSelected()) {
this.props.select(false);
}
}
+
}
@action
@@ -87,7 +101,7 @@ export class CollectionFreeFormView extends CollectionViewBase {
let x = this.props.Document.GetNumber(KeyStore.PanX, 0);
let y = this.props.Document.GetNumber(KeyStore.PanY, 0);
let [dx, dy] = this.props.ScreenToLocalTransform().transformDirection(e.clientX - this._lastX, e.clientY - this._lastY);
-
+ this._previewCursorVisible = false;
this.SetPan(x + dx, y + dy);
}
this._lastX = e.pageX;
@@ -145,6 +159,24 @@ export class CollectionFreeFormView extends CollectionViewBase {
}
@action
+ onKeyDown = (e: React.KeyboardEvent<Element>) => {
+ //if not these keys, make a textbox if preview cursor is active!
+ if (!e.ctrlKey && !e.altKey && !e.shiftKey) {
+ if (this._previewCursorVisible) {
+ //make textbox and add it to this collection
+ let [x, y] = this.getTransform().transformPoint(this._downX, this._downY); (this._downX, this._downY);
+ let newBox = Documents.TextDocument({ width: 200, height: 100, x: x, y: y, title: "new" });
+ // mark this collection so that when the text box is created we can send it the SelectOnLoad prop to focus itself
+ this._selectOnLoaded = newBox.Id;
+ //set text to be the typed key and get focus on text box
+ this.props.CollectionView.addDocument(newBox);
+ //remove cursor from screen
+ this._previewCursorVisible = false;
+ }
+ }
+ }
+
+ @action
bringToFront(doc: Document) {
const { fieldKey: fieldKey, Document: Document } = this.props;
@@ -177,15 +209,15 @@ export class CollectionFreeFormView extends CollectionViewBase {
}
@computed
get views() {
- const { fieldKey, Document } = this.props;
- const lvalue = Document.GetT<ListField<Document>>(fieldKey, ListField);
+ const lvalue = this.props.Document.GetT<ListField<Document>>(this.props.fieldKey, ListField);
if (lvalue && lvalue != FieldWaiting) {
return lvalue.Data.map(doc => {
- return (<CollectionFreeFormDocumentView key={doc.Id} Document={doc}
+ return (<CollectionFreeFormDocumentView key={doc.Id} Document={doc} ref={focus}
AddDocument={this.props.addDocument}
RemoveDocument={this.props.removeDocument}
ScreenToLocalTransform={this.getTransform}
isTopMost={false}
+ SelectOnLoad={doc.Id === this._selectOnLoaded}
ContentScaling={this.noScaling}
PanelWidth={doc.Width}
PanelHeight={doc.Height}
@@ -199,7 +231,7 @@ export class CollectionFreeFormView extends CollectionViewBase {
get backgroundView() {
return !this.backgroundLayout ? (null) :
(<JsxParser
- components={{ FormattedTextBox, ImageBox, CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, WebView }}
+ components={{ FormattedTextBox, ImageBox, CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, WebBox }}
bindings={this.props.bindings}
jsx={this.backgroundLayout}
showWarnings={true}
@@ -210,7 +242,7 @@ export class CollectionFreeFormView extends CollectionViewBase {
get overlayView() {
return !this.overlayLayout ? (null) :
(<JsxParser
- components={{ FormattedTextBox, ImageBox, CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView }}
+ components={{ FormattedTextBox, ImageBox, CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, WebBox }}
bindings={this.props.bindings}
jsx={this.overlayLayout}
showWarnings={true}
@@ -222,21 +254,41 @@ export class CollectionFreeFormView extends CollectionViewBase {
getLocalTransform = (): Transform => Transform.Identity.translate(-this.panX, -this.panY).scale(1 / this.scale);
noScaling = () => 1;
+ //when focus is lost, this will remove the preview cursor
+ @action
+ onBlur = (e: React.FocusEvent<HTMLInputElement>): void => {
+ this._previewCursorVisible = false;
+ }
+
render() {
+
+ //determines whether preview text cursor should be visible (ie when user taps this collection it should)
+ let cursor = null;
+ if (this._previewCursorVisible) {
+ //get local position and place cursor there!
+ let [x, y] = this.getTransform().transformPoint(this._downX, this._downY);
+ cursor = <div id="prevCursor" onKeyPress={this.onKeyDown} style={{ color: "black", position: "absolute", transformOrigin: "left top", transform: `translate(${x}px, ${y}px)` }}>I</div>
+ }
+
const panx: number = this.props.Document.GetNumber(KeyStore.PanX, 0) + this.centeringShiftX;
const pany: number = this.props.Document.GetNumber(KeyStore.PanY, 0) + this.centeringShiftY;
+
return (
<div className="collectionfreeformview-container"
onPointerDown={this.onPointerDown}
+ onKeyPress={this.onKeyDown}
onWheel={this.onPointerWheel}
onDrop={this.onDrop.bind(this)}
onDragOver={this.onDragOver}
+ onBlur={this.onBlur}
style={{ borderWidth: `${COLLECTION_BORDER_WIDTH}px`, }}
+ tabIndex={0}
ref={this.createDropTarget}>
<div className="collectionfreeformview"
style={{ transformOrigin: "left top", transform: ` translate(${panx}px, ${pany}px) scale(${this.zoomScaling}, ${this.zoomScaling})` }}
ref={this._canvasRef}>
{this.backgroundView}
+ {cursor}
{this.views}
</div>
{this.overlayView}
diff --git a/src/client/views/collections/CollectionSchemaView.scss b/src/client/views/collections/CollectionSchemaView.scss
index 995d60f74..d40e6d314 100644
--- a/src/client/views/collections/CollectionSchemaView.scss
+++ b/src/client/views/collections/CollectionSchemaView.scss
@@ -6,15 +6,6 @@
position: absolute;
width: 100%;
height: 100%;
- ::-webkit-scrollbar {
- -webkit-appearance: none;
- height:5px;
- width:5px;
- }
- ::-webkit-scrollbar-thumb {
- border-radius: 2px;
- background-color: rgba(0,0,0,.5);
- }
.collectionSchemaView-previewRegion {
position: relative;
background: black;
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 106a5c4f5..5bcd501cc 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -39,15 +39,16 @@ export class CollectionSchemaView extends CollectionViewBase {
isSelected: () => false,
select: () => { },
isTopMost: false,
- bindings: {}
+ bindings: {},
+ selectOnLoad: false,
}
let contents = (
<FieldView {...props} />
)
let reference = React.createRef<HTMLDivElement>();
- let onItemDown = setupDrag(reference, props.doc);
+ let onItemDown = setupDrag(reference, () => props.doc);
return (
- <div onPointerDown={onItemDown} ref={reference}>
+ <div onPointerDown={onItemDown} key={props.doc.Id} ref={reference}>
<EditableView contents={contents}
height={36} GetValue={() => {
let field = props.doc.Get(props.fieldKey);
@@ -185,6 +186,7 @@ export class CollectionSchemaView extends CollectionViewBase {
<DocumentView Document={selected}
AddDocument={this.props.addDocument} RemoveDocument={this.props.removeDocument}
isTopMost={false}
+ SelectOnLoad={false}
ScreenToLocalTransform={this.getTransform}
ContentScaling={this.getContentScaling}
PanelWidth={this.getPanelWidth}
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 64f4c0970..55c804337 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -33,7 +33,7 @@ class TreeView extends React.Component<TreeViewProps> {
var children = childDocument.GetT<ListField<Document>>(KeyStore.Data, ListField);
let title = childDocument.GetT<TextField>(KeyStore.Title, TextField);
- let onItemDown = setupDrag(reference, childDocument);
+ let onItemDown = setupDrag(reference, () => childDocument);
if (title && title != FieldWaiting) {
let subView = !children || this.collapsed || children === FieldWaiting ? (null) :
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 03e1f1fa4..8332249c9 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -1,4 +1,4 @@
-import { action, computed } from "mobx";
+import { action, computed, observable } from "mobx";
import { observer } from "mobx-react";
import { Document } from "../../../fields/Document";
import { ListField } from "../../../fields/ListField";
@@ -30,7 +30,7 @@ export class CollectionView extends React.Component<CollectionViewProps> {
public static LayoutString(fieldKey: string = "DataKey") {
return `<CollectionView Document={Document}
ScreenToLocalTransform={ScreenToLocalTransform} fieldKey={${fieldKey}} panelWidth={PanelWidth} panelHeight={PanelHeight} isSelected={isSelected} select={select} bindings={bindings}
- isTopMost={isTopMost} BackgroundView={BackgroundView} />`;
+ isTopMost={isTopMost} SelectOnLoad={selectOnLoad} BackgroundView={BackgroundView} />`;
}
public active = () => {
var isSelected = this.props.isSelected();
@@ -87,13 +87,15 @@ export class CollectionView extends React.Component<CollectionViewProps> {
Document.SetData(KeyStore.ViewType, type, NumberField);
}
+
render() {
let viewType = this.collectionViewType;
+
switch (viewType) {
case CollectionViewType.Freeform:
return (<CollectionFreeFormView {...this.props}
addDocument={this.addDocument} removeDocument={this.removeDocument} active={this.active}
- CollectionView={this} />)
+ CollectionView={this} />);
case CollectionViewType.Schema:
return (<CollectionSchemaView {...this.props}
addDocument={this.addDocument} removeDocument={this.removeDocument} active={this.active}
diff --git a/src/client/views/collections/CollectionViewBase.tsx b/src/client/views/collections/CollectionViewBase.tsx
index 217536e2b..7067724c8 100644
--- a/src/client/views/collections/CollectionViewBase.tsx
+++ b/src/client/views/collections/CollectionViewBase.tsx
@@ -67,7 +67,7 @@ export class CollectionViewBase extends React.Component<SubCollectionViewProps>
let html = e.dataTransfer.getData("text/html");
let text = e.dataTransfer.getData("text/plain");
if (html && html.indexOf("<img") != 0) {
- let htmlDoc = Documents.HtmlDocument(html, { ...options });
+ let htmlDoc = Documents.HtmlDocument(html, { ...options, width: 300, height: 300 });
htmlDoc.SetText(KeyStore.DocumentText, text);
this.props.addDocument(htmlDoc);
return;
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 6e14df229..d5b4a723d 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -12,10 +12,10 @@ import { CollectionDockingView } from "../collections/CollectionDockingView";
import { CollectionFreeFormView } from "../collections/CollectionFreeFormView";
import { CollectionSchemaView } from "../collections/CollectionSchemaView";
import { CollectionView, CollectionViewType } from "../collections/CollectionView";
-import { WebView } from "./WebView";
import { ContextMenu } from "../ContextMenu";
import { FormattedTextBox } from "../nodes/FormattedTextBox";
import { ImageBox } from "../nodes/ImageBox";
+import { WebBox } from "../nodes/WebBox";
import "./DocumentView.scss";
import React = require("react");
const JsxParser = require('react-jsx-parser').default;//TODO Why does this need to be imported like this?
@@ -32,6 +32,7 @@ export interface DocumentViewProps {
ContentScaling: () => number;
PanelWidth: () => number;
PanelHeight: () => number;
+ SelectOnLoad: boolean;
}
export interface JsxArgs extends DocumentViewProps {
Keys: { [name: string]: Key }
@@ -95,7 +96,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
this._downX = e.clientX;
this._downY = e.clientY;
if (e.shiftKey && e.buttons === 1) {
- CollectionDockingView.Instance.StartOtherDrag(this._mainCont.current!, this.props.Document);
+ CollectionDockingView.Instance.StartOtherDrag(this.props.Document, e);
e.stopPropagation();
} else {
if (this.active && !e.isDefaultPrevented()) {
@@ -116,6 +117,8 @@ export class DocumentView extends React.Component<DocumentViewProps> {
return;
}
if (Math.abs(this._downX - e.clientX) > 3 || Math.abs(this._downY - e.clientY) > 3) {
+ document.removeEventListener("pointermove", this.onPointerMove)
+ document.removeEventListener("pointerup", this.onPointerUp)
if (this._mainCont.current != null && !this.topMost) {
const [left, top] = this.props.ScreenToLocalTransform().inverse().transformPoint(0, 0);
let dragData: { [id: string]: any } = {};
@@ -190,13 +193,14 @@ export class DocumentView extends React.Component<DocumentViewProps> {
@computed get mainContent() {
return <JsxParser
- components={{ FormattedTextBox, ImageBox, CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, WebView }}
+ components={{ FormattedTextBox, ImageBox, CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, WebBox }}
bindings={this._documentBindings}
jsx={this.layout}
showWarnings={true}
onError={(test: any) => { console.log(test) }}
/>
}
+
render() {
if (!this.props.Document)
return <div></div>
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index b71309bf5..f372258f8 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -7,11 +7,11 @@ import { TextField } from "../../../fields/TextField";
import { NumberField } from "../../../fields/NumberField";
import { RichTextField } from "../../../fields/RichTextField";
import { ImageField } from "../../../fields/ImageField";
+import { WebField } from "../../../fields/WebField";
import { Key } from "../../../fields/Key";
import { FormattedTextBox } from "./FormattedTextBox";
import { ImageBox } from "./ImageBox";
-import { HtmlField } from "../../../fields/HtmlField";
-import { WebView } from "./WebView";
+import { WebBox } from "./WebBox";
//
// these properties get assigned through the render() method of the DocumentView when it creates this node.
@@ -24,12 +24,15 @@ export interface FieldViewProps {
isSelected: () => boolean;
select: () => void;
isTopMost: boolean;
+ selectOnLoad: boolean;
bindings: any;
}
@observer
export class FieldView extends React.Component<FieldViewProps> {
- public static LayoutString(fieldType: { name: string }, fieldStr: string = "DataKey") { return `<${fieldType.name} doc={Document} DocumentViewForField={DocumentView} bindings={bindings} fieldKey={${fieldStr}} isSelected={isSelected} select={select} isTopMost={isTopMost} />`; }
+ public static LayoutString(fieldType: { name: string }, fieldStr: string = "DataKey") {
+ return `<${fieldType.name} doc={Document} DocumentViewForField={DocumentView} bindings={bindings} fieldKey={${fieldStr}} isSelected={isSelected} select={select} selectOnLoad={SelectOnLoad} isTopMost={isTopMost} />`;
+ }
@computed
get field(): FieldValue<Field> {
@@ -50,12 +53,16 @@ export class FieldView extends React.Component<FieldViewProps> {
else if (field instanceof ImageField) {
return <ImageBox {...this.props} />
}
+ else if (field instanceof WebField) {
+ return <WebBox {...this.props} />
+ }
+ // 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 (field instanceof HtmlField) {
- return <WebView {...this.props} />
- }
else if (field != FieldWaiting) {
return <p>{JSON.stringify(field.GetValue())}</p>
}
diff --git a/src/client/views/nodes/FormattedTextBox.scss b/src/client/views/nodes/FormattedTextBox.scss
index f06fdae24..21bd43b6e 100644
--- a/src/client/views/nodes/FormattedTextBox.scss
+++ b/src/client/views/nodes/FormattedTextBox.scss
@@ -9,7 +9,7 @@
}
.formattedTextBox-cont {
- background: rgb(218, 215, 215);
+ background: white;
padding: 1;
border: black;
border-width: 10;
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index a58e1955f..e65615af4 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -3,15 +3,17 @@ import { baseKeymap } from "prosemirror-commands";
import { history, redo, undo } from "prosemirror-history";
import { keymap } from "prosemirror-keymap";
import { schema } from "prosemirror-schema-basic";
-import { EditorState, Transaction } from "prosemirror-state";
+import { EditorState, Transaction, } from "prosemirror-state";
import { EditorView } from "prosemirror-view";
-import { Opt, FieldWaiting, FieldValue } from "../../../fields/Field";
+import { Opt, FieldWaiting } from "../../../fields/Field";
import "./FormattedTextBox.scss";
import React = require("react")
import { RichTextField } from "../../../fields/RichTextField";
import { FieldViewProps, FieldView } from "./FieldView";
+
+
// FormattedTextBox: Displays an editable plain text node that maps to a specified Key of a Document
//
// HTML Markup: <FormattedTextBox Doc={Document's ID} FieldKey={Key's name + "Key"}
@@ -39,7 +41,6 @@ export class FormattedTextBox extends React.Component<FieldViewProps> {
super(props);
this._ref = React.createRef();
-
this.onChange = this.onChange.bind(this);
}
@@ -47,25 +48,23 @@ export class FormattedTextBox extends React.Component<FieldViewProps> {
if (this._editorView) {
const state = this._editorView.state.apply(tx);
this._editorView.updateState(state);
- const { doc, fieldKey } = this.props;
- doc.SetData(fieldKey, JSON.stringify(state.toJSON()), RichTextField);
+ this.props.doc.SetData(this.props.fieldKey, JSON.stringify(state.toJSON()), RichTextField);
}
}
componentDidMount() {
let state: EditorState;
- const { doc, fieldKey } = this.props;
const config = {
schema,
plugins: [
history(),
keymap({ "Mod-z": undo, "Mod-y": redo }),
- keymap(baseKeymap)
+ keymap(baseKeymap),
]
};
- let field = doc.GetT(fieldKey, RichTextField);
- if (field && field != FieldWaiting) { // bcz: don't think this works
+ let field = this.props.doc.GetT(this.props.fieldKey, RichTextField);
+ if (field && field != FieldWaiting) {
state = EditorState.fromJSON(config, JSON.parse(field.Data));
} else {
state = EditorState.create(config);
@@ -85,6 +84,10 @@ export class FormattedTextBox extends React.Component<FieldViewProps> {
this._editorView.updateState(EditorState.fromJSON(config, JSON.parse(field)));
}
})
+ if (this.props.selectOnLoad) {
+ this.props.select();
+ this._editorView!.focus();
+ }
}
componentWillUnmount() {
@@ -102,11 +105,9 @@ export class FormattedTextBox extends React.Component<FieldViewProps> {
@action
onChange(e: React.ChangeEvent<HTMLInputElement>) {
- const { fieldKey, doc } = this.props;
- doc.SetData(fieldKey, e.target.value, RichTextField);
+ this.props.doc.SetData(this.props.fieldKey, e.target.value, RichTextField);
}
onPointerDown = (e: React.PointerEvent): void => {
- let me = this;
if (e.buttons === 1 && this.props.isSelected()) {
e.stopPropagation();
}
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 4fe73fb8d..e206bf8d5 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -9,7 +9,6 @@ import { FieldWaiting } from '../../../fields/Field';
import { observer } from "mobx-react"
import { observable, action } from 'mobx';
import { KeyStore } from '../../../fields/KeyStore';
-import { element } from 'prop-types';
@observer
export class ImageBox extends React.Component<FieldViewProps> {
diff --git a/src/client/views/nodes/WebBox.scss b/src/client/views/nodes/WebBox.scss
new file mode 100644
index 000000000..e72b3c4da
--- /dev/null
+++ b/src/client/views/nodes/WebBox.scss
@@ -0,0 +1,14 @@
+
+.webBox-cont {
+ padding: 0vw;
+ position: absolute;
+ width: 100%;
+ height: 100%;
+}
+
+.webBox-button {
+ padding : 0vw;
+ border: none;
+ width : 100%;
+ height: 100%;
+} \ No newline at end of file
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
new file mode 100644
index 000000000..2ca8d49ce
--- /dev/null
+++ b/src/client/views/nodes/WebBox.tsx
@@ -0,0 +1,38 @@
+import "./WebBox.scss";
+import React = require("react")
+import { WebField } from '../../../fields/WebField';
+import { FieldViewProps, FieldView } from './FieldView';
+import { FieldWaiting } from '../../../fields/Field';
+import { observer } from "mobx-react"
+import { computed } from 'mobx';
+import { KeyStore } from '../../../fields/KeyStore';
+
+@observer
+export class WebBox extends React.Component<FieldViewProps> {
+
+ public static LayoutString() { return FieldView.LayoutString(WebBox); }
+
+ constructor(props: FieldViewProps) {
+ super(props);
+ }
+
+ @computed get html(): string { return this.props.doc.GetHtml(KeyStore.Data, ""); }
+
+ render() {
+ let field = this.props.doc.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 content = this.html ?
+ <span dangerouslySetInnerHTML={{ __html: this.html }}></span> :
+ <div style={{ width: "100%", height: "100%", position: "absolute" }}>
+ <iframe src={path} style={{ position: "absolute", width: "100%", height: "100%" }}></iframe>
+ {this.props.isSelected() ? (null) : <div style={{ width: "100%", height: "100%", position: "absolute" }} />}
+ </div>;
+
+ return (
+ <div className="webBox-cont" >
+ {content}
+ </div>)
+ }
+} \ No newline at end of file
diff --git a/src/client/views/nodes/WebView.tsx b/src/client/views/nodes/WebView.tsx
deleted file mode 100644
index 717aa8bf5..000000000
--- a/src/client/views/nodes/WebView.tsx
+++ /dev/null
@@ -1,22 +0,0 @@
-import { FieldViewProps, FieldView } from "./FieldView";
-import { computed } from "mobx";
-import { observer } from "mobx-react";
-import { KeyStore } from "../../../fields/KeyStore";
-import React = require('react')
-import { TextField } from "../../../fields/TextField";
-import { HtmlField } from "../../../fields/HtmlField";
-import { RichTextField } from "../../../fields/RichTextField";
-
-@observer
-export class WebView extends React.Component<FieldViewProps> {
- public static LayoutString(fieldStr: string = "DataKey") { return FieldView.LayoutString(WebView, fieldStr) }
-
- @computed
- get html(): string {
- return this.props.doc.GetData(KeyStore.Data, HtmlField, "" as string);
- }
-
- render() {
- return <span dangerouslySetInnerHTML={{ __html: this.html }}></span>
- }
-} \ No newline at end of file
diff --git a/src/fields/Document.ts b/src/fields/Document.ts
index 6193ea56c..5b91de6ed 100644
--- a/src/fields/Document.ts
+++ b/src/fields/Document.ts
@@ -8,6 +8,7 @@ import { ListField } from "./ListField";
import { Server } from "../client/Server";
import { Types } from "../server/Message";
import { UndoManager } from "../client/util/UndoManager";
+import { HtmlField } from "./HtmlField";
export class Document extends Field {
public fields: ObservableMap<string, { key: Key, field: Field }> = new ObservableMap();
@@ -125,6 +126,10 @@ export class Document extends Field {
return vval;
}
+ GetHtml(key: Key, defaultVal: string): string {
+ return this.GetData(key, HtmlField, defaultVal);
+ }
+
GetNumber(key: Key, defaultVal: number): number {
return this.GetData(key, NumberField, defaultVal);
}
diff --git a/src/fields/Key.ts b/src/fields/Key.ts
index c16a00878..00d78d516 100644
--- a/src/fields/Key.ts
+++ b/src/fields/Key.ts
@@ -2,7 +2,6 @@ import { Field, FieldId } from "./Field"
import { Utils } from "../Utils";
import { observable } from "mobx";
import { Types } from "../server/Message";
-import { ObjectID } from "bson";
import { Server } from "../client/Server";
export class Key extends Field {
diff --git a/src/fields/WebField.ts b/src/fields/WebField.ts
new file mode 100644
index 000000000..8f945d686
--- /dev/null
+++ b/src/fields/WebField.ts
@@ -0,0 +1,30 @@
+import { BasicField } from "./BasicField";
+import { Field, FieldId } from "./Field";
+import { Types } from "../server/Message";
+
+export class WebField extends BasicField<URL> {
+ constructor(data: URL | undefined = undefined, id?: FieldId, save: boolean = true) {
+ super(data == undefined ? new URL("https://crossorigin.me/" + "https://cs.brown.edu/") : data, save, id);
+ }
+
+ toString(): string {
+ return this.Data.href;
+ }
+
+ ToScriptString(): string {
+ return `new WebField("${this.Data}")`;
+ }
+
+ Copy(): Field {
+ return new WebField(this.Data);
+ }
+
+ ToJson(): { type: Types, data: URL, _id: string } {
+ return {
+ type: Types.Web,
+ data: this.Data,
+ _id: this.Id
+ }
+ }
+
+} \ No newline at end of file
diff --git a/src/server/Message.ts b/src/server/Message.ts
index 80fc9a80d..148e6e723 100644
--- a/src/server/Message.ts
+++ b/src/server/Message.ts
@@ -45,7 +45,7 @@ export class GetFieldArgs {
}
export enum Types {
- Number, List, Key, Image, Document, Text, RichText, DocumentReference, Html
+ Number, List, Key, Image, Web, Document, Text, RichText, DocumentReference, Html
}
export class DocumentTransfer implements Transferable {
diff --git a/src/server/ServerUtil.ts b/src/server/ServerUtil.ts
index 08e72fdae..a53fb5d2b 100644
--- a/src/server/ServerUtil.ts
+++ b/src/server/ServerUtil.ts
@@ -10,6 +10,7 @@ import { Server } from './../client/Server';
import { Types } from './Message';
import { Utils } from '../Utils';
import { HtmlField } from '../fields/HtmlField';
+import { WebField } from '../fields/WebField';
export class ServerUtils {
public static FromJson(json: any): Field {
@@ -30,6 +31,8 @@ export class ServerUtils {
return new TextField(data, id, false)
case Types.Html:
return new HtmlField(data, id, false)
+ case Types.Web:
+ return new WebField(new URL(data), id, false)
case Types.RichText:
return new RichTextField(data, id, false)
case Types.Key: