aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/.DS_Storebin6148 -> 6148 bytes
-rw-r--r--src/client/util/DocumentManager.ts51
-rw-r--r--src/client/util/DragManager.ts1
-rw-r--r--src/client/views/.DS_Storebin0 -> 6148 bytes
-rw-r--r--src/client/views/DocumentDecorations.scss11
-rw-r--r--src/client/views/DocumentDecorations.tsx73
-rw-r--r--src/client/views/collections/CollectionFreeFormView.tsx3
-rw-r--r--src/client/views/nodes/DocumentView.tsx66
-rw-r--r--src/client/views/nodes/LinkBox.scss39
-rw-r--r--src/client/views/nodes/LinkBox.tsx83
-rw-r--r--src/client/views/nodes/LinkEditor.scss16
-rw-r--r--src/client/views/nodes/LinkEditor.tsx37
-rw-r--r--src/client/views/nodes/LinkMenu.scss20
-rw-r--r--src/client/views/nodes/LinkMenu.tsx58
-rw-r--r--src/fields/Document.ts8
-rw-r--r--src/fields/KeyStore.ts4
16 files changed, 464 insertions, 6 deletions
diff --git a/src/.DS_Store b/src/.DS_Store
index 620e4ebce..f20f36d63 100644
--- a/src/.DS_Store
+++ b/src/.DS_Store
Binary files differ
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
new file mode 100644
index 000000000..5b99b4ef8
--- /dev/null
+++ b/src/client/util/DocumentManager.ts
@@ -0,0 +1,51 @@
+import React = require('react')
+import { observer } from 'mobx-react';
+import { observable, action } from 'mobx';
+import { Document } from "../../fields/Document"
+import { DocumentView } from '../views/nodes/DocumentView';
+
+
+export class DocumentManager {
+
+ //global holds all of the nodes (regardless of which collection they're in)
+ @observable
+ public DocumentViews: DocumentView[] = [];
+
+ // singleton instance
+ private static _instance: DocumentManager;
+
+ // create one and only one instance of NodeManager
+ public static get Instance(): DocumentManager {
+ return this._instance || (this._instance = new this());
+ }
+
+ //private constructor so no other class can create a nodemanager
+ private constructor() {
+ // this.DocumentViews = new Array<DocumentView>();
+ }
+
+ public getDocumentView(toFind: Document): DocumentView | null {
+
+ let toReturn: DocumentView | null;
+ toReturn = null;
+
+ //gets document view that is in a freeform canvas collection
+ DocumentManager.Instance.DocumentViews.map(view => {
+ let doc = view.props.Document;
+ // if (view.props.ContainingCollectionView instanceof CollectionFreeFormView) {
+ // if (Object.is(doc, toFind)) {
+ // toReturn = view;
+ // return;
+ // }
+ // }
+
+ if (Object.is(doc, toFind)) {
+ toReturn = view;
+ return;
+ }
+
+ })
+
+ return (toReturn);
+ }
+} \ No newline at end of file
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 513a6ac9e..bb9a3b793 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -97,7 +97,6 @@ export namespace DragManager {
}
export function StartDrag(ele: HTMLElement, dragData: { [id: string]: any }, options?: DragOptions) {
- DocumentDecorations.Instance.Hidden = true;
if (!dragDiv) {
dragDiv = document.createElement("div");
DragManager.Root().appendChild(dragDiv);
diff --git a/src/client/views/.DS_Store b/src/client/views/.DS_Store
new file mode 100644
index 000000000..6bd614c8b
--- /dev/null
+++ b/src/client/views/.DS_Store
Binary files differ
diff --git a/src/client/views/DocumentDecorations.scss b/src/client/views/DocumentDecorations.scss
index e8b93a18b..f88bf9c14 100644
--- a/src/client/views/DocumentDecorations.scss
+++ b/src/client/views/DocumentDecorations.scss
@@ -2,7 +2,7 @@
position: absolute;
display: grid;
z-index: 1000;
- grid-template-rows: 20px 1fr 20px;
+ grid-template-rows: 20px 1fr 20px 0px;
grid-template-columns: 20px 1fr 20px;
pointer-events: none;
#documentDecorations-centerCont {
@@ -29,4 +29,13 @@
#documentDecorations-rightResizer {
cursor: ew-resize;
}
+ #linkButton {
+ height: 20px;
+ width: 20px;
+ margin-top: 10px;
+ border-radius: 50%;
+ opacity: 0.6;
+ pointer-events: auto;
+ background-color: #2B6091;
+ }
} \ No newline at end of file
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 9fd73a33b..8c3913232 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -1,16 +1,23 @@
-import { observable, computed } from "mobx";
+import { observable, computed, action } from "mobx";
import React = require("react");
import { SelectionManager } from "../util/SelectionManager";
import { observer } from "mobx-react";
import './DocumentDecorations.scss'
import { KeyStore } from '../../fields/KeyStore'
import { NumberField } from "../../fields/NumberField";
+import { props } from "bluebird";
+import { DragManager } from "../util/DragManager";
+import { LinkMenu } from "./nodes/LinkMenu";
+const higflyout = require("@hig/flyout");
+const { anchorPoints } = higflyout;
+const Flyout = higflyout.default;
@observer
export class DocumentDecorations extends React.Component {
static Instance: DocumentDecorations
private _resizer = ""
private _isPointerDown = false;
+ private _linkButton = React.createRef<HTMLDivElement>();
@observable private _hidden = false;
constructor(props: Readonly<{}>) {
@@ -52,6 +59,46 @@ export class DocumentDecorations extends React.Component {
}
}
+ onLinkButtonDown = (e: React.PointerEvent): void => {
+ // if ()
+ // let linkMenu = new LinkMenu(SelectionManager.SelectedDocuments()[0]);
+ // linkMenu.Hidden = false;
+ console.log("down");
+
+ e.stopPropagation();
+ document.removeEventListener("pointermove", this.onLinkButtonMoved)
+ document.addEventListener("pointermove", this.onLinkButtonMoved);
+ document.removeEventListener("pointerup", this.onLinkButtonUp)
+ document.addEventListener("pointerup", this.onLinkButtonUp);
+
+ }
+
+ onLinkButtonUp = (e: PointerEvent): void => {
+ console.log("up");
+ document.removeEventListener("pointermove", this.onLinkButtonMoved)
+ document.removeEventListener("pointerup", this.onLinkButtonUp)
+ e.stopPropagation();
+ }
+
+
+ onLinkButtonMoved = (e: PointerEvent): void => {
+ console.log("moved");
+ let dragData: { [id: string]: any } = {};
+ dragData["linkSourceDoc"] = SelectionManager.SelectedDocuments()[0];
+ if (this._linkButton.current != null) {
+ DragManager.StartDrag(this._linkButton.current, dragData, {
+ handlers: {
+ dragComplete: action(() => { }),
+ },
+ hideSource: false
+ })
+ }
+ document.removeEventListener("pointermove", this.onLinkButtonMoved)
+ document.removeEventListener("pointerup", this.onLinkButtonUp)
+ e.stopPropagation();
+ }
+
+
onPointerMove = (e: PointerEvent): void => {
e.stopPropagation();
e.preventDefault();
@@ -138,6 +185,13 @@ export class DocumentDecorations extends React.Component {
}
}
+ changeFlyoutContent = (): void => {
+
+ }
+ // buttonOnPointerUp = (e: React.PointerEvent): void => {
+ // e.stopPropagation();
+ // }
+
render() {
var bounds = this.Bounds;
if (this.Hidden) {
@@ -147,6 +201,18 @@ export class DocumentDecorations extends React.Component {
console.log("DocumentDecorations: Bounds Error")
return (null);
}
+
+ let linkButton = null;
+ if (SelectionManager.SelectedDocuments().length > 0) {
+ linkButton = (<Flyout
+ anchorPoint={anchorPoints.RIGHT_TOP}
+ content={
+ <LinkMenu docView={SelectionManager.SelectedDocuments()[0]} changeFlyout={this.changeFlyoutContent}>
+ </LinkMenu>
+ }>
+ <div id="linkButton" onPointerDown={this.onLinkButtonDown} ref={this._linkButton}></div>
+ </Flyout>);
+ }
return (
<div id="documentDecorations-container" style={{
width: (bounds.r - bounds.x + 40) + "px",
@@ -163,7 +229,10 @@ export class DocumentDecorations extends React.Component {
<div id="documentDecorations-bottomLeftResizer" className="documentDecorations-resizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
<div id="documentDecorations-bottomResizer" className="documentDecorations-resizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
<div id="documentDecorations-bottomRightResizer" className="documentDecorations-resizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
- </div>
+
+ {linkButton}
+
+ </div >
)
}
} \ No newline at end of file
diff --git a/src/client/views/collections/CollectionFreeFormView.tsx b/src/client/views/collections/CollectionFreeFormView.tsx
index 16002ad9f..e12d3d15c 100644
--- a/src/client/views/collections/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/CollectionFreeFormView.tsx
@@ -57,6 +57,9 @@ export class CollectionFreeFormView extends CollectionViewBase {
@undoBatch
@action
drop = (e: Event, de: DragManager.DropEvent) => {
+ if (!("documentView" in de.data)) {
+ return;
+ }
super.drop(e, de);
const docView: DocumentView = de.data["documentView"];
let doc: Document = docView ? docView.props.Document : de.data["document"];
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 263bb31d7..6d903b955 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -1,4 +1,4 @@
-import { action, computed } from "mobx";
+import { action, computed, runInAction } from "mobx";
import { observer } from "mobx-react";
import { Document } from "../../../fields/Document";
import { Field, FieldWaiting, Opt } from "../../../fields/Field";
@@ -24,6 +24,8 @@ import { WebBox } from "../nodes/WebBox";
import { PDFBox } from "../nodes/PDFBox";
import "./DocumentView.scss";
import React = require("react");
+import { TextField } from "../../../fields/TextField";
+import { DocumentManager } from "../../util/DocumentManager";
const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this?
@@ -113,6 +115,40 @@ export class DocumentView extends React.Component<DocumentViewProps> {
}
}
}
+
+ private dropDisposer?: DragManager.DragDropDisposer;
+ protected createDropTarget = (ele: HTMLDivElement) => {
+
+ }
+
+ componentDidMount() {
+ if (this._mainCont.current) {
+ this.dropDisposer = DragManager.MakeDropTarget(this._mainCont.current, { handlers: { drop: this.drop.bind(this) } });
+ }
+ runInAction(() => {
+ DocumentManager.Instance.DocumentViews.push(this);
+ })
+ }
+
+ componentDidUpdate() {
+ if (this.dropDisposer) {
+ this.dropDisposer();
+ }
+ if (this._mainCont.current) {
+ this.dropDisposer = DragManager.MakeDropTarget(this._mainCont.current, { handlers: { drop: this.drop.bind(this) } });
+ }
+ }
+
+ componentWillUnmount() {
+ if (this.dropDisposer) {
+ this.dropDisposer();
+ }
+ runInAction(() => {
+ DocumentManager.Instance.DocumentViews.splice(DocumentManager.Instance.DocumentViews.indexOf(this), 1);
+
+ })
+ }
+
onPointerMove = (e: PointerEvent): void => {
if (e.cancelBubble) {
return;
@@ -172,6 +208,34 @@ export class DocumentView extends React.Component<DocumentViewProps> {
}
@action
+ drop = (e: Event, de: DragManager.DropEvent) => {
+ console.log("drop");
+ const sourceDocView: DocumentView = de.data["linkSourceDoc"];
+ if (!sourceDocView) {
+ return;
+ }
+ let sourceDoc: Document = sourceDocView.props.Document;
+ let destDoc: Document = this.props.Document;
+ if (this.props.isTopMost) {
+ return;
+ }
+ let linkDoc: Document = new Document();
+
+ linkDoc.Set(KeyStore.Title, new TextField("New Link"));
+ linkDoc.Set(KeyStore.LinkDescription, new TextField(""));
+ linkDoc.Set(KeyStore.LinkTags, new TextField("Default"));
+
+ sourceDoc.GetOrCreateAsync(KeyStore.LinkedToDocs, ListField, field => { (field as ListField<Document>).Data.push(linkDoc) });
+ linkDoc.Set(KeyStore.LinkedToDocs, destDoc);
+ destDoc.GetOrCreateAsync(KeyStore.LinkedFromDocs, ListField, field => { (field as ListField<Document>).Data.push(linkDoc) });
+ linkDoc.Set(KeyStore.LinkedFromDocs, sourceDoc);
+
+
+
+ e.stopPropagation();
+ }
+
+ @action
onContextMenu = (e: React.MouseEvent): void => {
e.stopPropagation();
let moved = Math.abs(this._downX - e.clientX) > 3 || Math.abs(this._downY - e.clientY) > 3;
diff --git a/src/client/views/nodes/LinkBox.scss b/src/client/views/nodes/LinkBox.scss
new file mode 100644
index 000000000..00e5ebb3d
--- /dev/null
+++ b/src/client/views/nodes/LinkBox.scss
@@ -0,0 +1,39 @@
+.link-container {
+ width: 100%;
+ height: 30px;
+ display: flex;
+ flex-direction: row;
+ border-top: 0.5px solid #bababa;
+}
+
+.info-container {
+ width: 60%;
+ padding-top: 5px;
+ padding-left: 5px;
+ display: flex;
+ flex-direction: column
+}
+
+.link-name {
+ font-size: 11px;
+}
+
+.doc-name {
+ font-size: 8px;
+}
+
+.button-container {
+ width: 40%;
+ display: flex;
+ flex-direction: row;
+}
+
+.button {
+ height: 15px;
+ width: 15px;
+ margin: 8px 5px;
+ border-radius: 50%;
+ opacity: 0.6;
+ pointer-events: auto;
+ background-color: #2B6091;
+} \ No newline at end of file
diff --git a/src/client/views/nodes/LinkBox.tsx b/src/client/views/nodes/LinkBox.tsx
new file mode 100644
index 000000000..fcfb2fcb5
--- /dev/null
+++ b/src/client/views/nodes/LinkBox.tsx
@@ -0,0 +1,83 @@
+import { observable, computed, action } from "mobx";
+import React = require("react");
+import { SelectionManager } from "../../util/SelectionManager";
+import { observer } from "mobx-react";
+import './LinkBox.scss'
+import { KeyStore } from '../../../fields/KeyStore'
+import { props } from "bluebird";
+import { DocumentView } from "./DocumentView";
+import { Document } from "../../../fields/Document";
+import { ListField } from "../../../fields/ListField";
+import { DocumentManager } from "../../util/DocumentManager";
+import { LinkEditor } from "./LinkEditor";
+
+interface Props {
+ linkDoc: Document;
+ linkName: String;
+ pairedDoc: Document;
+ type: String;
+}
+
+@observer
+export class LinkBox extends React.Component<Props> {
+
+ onViewButtonPressed = (e: React.PointerEvent): void => {
+ console.log("view down");
+ e.stopPropagation();
+ let docView = DocumentManager.Instance.getDocumentView(this.props.pairedDoc);
+ if (docView) {
+ docView.props.focus(this.props.pairedDoc);
+ }
+ }
+
+ onEditButtonPressed = (e: React.PointerEvent): void => {
+ console.log("edit down");
+ e.stopPropagation();
+ }
+
+ onDeleteButtonPressed = (e: React.PointerEvent): void => {
+ console.log("delete down");
+ e.stopPropagation();
+ this.props.linkDoc.GetTAsync(KeyStore.LinkedFromDocs, Document, field => {
+ if (field) {
+ field.GetTAsync<ListField<Document>>(KeyStore.LinkedToDocs, ListField, field => {
+ if (field) {
+ field.Data.splice(field.Data.indexOf(this.props.linkDoc));
+ }
+ })
+ }
+ });
+ this.props.linkDoc.GetTAsync(KeyStore.LinkedToDocs, Document, field => {
+ if (field) {
+ field.GetTAsync<ListField<Document>>(KeyStore.LinkedFromDocs, ListField, field => {
+ if (field) {
+ field.Data.splice(field.Data.indexOf(this.props.linkDoc));
+ }
+ })
+ }
+ });
+ }
+
+ render() {
+
+ return (
+ <LinkEditor linkBox={this} />
+ // <div className="link-container">
+ // <div className="info-container">
+ // <div className="link-name">
+ // <p>{this.props.linkName}</p>
+ // </div>
+ // <div className="doc-name">
+ // <p>{this.props.type}{this.props.pairedDoc.Title}</p>
+ // </div>
+ // </div>
+
+ // <div className="button-container">
+ // <div className="button" onPointerDown={this.onViewButtonPressed}></div>
+ // <div className="button" onPointerDown={this.onEditButtonPressed}></div>
+ // <div className="button" onPointerDown={this.onDeleteButtonPressed}></div>
+ // </div>
+ // </div>
+ )
+ }
+} \ No newline at end of file
diff --git a/src/client/views/nodes/LinkEditor.scss b/src/client/views/nodes/LinkEditor.scss
new file mode 100644
index 000000000..b5db19b65
--- /dev/null
+++ b/src/client/views/nodes/LinkEditor.scss
@@ -0,0 +1,16 @@
+.edit-container {
+ width: 100%;
+ height: auto;
+ display: flex;
+ flex-direction: column;
+}
+
+.name-input {
+ margin-bottom: 10px;
+ font-size: 12px;
+}
+
+.description-input {
+ height: 100px;
+ font-size: 10px;
+} \ No newline at end of file
diff --git a/src/client/views/nodes/LinkEditor.tsx b/src/client/views/nodes/LinkEditor.tsx
new file mode 100644
index 000000000..38cfef239
--- /dev/null
+++ b/src/client/views/nodes/LinkEditor.tsx
@@ -0,0 +1,37 @@
+import { observable, computed, action } from "mobx";
+import React = require("react");
+import { SelectionManager } from "../../util/SelectionManager";
+import { observer } from "mobx-react";
+import './LinkBox.scss'
+import { KeyStore } from '../../../fields/KeyStore'
+import { props } from "bluebird";
+import { DocumentView } from "./DocumentView";
+import { Document } from "../../../fields/Document";
+import { ListField } from "../../../fields/ListField";
+import { DocumentManager } from "../../util/DocumentManager";
+import { LinkBox } from "./LinkBox";
+
+interface Props {
+ linkBox: LinkBox;
+}
+
+@observer
+export class LinkEditor extends React.Component<Props> {
+
+ onSaveButtonPressed = (e: React.PointerEvent): void => {
+ console.log("view down");
+ e.stopPropagation();
+
+ }
+
+ render() {
+
+ return (
+ <div className="edit-container">
+ <input className="name-input" type="text" placeholder="Name..."></input>
+ <input className="description-input" type="text" placeholder="Description"></input>
+ </div>
+
+ )
+ }
+} \ No newline at end of file
diff --git a/src/client/views/nodes/LinkMenu.scss b/src/client/views/nodes/LinkMenu.scss
new file mode 100644
index 000000000..af5b84ec6
--- /dev/null
+++ b/src/client/views/nodes/LinkMenu.scss
@@ -0,0 +1,20 @@
+#menu-container {
+ width: 100%;
+ height: auto;
+ display: flex;
+ flex-direction: column;
+}
+
+#search-bar {
+ width: 100%;
+ padding: 5px;
+ margin-bottom: 10px;
+ font-size: 12px;
+}
+
+#link-list {
+ margin-top: 5px;
+ width: 100%;
+ height: 100px;
+ overflow-y: scroll;
+} \ No newline at end of file
diff --git a/src/client/views/nodes/LinkMenu.tsx b/src/client/views/nodes/LinkMenu.tsx
new file mode 100644
index 000000000..577aba398
--- /dev/null
+++ b/src/client/views/nodes/LinkMenu.tsx
@@ -0,0 +1,58 @@
+import { observable, computed, action } from "mobx";
+import React = require("react");
+import { SelectionManager } from "../../util/SelectionManager";
+import { observer } from "mobx-react";
+import './LinkMenu.scss'
+import { KeyStore } from '../../../fields/KeyStore'
+import { props } from "bluebird";
+import { DocumentView } from "./DocumentView";
+import { LinkBox } from "./LinkBox"
+import { Document } from "../../../fields/Document";
+import { ListField } from "../../../fields/ListField";
+import { TextField } from "../../../fields/TextField";
+import { FieldWaiting } from "../../../fields/Field";
+
+interface Props {
+ docView: DocumentView;
+ changeFlyout: () => void
+}
+
+@observer
+export class LinkMenu extends React.Component<Props> {
+
+ 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, []);
+
+ return (
+ <div id="menu-container">
+ <input id="search-bar" type="text" placeholder="Search..."></input>
+ <div id="link-list">
+
+ {linkTo.map(link => {
+ let name = link.GetData(KeyStore.Title, TextField, new String);
+ let doc = link.GetT(KeyStore.LinkedToDocs, Document);
+ if (doc && doc != FieldWaiting) {
+ return <LinkBox linkDoc={link} linkName={name} pairedDoc={doc} type={"Destination: "} />
+ } else {
+ return <div></div>
+ }
+
+ })}
+
+ {linkFrom.map(link => {
+ let name = link.GetData(KeyStore.Title, TextField, new String);
+ let doc = link.GetT(KeyStore.LinkedFromDocs, Document);
+ if (doc && doc != FieldWaiting) {
+ return <LinkBox linkDoc={link} linkName={name} pairedDoc={doc} type={"Source: "} />
+ } else {
+ return <div></div>
+ }
+ })}
+ </div>
+
+ </div>
+ )
+ }
+} \ No newline at end of file
diff --git a/src/fields/Document.ts b/src/fields/Document.ts
index 2e873439c..25e239417 100644
--- a/src/fields/Document.ts
+++ b/src/fields/Document.ts
@@ -1,6 +1,6 @@
import { Key } from "./Key"
import { KeyStore } from "./KeyStore";
-import { Field, Cast, FieldWaiting, FieldValue, FieldId } from "./Field"
+import { Field, Cast, FieldWaiting, FieldValue, FieldId, Opt } from "./Field"
import { NumberField } from "./NumberField";
import { ObservableMap, computed, action } from "mobx";
import { TextField } from "./TextField";
@@ -128,6 +128,12 @@ export class Document extends Field {
return false;
}
+ GetTAsync<T extends Field>(key: Key, ctor: { new(): T }, callback: (field: Opt<T>) => void): boolean {
+ return this.GetAsync(key, (field) => {
+ callback(Cast(field, ctor));
+ })
+ }
+
/**
* Same as {@link Document#GetAsync}, except a field of the given type
* will be created if there is no field associated with the given key,
diff --git a/src/fields/KeyStore.ts b/src/fields/KeyStore.ts
index f67257093..f93a68c85 100644
--- a/src/fields/KeyStore.ts
+++ b/src/fields/KeyStore.ts
@@ -27,6 +27,10 @@ export namespace KeyStore {
export const Caption = new Key("Caption");
export const ActiveFrame = new Key("ActiveFrame");
export const DocumentText = new Key("DocumentText");
+ export const LinkedToDocs = new Key("LinkedToDocs");
+ export const LinkedFromDocs = new Key("LinkedFromDocs");
+ export const LinkDescription = new Key("LinkDescription");
+ export const LinkTags = new Key("LinkTag");
export const Thumbnail = new Key("Thumbnail");
export const CurPage = new Key("CurPage");
export const NumPages = new Key("NumPages");