aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/collections
diff options
context:
space:
mode:
authorSam Wilkins <abdullah_ahmed@brown.edu>2019-04-09 18:05:57 -0400
committerSam Wilkins <abdullah_ahmed@brown.edu>2019-04-09 18:05:57 -0400
commit79a0d8d66204868158849afda6518f6c641c826b (patch)
treec36b6064cb8577f375107c6df4885c7a3e0dc9e0 /src/client/views/collections
parentb7d02ec188ecf043300ed858fdb68148b3e52a71 (diff)
parent89fd4327db1536990b4a4dc218819a1077f82445 (diff)
merged with master
Diffstat (limited to 'src/client/views/collections')
-rw-r--r--src/client/views/collections/CollectionBaseView.tsx187
-rw-r--r--src/client/views/collections/CollectionDockingView.scss2
-rw-r--r--src/client/views/collections/CollectionDockingView.tsx74
-rw-r--r--src/client/views/collections/CollectionPDFView.scss2
-rw-r--r--src/client/views/collections/CollectionPDFView.tsx52
-rw-r--r--src/client/views/collections/CollectionSchemaView.tsx135
-rw-r--r--src/client/views/collections/CollectionSubView.tsx (renamed from src/client/views/collections/CollectionViewBase.tsx)119
-rw-r--r--src/client/views/collections/CollectionTreeView.scss86
-rw-r--r--src/client/views/collections/CollectionTreeView.tsx44
-rw-r--r--src/client/views/collections/CollectionVideoView.scss2
-rw-r--r--src/client/views/collections/CollectionVideoView.tsx56
-rw-r--r--src/client/views/collections/CollectionView.tsx185
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx10
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.scss2
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx108
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx23
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss14
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx173
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeView.scss2
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeView.tsx71
-rw-r--r--src/client/views/collections/collectionFreeForm/PreviewCursor.scss4
-rw-r--r--src/client/views/collections/collectionFreeForm/PreviewCursor.tsx7
22 files changed, 754 insertions, 604 deletions
diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx
new file mode 100644
index 000000000..4380c8194
--- /dev/null
+++ b/src/client/views/collections/CollectionBaseView.tsx
@@ -0,0 +1,187 @@
+import { action } from 'mobx';
+import { observer } from 'mobx-react';
+import * as React from 'react';
+import { Document } from '../../../fields/Document';
+import { Field, 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';
+
+export enum CollectionViewType {
+ Invalid,
+ Freeform,
+ Schema,
+ Docking,
+ Tree,
+}
+
+export interface CollectionRenderProps {
+ addDocument: (document: Document, allowDuplicates?: boolean) => boolean;
+ removeDocument: (document: Document) => boolean;
+ moveDocument: (document: Document, targetCollection: Document, addDocument: (document: Document) => boolean) => boolean;
+ active: () => boolean;
+ onActiveChanged: (isActive: boolean) => void;
+}
+
+export interface CollectionViewProps extends FieldViewProps {
+ onContextMenu?: (e: React.MouseEvent) => void;
+ children: (type: CollectionViewType, props: CollectionRenderProps) => JSX.Element | JSX.Element[] | null;
+ className?: string;
+ contentRef?: React.Ref<HTMLDivElement>;
+}
+
+export const COLLECTION_BORDER_WIDTH = 1;
+
+@observer
+export class CollectionBaseView extends React.Component<CollectionViewProps> {
+ get collectionViewType(): CollectionViewType {
+ let Document = this.props.Document;
+ let viewField = Document.GetT(KeyStore.ViewType, NumberField);
+ if (viewField === FieldWaiting) {
+ return CollectionViewType.Invalid;
+ } else if (viewField) {
+ return viewField.Data;
+ } else {
+ return CollectionViewType.Freeform;
+ }
+ }
+
+ active = (): boolean => {
+ var isSelected = this.props.isSelected();
+ var topMost = this.props.isTopMost;
+ return isSelected || this._isChildActive || topMost;
+ }
+
+ //TODO should this be observable?
+ private _isChildActive = false;
+ onActiveChanged = (isActive: boolean) => {
+ this._isChildActive = isActive;
+ this.props.onActiveChanged(isActive);
+ }
+
+ createsCycle(documentToAdd: Document, containerDocument: Document): boolean {
+ let data = documentToAdd.GetList<Document>(KeyStore.Data, []);
+ for (const doc of data) {
+ if (this.createsCycle(doc, containerDocument)) {
+ return true;
+ }
+ }
+ let annots = documentToAdd.GetList<Document>(KeyStore.Annotations, []);
+ 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) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @action.bound
+ addDocument(doc: Document, allowDuplicates: boolean = false): boolean {
+ let props = this.props;
+ var curPage = props.Document.GetNumber(KeyStore.CurPage, -1);
+ doc.SetOnPrototype(KeyStore.Page, new NumberField(curPage));
+ if (curPage >= 0) {
+ doc.SetOnPrototype(KeyStore.AnnotationOn, props.Document);
+ }
+ if (props.Document.Get(props.fieldKey) instanceof Field) {
+ //TODO This won't create the field if it doesn't already exist
+ const value = props.Document.GetData(props.fieldKey, ListField, new Array<Document>());
+ if (!this.createsCycle(doc, props.Document)) {
+ if (!value.some(v => v.Id === doc.Id) || allowDuplicates) {
+ value.push(doc);
+ }
+ }
+ else {
+ return false;
+ }
+ } 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);
+ // } else {
+ // console.log("removed " + field.Title);
+ // }
+ // `, {
+ // addReturn: false,
+ // params: {
+ // field: Document.name,
+ // added: "boolean"
+ // }
+ // });
+ // if (script.compiled) {
+ // field.addScript(new ScriptField(script));
+ // }
+ props.Document.SetOnPrototype(props.fieldKey, field);
+ }
+ else {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @action.bound
+ removeDocument(doc: Document): 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>());
+ let index = -1;
+ for (let i = 0; i < value.length; i++) {
+ if (value[i].Id === doc.Id) {
+ index = i;
+ break;
+ }
+ }
+ doc.GetTAsync(KeyStore.AnnotationOn, Document).then((annotationOn) => {
+ if (annotationOn === props.Document) {
+ doc.Set(KeyStore.AnnotationOn, undefined, true);
+ }
+ });
+
+ if (index !== -1) {
+ value.splice(index, 1);
+
+ // SelectionManager.DeselectAll()
+ ContextMenu.Instance.clearItems();
+ return true;
+ }
+ return false;
+ }
+
+ @action.bound
+ moveDocument(doc: Document, targetCollection: Document, addDocument: (doc: Document) => boolean): boolean {
+ if (this.props.Document === targetCollection) {
+ return true;
+ }
+ if (this.removeDocument(doc)) {
+ return addDocument(doc);
+ }
+ return false;
+ }
+
+ render() {
+ const props: CollectionRenderProps = {
+ addDocument: this.addDocument,
+ removeDocument: this.removeDocument,
+ moveDocument: this.moveDocument,
+ active: this.active,
+ onActiveChanged: this.onActiveChanged,
+ };
+ return (
+ <div className={this.props.className || "collectionView-cont"} onContextMenu={this.props.onContextMenu} ref={this.props.contentRef}>
+ {this.props.children(this.collectionViewType, props)}
+ </div>
+ );
+ }
+
+} \ No newline at end of file
diff --git a/src/client/views/collections/CollectionDockingView.scss b/src/client/views/collections/CollectionDockingView.scss
index 2706c3272..583d50c5b 100644
--- a/src/client/views/collections/CollectionDockingView.scss
+++ b/src/client/views/collections/CollectionDockingView.scss
@@ -3,7 +3,7 @@
}
.collectiondockingview-container {
- position: relative;
+ position: absolute;
top: 0;
left: 0;
overflow: hidden;
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 39e0dd989..ea6d3a247 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -8,14 +8,14 @@ import { Document } from "../../../fields/Document";
import { KeyStore } from "../../../fields/KeyStore";
import Measure from "react-measure";
import { FieldId, Opt, Field } from "../../../fields/Field";
-import { Utils } from "../../../Utils";
+import { Utils, returnTrue, emptyFunction } from "../../../Utils";
import { Server } from "../../Server";
import { undoBatch } from "../../util/UndoManager";
import { DocumentView } from "../nodes/DocumentView";
import "./CollectionDockingView.scss";
-import { COLLECTION_BORDER_WIDTH } from "./CollectionView";
+import { COLLECTION_BORDER_WIDTH } from "./CollectionBaseView";
import React = require("react");
-import { SubCollectionViewProps } from "./CollectionViewBase";
+import { SubCollectionViewProps } from "./CollectionSubView";
import { ServerUtils } from "../../../server/ServerUtil";
import { DragManager } from "../../util/DragManager";
import { TextField } from "../../../fields/TextField";
@@ -32,7 +32,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
documentId: document.Id,
//collectionDockingView: CollectionDockingView.Instance
}
- }
+ };
}
private _goldenLayout: any = null;
@@ -58,7 +58,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
let newItemStackConfig = {
type: 'stack',
content: [CollectionDockingView.makeDocumentConfig(document)]
- }
+ };
var docconfig = this._goldenLayout.root.layoutManager.createContentItem(newItemStackConfig, this._goldenLayout);
this._goldenLayout.root.contentItems[0].addChild(docconfig);
docconfig.callDownwards('_$init');
@@ -86,7 +86,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
let newItemStackConfig = {
type: 'stack',
content: [CollectionDockingView.makeDocumentConfig(document)]
- }
+ };
var newContentItem = this._goldenLayout.root.layoutManager.createContentItem(newItemStackConfig, this._goldenLayout);
@@ -101,12 +101,12 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
newRow.addChild(newContentItem, undefined, true);
newRow.addChild(collayout, 0, true);
- collayout.config["width"] = 50;
- newContentItem.config["width"] = 50;
+ collayout.config.width = 50;
+ newContentItem.config.width = 50;
}
if (minimize) {
- newContentItem.config["width"] = 10;
- newContentItem.config["height"] = 10;
+ newContentItem.config.width = 10;
+ newContentItem.config.height = 10;
}
newContentItem.callDownwards('_$init');
this._goldenLayout.root.callDownwards('setSize', [this._goldenLayout.width, this._goldenLayout.height]);
@@ -124,8 +124,9 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
this._goldenLayout = new GoldenLayout(JSON.parse(config));
}
else {
- if (config == JSON.stringify(this._goldenLayout.toConfig()))
+ if (config === JSON.stringify(this._goldenLayout.toConfig())) {
return;
+ }
try {
this._goldenLayout.unbind('itemDropped', this.itemDropped);
this._goldenLayout.unbind('tabCreated', this.tabCreated);
@@ -154,7 +155,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
reaction(
() => this.props.Document.GetText(KeyStore.Data, ""),
() => {
- if (!this._goldenLayout || this._ignoreStateChange != JSON.stringify(this._goldenLayout.toConfig())) {
+ if (!this._goldenLayout || this._ignoreStateChange !== JSON.stringify(this._goldenLayout.toConfig())) {
setTimeout(() => this.setupGoldenLayout(), 1);
}
this._ignoreStateChange = "";
@@ -193,23 +194,24 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
@action
onPointerDown = (e: React.PointerEvent): void => {
var className = (e.target as any).className;
- if ((className == "lm_title" || className == "lm_tab lm_active") && (e.ctrlKey || e.altKey)) {
+ if ((className === "lm_title" || className === "lm_tab lm_active") && (e.ctrlKey || e.altKey)) {
e.stopPropagation();
e.preventDefault();
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)
- DragManager.StartDocumentDrag([tab], new DragManager.DocumentDragData([f as Document]),
+ if (f instanceof Document) {
+ DragManager.StartDocumentDrag([tab], new DragManager.DocumentDragData([f]), e.pageX, e.pageY,
{
handlers: {
dragComplete: action(() => { }),
},
hideSource: false
- })
+ });
+ }
}));
}
- if (className == "lm_drag_handle" || className == "lm_close" || className == "lm_maximise" || className == "lm_minimise" || className == "lm_close_tab") {
+ if (className === "lm_drag_handle" || className === "lm_close" || className === "lm_maximise" || className === "lm_minimise" || className === "lm_close_tab") {
this._flush = true;
}
if (this.props.active()) {
@@ -220,22 +222,23 @@ 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.SetText(KeyStore.Data, json);
}
itemDropped = () => {
this.stateChanged();
}
+
tabCreated = (tab: any) => {
- if (tab.hasOwnProperty("contentItem") && tab.contentItem.config.type != "stack") {
- if (tab.titleElement[0].textContent.indexOf("-waiting") != -1) {
+ if (tab.hasOwnProperty("contentItem") && tab.contentItem.config.type !== "stack") {
+ if (tab.titleElement[0].textContent.indexOf("-waiting") !== -1) {
Server.GetField(tab.contentItem.config.props.documentId, action((f: Opt<Field>) => {
- if (f != undefined && f instanceof Document) {
+ if (f !== undefined && f instanceof Document) {
f.GetTAsync(KeyStore.Title, TextField, (tfield) => {
- if (tfield != undefined) {
+ if (tfield !== undefined) {
tab.titleElement[0].textContent = f.Title;
}
- })
+ });
}
}));
tab.titleElement[0].DashDocId = tab.contentItem.config.props.documentId;
@@ -280,7 +283,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
}
interface DockedFrameProps {
- documentId: FieldId,
+ documentId: FieldId;
//collectionDockingView: CollectionDockingView
}
@observer
@@ -296,35 +299,38 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
Server.GetField(this.props.documentId, action((f: Opt<Field>) => this._document = f as Document));
}
- private _nativeWidth = () => { return this._document!.GetNumber(KeyStore.NativeWidth, this._panelWidth); }
- private _nativeHeight = () => { return this._document!.GetNumber(KeyStore.NativeHeight, this._panelHeight); }
- private _contentScaling = () => { return this._panelWidth / (this._nativeWidth() ? this._nativeWidth() : this._panelWidth); }
+ private _nativeWidth = () => this._document!.GetNumber(KeyStore.NativeWidth, this._panelWidth);
+ private _nativeHeight = () => this._document!.GetNumber(KeyStore.NativeHeight, this._panelHeight);
+ private _contentScaling = () => this._panelWidth / (this._nativeWidth() ? this._nativeWidth() : this._panelWidth);
ScreenToLocalTransform = () => {
let { scale, translateX, translateY } = Utils.GetScreenTransform(this._mainCont.current!);
- return CollectionDockingView.Instance.props.ScreenToLocalTransform().translate(-translateX, -translateY).scale(scale / this._contentScaling())
+ return CollectionDockingView.Instance.props.ScreenToLocalTransform().translate(-translateX, -translateY).scale(scale / this._contentScaling());
}
render() {
- if (!this._document)
+ if (!this._document) {
return (null);
+ }
var content =
<div className="collectionDockingView-content" ref={this._mainCont}>
<DocumentView key={this._document.Id} Document={this._document}
- AddDocument={undefined}
- RemoveDocument={undefined}
+ addDocument={undefined}
+ removeDocument={undefined}
ContentScaling={this._contentScaling}
PanelWidth={this._nativeWidth}
PanelHeight={this._nativeHeight}
ScreenToLocalTransform={this.ScreenToLocalTransform}
isTopMost={true}
- SelectOnLoad={false}
+ selectOnLoad={false}
+ parentActive={returnTrue}
+ onActiveChanged={emptyFunction}
focus={(doc: Document) => { }}
ContainingCollectionView={undefined} />
- </div>
+ </div>;
return <Measure onResize={action((r: any) => { this._panelWidth = r.entry.width; this._panelHeight = r.entry.height; })}>
{({ measureRef }) => <div ref={measureRef}> {content} </div>}
- </Measure>
+ </Measure>;
}
} \ No newline at end of file
diff --git a/src/client/views/collections/CollectionPDFView.scss b/src/client/views/collections/CollectionPDFView.scss
index 0144625c1..0eca3f1cd 100644
--- a/src/client/views/collections/CollectionPDFView.scss
+++ b/src/client/views/collections/CollectionPDFView.scss
@@ -9,6 +9,8 @@
width: 100%;
height: 100%;
position: absolute;
+ top: 0;
+ left:0;
}
.collectionPdfView-backward {
diff --git a/src/client/views/collections/CollectionPDFView.tsx b/src/client/views/collections/CollectionPDFView.tsx
index 4d2daf149..97bac745c 100644
--- a/src/client/views/collections/CollectionPDFView.tsx
+++ b/src/client/views/collections/CollectionPDFView.tsx
@@ -1,22 +1,19 @@
import { action, computed, observable } from "mobx";
import { observer } from "mobx-react";
-import { Document } from "../../../fields/Document";
import { KeyStore } from "../../../fields/KeyStore";
import { ContextMenu } from "../ContextMenu";
-import { CollectionView, CollectionViewType } from "./CollectionView";
-import { CollectionViewProps } from "./CollectionViewBase";
-import "./CollectionPDFView.scss"
+import "./CollectionPDFView.scss";
import React = require("react");
-import { FieldId } from "../../../fields/Field";
+import { CollectionFreeFormView } from "./collectionFreeForm/CollectionFreeFormView";
+import { FieldView, FieldViewProps } from "../nodes/FieldView";
+import { CollectionRenderProps, CollectionBaseView, CollectionViewType } from "./CollectionBaseView";
@observer
-export class CollectionPDFView extends React.Component<CollectionViewProps> {
+export class CollectionPDFView extends React.Component<FieldViewProps> {
public static LayoutString(fieldKey: string = "DataKey") {
- return `<${CollectionPDFView.name} Document={Document}
- ScreenToLocalTransform={ScreenToLocalTransform} fieldKey={${fieldKey}} panelWidth={PanelWidth} panelHeight={PanelHeight} isSelected={isSelected} select={select} bindings={bindings}
- isTopMost={isTopMost} SelectOnLoad={selectOnLoad} BackgroundView={BackgroundView} focus={focus}/>`;
+ return FieldView.LayoutString(CollectionPDFView, fieldKey);
}
private get curPage() { return this.props.Document.GetNumber(KeyStore.CurPage, -1); }
@@ -25,35 +22,36 @@ export class CollectionPDFView extends React.Component<CollectionViewProps> {
@action onPageForward = () => this.curPage < this.numPages ? this.props.Document.SetNumber(KeyStore.CurPage, this.curPage + 1) : -1;
private get uIButtons() {
- let scaling = Math.min(1.8, this.props.ScreenToLocalTransform().transformDirection(1, 1)[0]);
+ let scaling = Math.min(1.8, this.props.ScreenToLocalTransform().Scale);
return (
<div className="collectionPdfView-buttonTray" key="tray" style={{ transform: `scale(${scaling}, ${scaling})` }}>
<button className="collectionPdfView-backward" onClick={this.onPageBack}>{"<"}</button>
<button className="collectionPdfView-forward" onClick={this.onPageForward}>{">"}</button>
- </div>);
+ </div>
+ );
}
- // "inherited" CollectionView API starts here...
- @observable
- public SelectedDocs: FieldId[] = []
- public active: () => boolean = () => CollectionView.Active(this);
-
- addDocument = (doc: Document, allowDuplicates: boolean): boolean => { return CollectionView.AddDocument(this.props, doc, allowDuplicates); }
- removeDocument = (doc: Document): boolean => { return CollectionView.RemoveDocument(this.props, doc); }
-
- specificContextMenu = (e: React.MouseEvent): void => {
- if (!e.isPropagationStopped() && this.props.Document.Id != "mainDoc") { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7
+ onContextMenu = (e: React.MouseEvent): void => {
+ if (!e.isPropagationStopped() && this.props.Document.Id !== "mainDoc") { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7
ContextMenu.Instance.addItem({ description: "PDFOptions", event: () => { } });
}
}
- get collectionViewType(): CollectionViewType { return CollectionViewType.Freeform; }
- get subView(): any { return CollectionView.SubView(this); }
+ private subView = (_type: CollectionViewType, renderProps: CollectionRenderProps) => {
+ let props = { ...this.props, ...renderProps };
+ return (
+ <>
+ <CollectionFreeFormView {...props} />
+ {this.props.isSelected() ? this.uIButtons : (null)}
+ </>
+ );
+ }
render() {
- return (<div className="collectionPdfView-cont" onContextMenu={this.specificContextMenu}>
- {this.subView}
- {this.props.isSelected() ? this.uIButtons : (null)}
- </div>)
+ return (
+ <CollectionBaseView {...this.props} className="collectionPdfView-cont" onContextMenu={this.onContextMenu}>
+ {this.subView}
+ </CollectionBaseView>
+ );
}
} \ No newline at end of file
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 0ff6c3b40..587f60b3d 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -1,4 +1,4 @@
-import React = require("react")
+import React = require("react");
import { library } from '@fortawesome/fontawesome-svg-core';
import { faCog, faPlus } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
@@ -22,9 +22,11 @@ import { EditableView } from "../EditableView";
import { DocumentView } from "../nodes/DocumentView";
import { FieldView, FieldViewProps } from "../nodes/FieldView";
import "./CollectionSchemaView.scss";
-import { CollectionView, COLLECTION_BORDER_WIDTH } from "./CollectionView";
-import { CollectionViewBase } from "./CollectionViewBase";
+import { CollectionView } from "./CollectionView";
+import { CollectionSubView } from "./CollectionSubView";
import { TextField } from "../../../fields/TextField";
+import { COLLECTION_BORDER_WIDTH } from "./CollectionBaseView";
+import { emptyFunction, returnFalse } from "../../../Utils";
// bcz: need to add drag and drop of rows and columns. This seems like it might work for rows: https://codesandbox.io/s/l94mn1q657
@@ -39,7 +41,7 @@ class KeyToggle extends React.Component<{ keyId: string, checked: boolean, toggl
if (field instanceof Key) {
this.key = field;
}
- }))
+ }));
}
render() {
@@ -47,14 +49,14 @@ class KeyToggle extends React.Component<{ keyId: string, checked: boolean, toggl
return (<div key={this.key.Id}>
<input type="checkbox" checked={this.props.checked} onChange={() => this.key && this.props.toggle(this.key)} />
{this.key.Name}
- </div>)
+ </div>);
}
return (null);
}
}
@observer
-export class CollectionSchemaView extends CollectionViewBase {
+export class CollectionSchemaView extends CollectionSubView {
private _mainCont = React.createRef<HTMLDivElement>();
private _startSplitPercent = 0;
private DIVIDER_WIDTH = 4;
@@ -73,25 +75,26 @@ export class CollectionSchemaView extends CollectionViewBase {
renderCell = (rowProps: CellInfo) => {
let props: FieldViewProps = {
- doc: rowProps.value[0],
+ Document: rowProps.value[0],
fieldKey: rowProps.value[1],
isSelected: () => false,
select: () => { },
isTopMost: false,
- bindings: {},
selectOnLoad: false,
- }
+ ScreenToLocalTransform: Transform.Identity,
+ focus: emptyFunction,
+ active: returnFalse,
+ onActiveChanged: emptyFunction,
+ };
let contents = (
<FieldView {...props} />
- )
+ );
let reference = React.createRef<HTMLDivElement>();
- let onItemDown = setupDrag(reference, () => props.doc, (containingCollection: CollectionView) => this.props.removeDocument(props.doc));
- let applyToDoc = (doc: Document, value: string) => {
- let script = CompileScript(value, { this: doc }, true);
- if (!script.compiled) {
- return false;
- }
- let field = script();
+ let onItemDown = setupDrag(reference, () => props.Document, this.props.moveDocument);
+ let applyToDoc = (doc: Document, 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;
@@ -103,33 +106,43 @@ export class CollectionSchemaView extends CollectionViewBase {
}
}
return false;
- }
+ };
return (
- <div className="collectionSchemaView-cellContents" onPointerDown={onItemDown} style={{ height: "56px" }} key={props.doc.Id} ref={reference}>
+ <div className="collectionSchemaView-cellContents" onPointerDown={onItemDown} style={{ height: "56px" }} key={props.Document.Id} ref={reference}>
<EditableView
display={"inline"}
contents={contents}
height={56}
GetValue={() => {
- let field = props.doc.Get(props.fieldKey);
+ let field = props.Document.Get(props.fieldKey);
if (field && field instanceof Field) {
return field.ToScriptString();
}
return field || "";
}}
SetValue={(value: string) => {
- return applyToDoc(props.doc, value);
+ let script = CompileScript(value, { addReturn: true, params: { this: Document.name } });
+ if (!script.compiled) {
+ return false;
+ }
+ return applyToDoc(props.Document, script.run);
}}
OnFillDown={(value: string) => {
+ let script = CompileScript(value, { addReturn: true, params: { this: Document.name } });
+ if (!script.compiled) {
+ return;
+ }
+ 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, value));
+ val.Data.forEach(doc => applyToDoc(doc, run));
}
- })
+ });
}}>
</EditableView>
</div>
- )
+ );
}
private getTrProps: ComponentPropsGetterR = (state, rowInfo) => {
@@ -143,12 +156,12 @@ export class CollectionSchemaView extends CollectionViewBase {
that._selectedIndex = rowInfo.index;
if (handleOriginal) {
- handleOriginal()
+ handleOriginal();
}
}),
style: {
- background: rowInfo.index == this._selectedIndex ? "lightGray" : "white",
- //color: rowInfo.index == this._selectedIndex ? "white" : "black"
+ background: rowInfo.index === this._selectedIndex ? "lightGray" : "white",
+ //color: rowInfo.index === this._selectedIndex ? "white" : "black"
}
};
}
@@ -169,22 +182,22 @@ export class CollectionSchemaView extends CollectionViewBase {
this.columns.splice(index, 1);
}
- })
+ });
}
//toggles preview side-panel of schema
@action
toggleExpander = (event: React.ChangeEvent<HTMLInputElement>) => {
this._startSplitPercent = this.splitPercentage;
- if (this._startSplitPercent == this.splitPercentage) {
- this.props.Document.SetNumber(KeyStore.SchemaSplitPercentage, this.splitPercentage == 0 ? 33 : 0);
+ if (this._startSplitPercent === this.splitPercentage) {
+ this.props.Document.SetNumber(KeyStore.SchemaSplitPercentage, this.splitPercentage === 0 ? 33 : 0);
}
}
@computed
get findAllDocumentKeys(): { [id: string]: boolean } {
const docs = this.props.Document.GetList<Document>(this.props.fieldKey, []);
- let keys: { [id: string]: boolean } = {}
+ let keys: { [id: string]: boolean } = {};
if (this._optionsActivated > -1) {
// 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
@@ -193,7 +206,7 @@ export class CollectionSchemaView extends CollectionViewBase {
// 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))));
}
- this.columns.forEach(key => keys[key.Id] = true)
+ this.columns.forEach(key => keys[key.Id] = true);
return keys;
}
@@ -206,8 +219,8 @@ export class CollectionSchemaView extends CollectionViewBase {
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);
+ if (this._startSplitPercent === this.splitPercentage) {
+ this.props.Document.SetNumber(KeyStore.SchemaSplitPercentage, this.splitPercentage === 0 ? 33 : 0);
}
}
onDividerDown = (e: React.PointerEvent) => {
@@ -235,14 +248,10 @@ export class CollectionSchemaView extends CollectionViewBase {
getContentScaling = (): number => this._contentScaling;
getPanelWidth = (): number => this._panelWidth;
getPanelHeight = (): number => this._panelHeight;
- getTransform = (): Transform => {
- return this.props.ScreenToLocalTransform().translate(- COLLECTION_BORDER_WIDTH - this.DIVIDER_WIDTH - this._dividerX, - COLLECTION_BORDER_WIDTH).scale(1 / this._contentScaling);
- }
- getPreviewTransform = (): Transform => {
- return this.props.ScreenToLocalTransform().translate(- COLLECTION_BORDER_WIDTH - this.DIVIDER_WIDTH - this._dividerX - this._tableWidth, - COLLECTION_BORDER_WIDTH).scale(1 / this._contentScaling);
- }
+ getTransform = (): Transform => this.props.ScreenToLocalTransform().translate(- COLLECTION_BORDER_WIDTH - this.DIVIDER_WIDTH - this._dividerX, - COLLECTION_BORDER_WIDTH).scale(1 / this._contentScaling);
+ getPreviewTransform = (): Transform => this.props.ScreenToLocalTransform().translate(- COLLECTION_BORDER_WIDTH - this.DIVIDER_WIDTH - this._dividerX - this._tableWidth, - COLLECTION_BORDER_WIDTH).scale(1 / this._contentScaling);
- focusDocument = (doc: Document) => { }
+ focusDocument = (doc: Document) => { };
onPointerDown = (e: React.PointerEvent): void => {
if (this.props.isSelected()) {
@@ -264,8 +273,9 @@ export class CollectionSchemaView extends CollectionViewBase {
this.newKeyName = e.currentTarget.value;
}
onWheel = (e: React.WheelEvent): void => {
- if (this.props.active())
+ if (this.props.active()) {
e.stopPropagation();
+ }
}
@observable _optionsActivated: number = 0;
@@ -291,29 +301,31 @@ export class CollectionSchemaView extends CollectionViewBase {
let doc: any = selected ? selected.Get(new Key(this.previewScript)) : undefined;
// let doc = CompileScript(this.previewScript, { this: selected }, true)();
- let content = this._selectedIndex == -1 || !selected ? (null) : (
+ let content = this._selectedIndex === -1 || !selected ? (null) : (
<Measure onResize={this.setScaling}>
{({ measureRef }) =>
<div className="collectionSchemaView-content" ref={measureRef}>
- {doc instanceof Document ? <DocumentView Document={doc}
- AddDocument={this.props.addDocument} RemoveDocument={this.props.removeDocument}
- isTopMost={false}
- SelectOnLoad={false}
- ScreenToLocalTransform={this.getPreviewTransform}
- ContentScaling={this.getContentScaling}
- PanelWidth={this.getPanelWidth}
- PanelHeight={this.getPanelHeight}
- ContainingCollectionView={this.props.CollectionView}
- focus={this.focusDocument}
- /> : null}
+ {doc instanceof Document ?
+ <DocumentView Document={doc}
+ addDocument={this.props.addDocument} removeDocument={this.props.removeDocument}
+ isTopMost={false}
+ selectOnLoad={false}
+ ScreenToLocalTransform={this.getPreviewTransform}
+ ContentScaling={this.getContentScaling}
+ PanelWidth={this.getPanelWidth}
+ PanelHeight={this.getPanelHeight}
+ ContainingCollectionView={undefined}
+ focus={this.focusDocument}
+ parentActive={this.props.active}
+ onActiveChanged={this.props.onActiveChanged} /> : null}
<input value={this.previewScript} onChange={this.onPreviewScriptChange}
style={{ position: 'absolute', bottom: '0px' }} />
</div>
}
</Measure>
- )
- let dividerDragger = this.splitPercentage == 0 ? (null) :
- <div className="collectionSchemaView-dividerDragger" onPointerDown={this.onDividerDown} style={{ width: `${this.DIVIDER_WIDTH}px` }} />
+ );
+ let dividerDragger = this.splitPercentage === 0 ? (null) :
+ <div className="collectionSchemaView-dividerDragger" onPointerDown={this.onDividerDown} style={{ width: `${this.DIVIDER_WIDTH}px` }} />;
//options button and menu
let optionsMenu = !this.props.active() ? (null) : (<Flyout
@@ -322,12 +334,11 @@ export class CollectionSchemaView extends CollectionViewBase {
<div id="schema-options-header"><h5><b>Options</b></h5></div>
<div id="options-flyout-div">
<h6 className="schema-options-subHeader">Preview Window</h6>
- <div id="preview-schema-checkbox-div"><input type="checkbox" key={"Show Preview"} checked={this.splitPercentage != 0} onChange={this.toggleExpander} /> Show Preview </div>
+ <div id="preview-schema-checkbox-div"><input type="checkbox" key={"Show Preview"} checked={this.splitPercentage !== 0} onChange={this.toggleExpander} /> Show Preview </div>
<h6 className="schema-options-subHeader" >Displayed Columns</h6>
<ul id="schema-col-checklist" >
- {Array.from(Object.keys(allKeys)).map(item => {
- return (<KeyToggle checked={allKeys[item]} key={item} keyId={item} toggle={this.toggleKey} />)
- })}
+ {Array.from(Object.keys(allKeys)).map(item =>
+ (<KeyToggle checked={allKeys[item]} key={item} keyId={item} toggle={this.toggleKey} />))}
</ul>
<input value={this.newKeyName} onChange={this.newKeyChange} />
<button onClick={this.addColumn}><FontAwesomeIcon style={{ color: "white" }} icon="plus" size="lg" /></button>
@@ -369,6 +380,6 @@ export class CollectionSchemaView extends CollectionViewBase {
{optionsMenu}
</div>
</div >
- )
+ );
}
} \ No newline at end of file
diff --git a/src/client/views/collections/CollectionViewBase.tsx b/src/client/views/collections/CollectionSubView.tsx
index 316d20c9d..5cdea0568 100644
--- a/src/client/views/collections/CollectionViewBase.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -4,43 +4,30 @@ import { ListField } from "../../../fields/ListField";
import React = require("react");
import { KeyStore } from "../../../fields/KeyStore";
import { FieldWaiting, Opt } from "../../../fields/Field";
-import { undoBatch } from "../../util/UndoManager";
+import { undoBatch, UndoManager } from "../../util/UndoManager";
import { DragManager } from "../../util/DragManager";
import { Documents, DocumentOptions } from "../../documents/Documents";
-import { Key } from "../../../fields/Key";
-import { Transform } from "../../util/Transform";
-import { CollectionView } from "./CollectionView";
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 request = require("request");
import { ServerUtils } from "../../../server/ServerUtil";
import { Server } from "../../Server";
+import { FieldViewProps } from "../nodes/FieldView";
+import * as rp from 'request-promise';
-export interface CollectionViewProps {
- fieldKey: Key;
- Document: Document;
- ScreenToLocalTransform: () => Transform;
- isSelected: () => boolean;
- isTopMost: boolean;
- select: (ctrlPressed: boolean) => void;
- bindings: any;
- panelWidth: () => number;
- panelHeight: () => number;
- focus: (doc: Document) => void;
+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;
}
export interface SubCollectionViewProps extends CollectionViewProps {
- active: () => boolean;
- addDocument: (doc: Document, allowDuplicates: boolean) => boolean;
- removeDocument: (doc: Document) => boolean;
- CollectionView: CollectionView;
}
export type CursorEntry = TupleField<[string, string], [number, number]>;
-export class CollectionViewBase extends React.Component<SubCollectionViewProps> {
+export class CollectionSubView extends React.Component<SubCollectionViewProps> {
private dropDisposer?: DragManager.DragDropDisposer;
protected createDropTarget = (ele: HTMLDivElement) => {
if (this.dropDisposer) {
@@ -71,8 +58,8 @@ export class CollectionViewBase extends React.Component<SubCollectionViewProps>
let entry = new TupleField<[string, string], [number, number]>([textInfo, position]);
cursors.push(entry);
}
- }))
- })
+ }));
+ });
}
}
@@ -80,13 +67,19 @@ export class CollectionViewBase extends React.Component<SubCollectionViewProps>
@action
protected drop(e: Event, de: DragManager.DropEvent): boolean {
if (de.data instanceof DragManager.DocumentDragData) {
- if (de.data.aliasOnDrop) {
+ if (de.data.aliasOnDrop || de.data.copyOnDrop) {
[KeyStore.Width, KeyStore.Height, KeyStore.CurPage].map(key =>
- de.data.draggedDocuments.GetTAsync(key, NumberField, (f: Opt<NumberField>) => f ? de.data.droppedDocument.SetNumber(key, f.Data) : null));
+ 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 = de.data.droppedDocuments.reduce((added, d) => this.props.addDocument(d, false), true);
- if (added && de.data.removeDocument && !de.data.aliasOnDrop) {
- de.data.removeDocument(this.props.CollectionView);
+ let added = false;
+ if (de.data.aliasOnDrop || de.data.copyOnDrop) {
+ added = de.data.droppedDocuments.reduce((added: boolean, d) => added || this.props.addDocument(d), false);
+ } else if (de.data.moveDocument) {
+ const move = de.data.moveDocument;
+ added = de.data.droppedDocuments.reduce((added: boolean, d) => added || move(d, this.props.Document, this.props.addDocument), false);
+ } else {
+ added = de.data.droppedDocuments.reduce((added: boolean, d) => added || this.props.addDocument(d), false);
}
e.stopPropagation();
return added;
@@ -107,6 +100,7 @@ export class CollectionViewBase extends React.Component<SubCollectionViewProps>
}
if (type.indexOf("pdf") !== -1) {
ctor = Documents.PdfDocument;
+ options.nativeWidth = 1200;
}
if (type.indexOf("html") !== -1) {
if (path.includes('localhost')) {
@@ -121,7 +115,7 @@ export class CollectionViewBase extends React.Component<SubCollectionViewProps>
alias.SetNumber(KeyStore.Height, options.height || options.width || 300);
this.props.addDocument(alias, false);
}
- })
+ });
return undefined;
}
ctor = Documents.WebDocument;
@@ -130,20 +124,19 @@ export class CollectionViewBase extends React.Component<SubCollectionViewProps>
return ctor ? ctor(path, options) : undefined;
}
+ @undoBatch
@action
protected onDrop(e: React.DragEvent, options: DocumentOptions): void {
- let that = this;
-
let html = e.dataTransfer.getData("text/html");
let text = e.dataTransfer.getData("text/plain");
if (text && text.startsWith("<div")) {
return;
}
- e.stopPropagation()
- e.preventDefault()
+ e.stopPropagation();
+ e.preventDefault();
- if (html && html.indexOf("<img") != 0 && !html.startsWith("<a")) {
+ if (html && html.indexOf("<img") !== 0 && !html.startsWith("<a")) {
console.log("not good");
let htmlDoc = Documents.HtmlDocument(html, { ...options, width: 300, height: 300 });
htmlDoc.SetText(KeyStore.DocumentText, text);
@@ -151,58 +144,70 @@ export class CollectionViewBase extends React.Component<SubCollectionViewProps>
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) {
- e.dataTransfer.items[i].getAsString(action((s: string) => {
- let document: Document;
- request.head(ServerUtils.prepend(RouteStore.corsProxy + "/" + s), (err, res, body) => {
+ if (item.kind === "string" && item.type.indexOf("uri") !== -1) {
+ let str: string;
+ let prom = new Promise<string>(res =>
+ e.dataTransfer.items[i].getAsString(res)).then(action((s: string) => {
+ str = s;
+ return rp.head(ServerUtils.prepend(RouteStore.corsProxy + "/" + s));
+ })).then(res => {
let type = res.headers["content-type"];
if (type) {
- let doc = this.getDocumentFromType(type, s, { ...options, width: 300, nativeWidth: 300 })
+ let doc = this.getDocumentFromType(type, str, { ...options, width: 300, nativeWidth: 300 });
if (doc) {
this.props.addDocument(doc, false);
}
}
});
- // this.props.addDocument(Documents.WebDocument(s, { ...options, width: 300, height: 300 }), false)
- }))
+ promises.push(prom);
+ // this.props.addDocument(Documents.WebDocument(s, { ...options, width: 300, height: 300 }), false)
}
- let type = item.type
- if (item.kind == "file") {
+ let type = item.type;
+ if (item.kind === "file") {
let file = item.getAsFile();
- let formData = new FormData()
+ let formData = new FormData();
if (file) {
- formData.append('file', file)
+ formData.append('file', file);
}
- fetch(upload, {
+ let prom = fetch(upload, {
method: 'POST',
body: formData
- }).then((res: Response) => {
- return res.json()
- }).then(json => {
+ }).then(async (res: Response) => {
+ const json = await res.json();
json.map((file: any) => {
- let path = window.location.origin + file
+ let path = window.location.origin + file;
runInAction(() => {
- let doc = this.getDocumentFromType(type, path, { ...options, nativeWidth: 300, width: 300 })
+ let doc = this.getDocumentFromType(type, path, { ...options, nativeWidth: 300, width: 300 });
- let docs = that.props.Document.GetT(KeyStore.Data, ListField);
- if (docs != FieldWaiting) {
+ let docs = this.props.Document.GetT(KeyStore.Data, ListField);
+ if (docs !== FieldWaiting) {
if (!docs) {
docs = new ListField<Document>();
- that.props.Document.Set(KeyStore.Data, docs)
+ this.props.Document.Set(KeyStore.Data, docs);
}
if (doc) {
docs.Data.push(doc);
}
}
- })
- })
- })
+ });
+ });
+ });
+ promises.push(prom);
}
}
+
+ if (promises.length) {
+ Promise.all(promises).catch(() => { }).then(() => batch.end());
+ } else {
+ batch.end();
+ }
}
}
diff --git a/src/client/views/collections/CollectionTreeView.scss b/src/client/views/collections/CollectionTreeView.scss
index 8ec996326..4f940e094 100644
--- a/src/client/views/collections/CollectionTreeView.scss
+++ b/src/client/views/collections/CollectionTreeView.scss
@@ -1,64 +1,62 @@
@import "../global_variables";
-#body {
+
+.collectionTreeView-dropTarget {
+ border: 0px solid transparent;
+ border-radius: $border-radius;
+ box-sizing: border-box;
+ height: 100%;
padding: 20px;
padding-left: 10px;
padding-right: 0px;
background: $light-color-secondary;
font-size: 13px;
overflow: scroll;
-}
-.no-indent {
- list-style: none;
- padding-left: 20px;
-}
+ ul {
+ list-style: none;
+ padding-left: 20px;
+ }
-li {
- margin: 5px 0;
-}
+ li {
+ margin: 5px 0;
+ }
-.collection-child {
- margin-top: 10px;
- margin-bottom: 10px;
-}
+ .collection-child {
+ margin-top: 10px;
+ margin-bottom: 10px;
+ }
-.no-indent {
- padding-left: 0;
-}
+ .no-indent {
+ padding-left: 0;
+ }
-.bullet {
- width: 1.5em;
- display: inline-block;
- color: $intermediate-color;
-}
+ .bullet {
+ width: 1.5em;
+ display: inline-block;
+ color: $intermediate-color;
+ }
-.coll-title {
- font-size: 24px;
- margin-bottom: 20px;
-}
+ .coll-title {
+ font-size: 24px;
+ margin-bottom: 20px;
+ }
-.collectionTreeView-dropTarget {
- border: 0px solid transparent;
- border-radius: $border-radius;
- box-sizing: border-box;
- height: 100%;
-}
+ .docContainer {
+ display: inline-table;
+ }
-.docContainer {
- display: inline-table;
-}
+ .docContainer:hover {
+ .delete-button {
+ display: inline;
+ // width: auto;
+ }
+ }
-.docContainer:hover {
.delete-button {
+ color: $intermediate-color;
+ // float: right;
+ margin-left: 15px;
+ // margin-top: 3px;
display: inline;
- width: auto;
}
-}
-
-.delete-button {
- color: $intermediate-color;
- float: right;
- margin-left: 15px;
- margin-top: 3px;
- display: inline;
} \ No newline at end of file
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 70790af18..659cff9fe 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -7,17 +7,21 @@ import { Document } from "../../../fields/Document";
import { FieldWaiting } from "../../../fields/Field";
import { KeyStore } from "../../../fields/KeyStore";
import { ListField } from "../../../fields/ListField";
-import { setupDrag } from "../../util/DragManager";
+import { setupDrag, DragManager } from "../../util/DragManager";
import { EditableView } from "../EditableView";
import "./CollectionTreeView.scss";
-import { CollectionView, COLLECTION_BORDER_WIDTH } from "./CollectionView";
-import { CollectionViewBase } from "./CollectionViewBase";
-import React = require("react")
+import { CollectionView } from "./CollectionView";
+import { CollectionSubView } from "./CollectionSubView";
+import React = require("react");
+import { COLLECTION_BORDER_WIDTH } from './CollectionBaseView';
+import { props } from 'bluebird';
export interface TreeViewProps {
document: Document;
deleteDoc: (doc: Document) => void;
+ moveDocument: DragManager.MoveFunction;
+ copyOnDrag: boolean;
}
export enum BulletType {
@@ -48,6 +52,16 @@ class TreeView extends React.Component<TreeViewProps> {
}
}
+ @action
+ move: DragManager.MoveFunction = (document, target, addDoc) => {
+ if (this.props.document === target) {
+ return true;
+ }
+ //TODO This should check if it was removed
+ this.remove(document);
+ return addDoc(document);
+ }
+
renderBullet(type: BulletType) {
let onClicked = action(() => this._collapsed = !this._collapsed);
let bullet: IconProp | undefined = undefined;
@@ -55,7 +69,7 @@ class TreeView extends React.Component<TreeViewProps> {
case BulletType.Collapsed: bullet = "caret-right"; break;
case BulletType.Collapsible: bullet = "caret-down"; break;
}
- return <div className="bullet" onClick={onClicked}>{bullet ? <FontAwesomeIcon icon={bullet} /> : ""} </div>
+ return <div className="bullet" onClick={onClicked}>{bullet ? <FontAwesomeIcon icon={bullet} /> : ""} </div>;
}
/**
@@ -63,7 +77,7 @@ class TreeView extends React.Component<TreeViewProps> {
*/
renderTitle() {
let reference = React.createRef<HTMLDivElement>();
- let onItemDown = setupDrag(reference, () => this.props.document, (containingCollection: CollectionView) => this.props.deleteDoc(this.props.document));
+ let onItemDown = setupDrag(reference, () => this.props.document, this.props.moveDocument, this.props.copyOnDrag);
let editableView = (titleString: string) =>
(<EditableView
display={"inline"}
@@ -79,20 +93,19 @@ class TreeView extends React.Component<TreeViewProps> {
<div className="docContainer" ref={reference} onPointerDown={onItemDown}>
{editableView(this.props.document.Title)}
<div className="delete-button" onClick={this.delete}><FontAwesomeIcon icon="trash-alt" size="xs" /></div>
- </div >)
+ </div >);
}
render() {
let bulletType = BulletType.List;
let childElements: JSX.Element | undefined = undefined;
-
var children = this.props.document.GetT<ListField<Document>>(KeyStore.Data, ListField);
if (children && children !== FieldWaiting) { // add children for a collection
if (!this._collapsed) {
bulletType = BulletType.Collapsible;
childElements = <ul>
- {children.Data.map(value => <TreeView key={value.Id} document={value} deleteDoc={this.remove} />)}
- </ul>
+ {children.Data.map(value => <TreeView key={value.Id} document={value} deleteDoc={this.remove} moveDocument={this.move} copyOnDrag={this.props.copyOnDrag} />)}
+ </ul >;
}
else bulletType = BulletType.Collapsed;
}
@@ -102,12 +115,12 @@ class TreeView extends React.Component<TreeViewProps> {
{this.renderTitle()}
{childElements ? childElements : (null)}
</li>
- </div>
+ </div>;
}
}
@observer
-export class CollectionTreeView extends CollectionViewBase {
+export class CollectionTreeView extends CollectionSubView {
@action
remove = (document: Document) => {
@@ -118,11 +131,12 @@ export class CollectionTreeView extends CollectionViewBase {
}
render() {
- var children = this.props.Document.GetT<ListField<Document>>(KeyStore.Data, ListField);
+ let children = this.props.Document.GetT<ListField<Document>>(KeyStore.Data, ListField);
+ let copyOnDrag = this.props.Document.GetBoolean(KeyStore.CopyDraggedItems, false);
let childrenElement = !children || children === FieldWaiting ? (null) :
(children.Data.map(value =>
- <TreeView document={value} key={value.Id} deleteDoc={this.remove} />)
- )
+ <TreeView document={value} key={value.Id} deleteDoc={this.remove} moveDocument={this.props.moveDocument} copyOnDrag={copyOnDrag} />)
+ );
return (
<div id="body" className="collectionTreeView-dropTarget" onWheel={(e: React.WheelEvent) => e.stopPropagation()} onDrop={(e: React.DragEvent) => this.onDrop(e, {})} ref={this.createDropTarget} style={{ borderWidth: `${COLLECTION_BORDER_WIDTH}px` }}>
diff --git a/src/client/views/collections/CollectionVideoView.scss b/src/client/views/collections/CollectionVideoView.scss
index cbb981b13..ed56ad268 100644
--- a/src/client/views/collections/CollectionVideoView.scss
+++ b/src/client/views/collections/CollectionVideoView.scss
@@ -3,6 +3,8 @@
width: 100%;
height: 100%;
position: absolute;
+ top: 0;
+ left:0;
}
.collectionVideoView-time{
diff --git a/src/client/views/collections/CollectionVideoView.tsx b/src/client/views/collections/CollectionVideoView.tsx
index 470a853e3..b02983a2e 100644
--- a/src/client/views/collections/CollectionVideoView.tsx
+++ b/src/client/views/collections/CollectionVideoView.tsx
@@ -1,17 +1,16 @@
-import { action, computed, observable, trace } from "mobx";
+import { action, observable, trace } from "mobx";
import { observer } from "mobx-react";
-import { Document } from "../../../fields/Document";
import { KeyStore } from "../../../fields/KeyStore";
import { ContextMenu } from "../ContextMenu";
-import { CollectionView, CollectionViewType } from "./CollectionView";
-import { CollectionViewProps } from "./CollectionViewBase";
+import { CollectionViewType, CollectionBaseView, CollectionRenderProps } from "./CollectionBaseView";
import React = require("react");
-import { FieldId } from "../../../fields/Field";
-import "./CollectionVideoView.scss"
+import "./CollectionVideoView.scss";
+import { CollectionFreeFormView } from "./collectionFreeForm/CollectionFreeFormView";
+import { FieldView, FieldViewProps } from "../nodes/FieldView";
@observer
-export class CollectionVideoView extends React.Component<CollectionViewProps> {
+export class CollectionVideoView extends React.Component<FieldViewProps> {
private _intervalTimer: any = undefined;
private _player: HTMLVideoElement | undefined = undefined;
@@ -19,12 +18,10 @@ export class CollectionVideoView extends React.Component<CollectionViewProps> {
@observable _isPlaying: boolean = false;
public static LayoutString(fieldKey: string = "DataKey") {
- return `<${CollectionVideoView.name} Document={Document}
- ScreenToLocalTransform={ScreenToLocalTransform} fieldKey={${fieldKey}} panelWidth={PanelWidth} panelHeight={PanelHeight} isSelected={isSelected} select={select} bindings={bindings}
- isTopMost={isTopMost} SelectOnLoad={selectOnLoad} BackgroundView={BackgroundView} focus={focus}/>`;
+ return FieldView.LayoutString(CollectionVideoView, fieldKey);
}
private get uIButtons() {
- let scaling = Math.min(1.8, this.props.ScreenToLocalTransform().transformDirection(1, 1)[0]);
+ let scaling = Math.min(1.8, this.props.ScreenToLocalTransform().Scale);
return ([
<div className="collectionVideoView-time" key="time" onPointerDown={this.onResetDown} style={{ transform: `scale(${scaling}, ${scaling})` }}>
<span>{"" + Math.round(this._currentTimecode)}</span>
@@ -42,7 +39,7 @@ export class CollectionVideoView extends React.Component<CollectionViewProps> {
@action
mainCont = (ele: HTMLDivElement | null) => {
if (ele) {
- this._player = ele!.getElementsByTagName("video")[0];
+ this._player = ele.getElementsByTagName("video")[0];
if (this.props.Document.GetNumber(KeyStore.CurPage, -1) >= 0) {
this._currentTimecode = this.props.Document.GetNumber(KeyStore.CurPage, -1);
}
@@ -60,7 +57,7 @@ export class CollectionVideoView extends React.Component<CollectionViewProps> {
@action
updateTimecode = () => {
if (this._player) {
- if ((this._player as any).AHackBecauseSomethingResetsTheVideoToZero != -1) {
+ if ((this._player as any).AHackBecauseSomethingResetsTheVideoToZero !== -1) {
this._player.currentTime = (this._player as any).AHackBecauseSomethingResetsTheVideoToZero;
(this._player as any).AHackBecauseSomethingResetsTheVideoToZero = -1;
} else {
@@ -101,30 +98,27 @@ export class CollectionVideoView extends React.Component<CollectionViewProps> {
}
- // "inherited" CollectionView API starts here...
-
- @observable
- public SelectedDocs: FieldId[] = []
- public active: () => boolean = () => CollectionView.Active(this);
-
- addDocument = (doc: Document, allowDuplicates: boolean): boolean => { return CollectionView.AddDocument(this.props, doc, allowDuplicates); }
- removeDocument = (doc: Document): boolean => { return CollectionView.RemoveDocument(this.props, doc); }
-
- specificContextMenu = (e: React.MouseEvent): void => {
- if (!e.isPropagationStopped() && this.props.Document.Id != "mainDoc") { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7
+ onContextMenu = (e: React.MouseEvent): void => {
+ if (!e.isPropagationStopped() && this.props.Document.Id !== "mainDoc") { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7
ContextMenu.Instance.addItem({ description: "VideoOptions", event: () => { } });
}
}
- get collectionViewType(): CollectionViewType { return CollectionViewType.Freeform; }
- get subView(): any { return CollectionView.SubView(this); }
-
+ private subView = (_type: CollectionViewType, renderProps: CollectionRenderProps) => {
+ let props = { ...this.props, ...renderProps };
+ return (
+ <>
+ <CollectionFreeFormView {...props} />
+ {this.props.isSelected() ? this.uIButtons : (null)}
+ </>
+ );
+ }
render() {
trace();
- return (<div className="collectionVideoView-cont" ref={this.mainCont} onContextMenu={this.specificContextMenu}>
- {this.subView}
- {this.props.isSelected() ? this.uIButtons : (null)}
- </div>)
+ return (
+ <CollectionBaseView {...this.props} className="collectionVideoView-cont" contentRef={this.mainCont} onContextMenu={this.onContextMenu}>
+ {this.subView}
+ </CollectionBaseView>);
}
} \ No newline at end of file
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 014aa1d8f..8abd0a02d 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -1,159 +1,46 @@
-import { action, computed, observable } from "mobx";
-import { observer } from "mobx-react";
-import { Document } from "../../../fields/Document";
-import { ListField } from "../../../fields/ListField";
-import { SelectionManager } from "../../util/SelectionManager";
-import { ContextMenu } from "../ContextMenu";
-import React = require("react");
-import { KeyStore } from "../../../fields/KeyStore";
-import { NumberField } from "../../../fields/NumberField";
-import { CollectionFreeFormView } from "./collectionFreeForm/CollectionFreeFormView";
-import { CollectionDockingView } from "./CollectionDockingView";
-import { CollectionSchemaView } from "./CollectionSchemaView";
-import { CollectionViewProps } from "./CollectionViewBase";
-import { CollectionTreeView } from "./CollectionTreeView";
-import { Field, FieldId, FieldWaiting } from "../../../fields/Field";
-import { CurrentUserUtils } from "../../../server/authentication/models/current_user_utils";
-
-export enum CollectionViewType {
- Invalid,
- Freeform,
- Schema,
- Docking,
- Tree
-}
-
-export const COLLECTION_BORDER_WIDTH = 1;
+import * as React from 'react';
+import { FieldViewProps, FieldView } from '../nodes/FieldView';
+import { CollectionBaseView, CollectionViewType, CollectionRenderProps } from './CollectionBaseView';
+import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormView';
+import { CollectionSchemaView } from './CollectionSchemaView';
+import { CollectionDockingView } from './CollectionDockingView';
+import { CollectionTreeView } from './CollectionTreeView';
+import { ContextMenu } from '../ContextMenu';
+import { CurrentUserUtils } from '../../../server/authentication/models/current_user_utils';
+import { KeyStore } from '../../../fields/KeyStore';
+import { observer } from 'mobx-react';
+import { undoBatch } from '../../util/UndoManager';
@observer
-export class CollectionView extends React.Component<CollectionViewProps> {
-
- public static LayoutString(fieldKey: string = "DataKey") {
- return `<${CollectionView.name} Document={Document}
- ScreenToLocalTransform={ScreenToLocalTransform} fieldKey={${fieldKey}} panelWidth={PanelWidth} panelHeight={PanelHeight} isSelected={isSelected} select={select} bindings={bindings}
- isTopMost={isTopMost} SelectOnLoad={selectOnLoad} BackgroundView={BackgroundView} focus={focus}/>`;
- }
-
- @observable
- public SelectedDocs: FieldId[] = [];
- public active: () => boolean = () => CollectionView.Active(this);
- addDocument = (doc: Document, allowDuplicates: boolean): boolean => { return CollectionView.AddDocument(this.props, doc, allowDuplicates); }
- removeDocument = (doc: Document): boolean => { return CollectionView.RemoveDocument(this.props, doc); }
- get subView() { return CollectionView.SubView(this); }
-
- public static Active(self: CollectionView): boolean {
- var isSelected = self.props.isSelected();
- var childSelected = SelectionManager.SelectedDocuments().some(view => view.props.ContainingCollectionView == self);
- var topMost = self.props.isTopMost;
- return isSelected || childSelected || topMost;
- }
-
- static createsCycle(documentToAdd: Document, containerDocument: Document): boolean {
- let data = documentToAdd.GetList<Document>(KeyStore.Data, []);
- for (let i = 0; i < data.length; i++) {
- if (CollectionView.createsCycle(data[i], containerDocument))
- return true;
- }
- let annots = documentToAdd.GetList<Document>(KeyStore.Annotations, []);
- for (let i = 0; i < annots.length; i++) {
- if (CollectionView.createsCycle(annots[i], containerDocument))
- return true;
- }
- for (let containerProto: any = containerDocument; containerProto && containerProto != FieldWaiting; containerProto = containerProto.GetPrototype()) {
- if (containerProto.Id == documentToAdd.Id)
- return true;
- }
- return false;
- }
-
- @action
- public static AddDocument(props: CollectionViewProps, doc: Document, allowDuplicates: boolean): boolean {
- var curPage = props.Document.GetNumber(KeyStore.CurPage, -1);
- doc.SetOnPrototype(KeyStore.Page, new NumberField(curPage));
- if (curPage >= 0) {
- doc.SetOnPrototype(KeyStore.AnnotationOn, props.Document);
- }
- if (props.Document.Get(props.fieldKey) instanceof Field) {
- //TODO This won't create the field if it doesn't already exist
- const value = props.Document.GetData(props.fieldKey, ListField, new Array<Document>())
- if (!CollectionView.createsCycle(doc, props.Document)) {
- if (!value.some(v => v.Id == doc.Id) || allowDuplicates)
- value.push(doc);
- }
- else
- return false;
- } else {
- let proto = props.Document.GetPrototype();
- if (!proto || proto == FieldWaiting || !CollectionView.createsCycle(proto, doc)) {
- props.Document.SetOnPrototype(props.fieldKey, new ListField([doc]));
- }
- else
- return false;
- }
- return true;
- }
-
- @action
- public static RemoveDocument(props: CollectionViewProps, doc: Document): boolean {
- //TODO This won't create the field if it doesn't already exist
- const value = props.Document.GetData(props.fieldKey, ListField, new Array<Document>())
- let index = -1;
- for (let i = 0; i < value.length; i++) {
- if (value[i].Id == doc.Id) {
- index = i;
- break;
- }
- }
- doc.GetTAsync(KeyStore.AnnotationOn, Document).then((annotationOn) => {
- if (annotationOn == props.Document) {
- doc.Set(KeyStore.AnnotationOn, undefined, true);
- }
- })
-
- if (index !== -1) {
- value.splice(index, 1)
-
- //SelectionManager.DeselectAll()
- ContextMenu.Instance.clearItems()
- return true;
- }
- return false
- }
-
- get collectionViewType(): CollectionViewType {
- let Document = this.props.Document;
- let viewField = Document.GetT(KeyStore.ViewType, NumberField);
- if (viewField === FieldWaiting) {
- return CollectionViewType.Invalid;
- } else if (viewField) {
- return viewField.Data;
- } else {
- return CollectionViewType.Freeform;
- }
- }
-
- specificContextMenu = (e: React.MouseEvent): void => {
- if (!e.isPropagationStopped() && this.props.Document.Id != CurrentUserUtils.MainDocId) { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7
- ContextMenu.Instance.addItem({ description: "Freeform", event: () => this.props.Document.SetNumber(KeyStore.ViewType, CollectionViewType.Freeform) })
- ContextMenu.Instance.addItem({ description: "Schema", event: () => this.props.Document.SetNumber(KeyStore.ViewType, CollectionViewType.Schema) })
- ContextMenu.Instance.addItem({ description: "Treeview", event: () => this.props.Document.SetNumber(KeyStore.ViewType, CollectionViewType.Tree) })
+export class CollectionView extends React.Component<FieldViewProps> {
+ public static LayoutString(fieldStr: string = "DataKey") { return FieldView.LayoutString(CollectionView, fieldStr); }
+
+ private SubView = (type: CollectionViewType, renderProps: CollectionRenderProps) => {
+ let props = { ...this.props, ...renderProps };
+ switch (type) {
+ case CollectionViewType.Schema: return (<CollectionSchemaView {...props} />);
+ case CollectionViewType.Docking: return (<CollectionDockingView {...props} />);
+ case CollectionViewType.Tree: return (<CollectionTreeView {...props} />);
+ case CollectionViewType.Freeform:
+ default:
+ return (<CollectionFreeFormView {...props} />);
}
+ return (null);
}
- public static SubView(self: CollectionView) {
- let subProps = { ...self.props, addDocument: self.addDocument, removeDocument: self.removeDocument, active: self.active, CollectionView: self }
- switch (self.collectionViewType) {
- case CollectionViewType.Freeform: return (<CollectionFreeFormView {...subProps} />)
- case CollectionViewType.Schema: return (<CollectionSchemaView {...subProps} />)
- case CollectionViewType.Docking: return (<CollectionDockingView {...subProps} />)
- case CollectionViewType.Tree: return (<CollectionTreeView {...subProps} />)
+ onContextMenu = (e: React.MouseEvent): void => {
+ if (!e.isPropagationStopped() && this.props.Document.Id !== CurrentUserUtils.MainDocId) { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7
+ ContextMenu.Instance.addItem({ description: "Freeform", event: undoBatch(() => this.props.Document.SetNumber(KeyStore.ViewType, CollectionViewType.Freeform)) });
+ ContextMenu.Instance.addItem({ description: "Schema", event: undoBatch(() => this.props.Document.SetNumber(KeyStore.ViewType, CollectionViewType.Schema)) });
+ ContextMenu.Instance.addItem({ description: "Treeview", event: undoBatch(() => this.props.Document.SetNumber(KeyStore.ViewType, CollectionViewType.Tree)) });
}
- return (null);
}
render() {
- return (<div className="collectionView-cont" onContextMenu={this.specificContextMenu}>
- {this.subView}
- </div>)
+ return (
+ <CollectionBaseView {...this.props} onContextMenu={this.onContextMenu}>
+ {this.SubView}
+ </CollectionBaseView>
+ );
}
-}
+} \ No newline at end of file
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
index e84f0c5ad..081b3eb6c 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
@@ -23,15 +23,15 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
let l = this.props.LinkDocs;
let a = this.props.A;
let b = this.props.B;
- let x1 = a.GetNumber(KeyStore.X, 0) + a.GetNumber(KeyStore.Width, 0) / 2;
- let y1 = a.GetNumber(KeyStore.Y, 0) + a.GetNumber(KeyStore.Height, 0) / 2;
- let x2 = b.GetNumber(KeyStore.X, 0) + b.GetNumber(KeyStore.Width, 0) / 2;
- let y2 = b.GetNumber(KeyStore.Y, 0) + b.GetNumber(KeyStore.Height, 0) / 2;
+ let x1 = a.GetNumber(KeyStore.X, 0) + (a.GetBoolean(KeyStore.Minimized, false) ? 5 : a.GetNumber(KeyStore.Width, 0) / 2);
+ let y1 = a.GetNumber(KeyStore.Y, 0) + (a.GetBoolean(KeyStore.Minimized, false) ? 5 : a.GetNumber(KeyStore.Height, 0) / 2);
+ let x2 = b.GetNumber(KeyStore.X, 0) + (b.GetBoolean(KeyStore.Minimized, false) ? 5 : b.GetNumber(KeyStore.Width, 0) / 2);
+ let y2 = b.GetNumber(KeyStore.Y, 0) + (b.GetBoolean(KeyStore.Minimized, false) ? 5 : b.GetNumber(KeyStore.Height, 0) / 2);
return (
<line key={Utils.GenerateGuid()} className="collectionfreeformlinkview-linkLine" onPointerDown={this.onPointerDown}
style={{ strokeWidth: `${l.length * 5}` }}
x1={`${x1}`} y1={`${y1}`}
x2={`${x2}`} y2={`${y2}`} />
- )
+ );
}
} \ No newline at end of file
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.scss
index 4341c82f7..30e158603 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.scss
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.scss
@@ -1,6 +1,8 @@
.collectionfreeformlinksview-svgCanvas{
transform: translate(-10000px,-10000px);
position: absolute;
+ top: 0;
+ left: 0;
width: 20000px;
height: 20000px;
pointer-events: none;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
index eb20b3100..cf058090d 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
@@ -1,4 +1,4 @@
-import { computed, reaction, runInAction, trace } from "mobx";
+import { computed, reaction } from "mobx";
import { observer } from "mobx-react";
import { Document } from "../../../../fields/Document";
import { FieldWaiting } from "../../../../fields/Field";
@@ -7,66 +7,73 @@ import { ListField } from "../../../../fields/ListField";
import { Utils } from "../../../../Utils";
import { DocumentManager } from "../../../util/DocumentManager";
import { DocumentView } from "../../nodes/DocumentView";
-import { CollectionViewProps } from "../CollectionViewBase";
+import { CollectionViewProps } from "../CollectionSubView";
import "./CollectionFreeFormLinksView.scss";
import { CollectionFreeFormLinkView } from "./CollectionFreeFormLinkView";
import React = require("react");
-import v5 = require("uuid/v5");
@observer
export class CollectionFreeFormLinksView extends React.Component<CollectionViewProps> {
+ HackToAvoidReactionFiringUnnecessarily?: Document = undefined;
componentDidMount() {
- reaction(() => {
- return DocumentManager.Instance.getAllDocumentViews(this.props.Document).map(dv => dv.props.Document.GetNumber(KeyStore.X, 0))
- }, () => {
- let views = DocumentManager.Instance.getAllDocumentViews(this.props.Document);
- for (let i = 0; i < views.length; i++) {
- for (let j = i + 1; j < views.length; j++) {
- let srcDoc = views[j].props.Document;
- let dstDoc = views[i].props.Document;
- let x1 = srcDoc.GetNumber(KeyStore.X, 0);
- let x1w = srcDoc.GetNumber(KeyStore.Width, -1);
- let x2 = dstDoc.GetNumber(KeyStore.X, 0);
- let x2w = dstDoc.GetNumber(KeyStore.Width, -1);
- if (x1w < 0 || x2w < 0)
- continue;
- dstDoc.GetTAsync(KeyStore.Prototype, Document).then((protoDest) =>
- srcDoc.GetTAsync(KeyStore.Prototype, Document).then((protoSrc) => runInAction(() => {
- let dstTarg = (protoDest ? protoDest : dstDoc);
- let srcTarg = (protoSrc ? protoSrc : srcDoc);
- let findBrush = (field: ListField<Document>) => field.Data.findIndex(brush => {
- let bdocs = brush.GetList(KeyStore.BrushingDocs, [] as Document[]);
- return (bdocs.length == 0 || (bdocs[0] == dstTarg && bdocs[1] == srcTarg) || (bdocs[0] == srcTarg && bdocs[1] == dstTarg))
- });
- let brushAction = (field: ListField<Document>) => {
- let found = findBrush(field);
- if (found != -1)
- field.Data.splice(found, 1);
- };
- if (Math.abs(x1 + x1w - x2) < 20 || Math.abs(x2 + x2w - x1) < 20) {
- let linkDoc: Document = new Document();
- linkDoc.SetText(KeyStore.Title, "Histogram Brush");
- linkDoc.SetText(KeyStore.LinkDescription, "Brush between " + srcTarg.Title + " and " + dstTarg.Title);
- linkDoc.SetData(KeyStore.BrushingDocs, [dstTarg, srcTarg], ListField);
-
- brushAction = brushAction = (field: ListField<Document>) => (findBrush(field) == -1) && field.Data.push(linkDoc);
+ this.HackToAvoidReactionFiringUnnecessarily = this.props.Document;
+ reaction(() =>
+ DocumentManager.Instance.getAllDocumentViews(this.HackToAvoidReactionFiringUnnecessarily!).
+ map(dv => dv.props.Document.GetNumber(KeyStore.X, 0)),
+ () => {
+ let views = DocumentManager.Instance.getAllDocumentViews(this.props.Document);
+ for (let i = 0; i < views.length; i++) {
+ for (let j = 0; j < views.length; j++) {
+ let srcDoc = views[j].props.Document;
+ let dstDoc = views[i].props.Document;
+ let x1 = srcDoc.GetNumber(KeyStore.X, 0);
+ let x1w = srcDoc.GetNumber(KeyStore.Width, -1);
+ let x2 = dstDoc.GetNumber(KeyStore.X, 0);
+ let x2w = dstDoc.GetNumber(KeyStore.Width, -1);
+ if (x1w < 0 || x2w < 0 || i === j) {
+ continue;
+ }
+ let dstTarg = dstDoc;
+ let srcTarg = srcDoc;
+ let findBrush = (field: ListField<Document>) => field.Data.findIndex(brush => {
+ let bdocs = brush ? brush.GetList(KeyStore.BrushingDocs, [] as Document[]) : [];
+ return (bdocs.length && ((bdocs[0] === dstTarg && bdocs[1] === srcTarg)) ? true : false);
+ });
+ let brushAction = (field: ListField<Document>) => {
+ let found = findBrush(field);
+ if (found !== -1) {
+ console.log("REMOVE BRUSH " + srcTarg.Title + " " + dstTarg.Title);
+ field.Data.splice(found, 1);
}
- dstTarg.GetOrCreateAsync(KeyStore.BrushingDocs, ListField, brushAction);
- srcTarg.GetOrCreateAsync(KeyStore.BrushingDocs, ListField, brushAction);
+ };
+ if (Math.abs(x1 + x1w - x2) < 20) {
+ let linkDoc: Document = new Document();
+ linkDoc.SetText(KeyStore.Title, "Histogram Brush");
+ linkDoc.SetText(KeyStore.LinkDescription, "Brush between " + srcTarg.Title + " and " + dstTarg.Title);
+ linkDoc.SetData(KeyStore.BrushingDocs, [dstTarg, srcTarg], ListField);
+
+ brushAction = brushAction = (field: ListField<Document>) => {
+ if (findBrush(field) === -1) {
+ console.log("ADD BRUSH " + srcTarg.Title + " " + dstTarg.Title);
+ (findBrush(field) === -1) && field.Data.push(linkDoc);
+ }
+ };
}
- )))
+ dstTarg.GetOrCreateAsync(KeyStore.BrushingDocs, ListField, brushAction);
+ srcTarg.GetOrCreateAsync(KeyStore.BrushingDocs, ListField, brushAction);
+
+ }
}
- }
- })
+ });
}
documentAnchors(view: DocumentView) {
let equalViews = [view];
let containerDoc = view.props.Document.GetT(KeyStore.AnnotationOn, Document);
- if (containerDoc && containerDoc != FieldWaiting && containerDoc instanceof Document) {
- equalViews = DocumentManager.Instance.getDocumentViews(containerDoc.GetPrototype() as Document)
+ if (containerDoc && containerDoc instanceof Document) {
+ equalViews = DocumentManager.Instance.getDocumentViews(containerDoc.GetPrototype()!);
}
- return equalViews.filter(sv => sv.props.ContainingCollectionView && sv.props.ContainingCollectionView.props.Document == this.props.Document);
+ return equalViews.filter(sv => sv.props.ContainingCollectionView && sv.props.ContainingCollectionView.props.Document === this.props.Document);
}
@computed
@@ -78,17 +85,18 @@ export class CollectionFreeFormLinksView extends React.Component<CollectionViewP
srcViews.map(sv => targetViews.map(tv => possiblePairs.push({ a: sv.props.Document, b: tv.props.Document })));
possiblePairs.map(possiblePair => {
if (!drawnPairs.reduce((found, drawnPair) => {
- let match = (possiblePair.a == drawnPair.a && possiblePair.b == drawnPair.b);
+ let match = (possiblePair.a === drawnPair.a && possiblePair.b === drawnPair.b);
if (match) {
- if (!drawnPair.l.reduce((found, link) => found || link.Id == connection.l.Id, false))
+ if (!drawnPair.l.reduce((found, link) => found || link.Id === connection.l.Id, false)) {
drawnPair.l.push(connection.l);
+ }
}
return match || found;
}, false)) {
- drawnPairs.push({ a: possiblePair.a, b: possiblePair.b, l: [connection.l] as Document[] });
+ drawnPairs.push({ a: possiblePair.a, b: possiblePair.b, l: [connection.l] });
}
- })
- return drawnPairs
+ });
+ return drawnPairs;
}, [] as { a: Document, b: Document, l: Document[] }[]);
return connections.map(c => <CollectionFreeFormLinkView key={Utils.GenerateGuid()} A={c.a} B={c.b} LinkDocs={c.l} />);
}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx
index 19382e66f..fc832264d 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx
@@ -1,21 +1,8 @@
-import { action, computed, observable } from "mobx";
+import { computed } from "mobx";
import { observer } from "mobx-react";
-import { Document } from "../../../../fields/Document";
-import { FieldWaiting } from "../../../../fields/Field";
import { KeyStore } from "../../../../fields/KeyStore";
-import { TextField } from "../../../../fields/TextField";
-import { DragManager } from "../../../util/DragManager";
-import { Transform } from "../../../util/Transform";
-import { undoBatch } from "../../../util/UndoManager";
-import { InkingCanvas } from "../../InkingCanvas";
-import { CollectionFreeFormDocumentView } from "../../nodes/CollectionFreeFormDocumentView";
-import { DocumentContentsView } from "../../nodes/DocumentContentsView";
-import { DocumentViewProps } from "../../nodes/DocumentView";
-import { COLLECTION_BORDER_WIDTH } from "../CollectionView";
-import { CollectionViewBase, CollectionViewProps, CursorEntry } from "../CollectionViewBase";
-import { CollectionFreeFormLinksView } from "./CollectionFreeFormLinksView";
+import { CollectionViewProps, CursorEntry } from "../CollectionSubView";
import "./CollectionFreeFormView.scss";
-import { MarqueeView } from "./MarqueeView";
import React = require("react");
import v5 = require("uuid/v5");
import { CurrentUserUtils } from "../../../../server/authentication/models/current_user_utils";
@@ -70,7 +57,7 @@ export class CollectionFreeFormRemoteCursors extends React.Component<CollectionV
let id = entry.Data[0][0];
let email = entry.Data[0][1];
let point = entry.Data[1];
- this.drawCrosshairs("#" + v5(id, v5.URL).substring(0, 6).toUpperCase() + "22")
+ this.drawCrosshairs("#" + v5(id, v5.URL).substring(0, 6).toUpperCase() + "22");
return (
<div
key={id}
@@ -82,7 +69,7 @@ export class CollectionFreeFormRemoteCursors extends React.Component<CollectionV
}}
>
<canvas
- ref={(el) => { if (el) this.crosshairs = el }}
+ ref={(el) => { if (el) this.crosshairs = el; }}
width={20}
height={20}
style={{
@@ -106,7 +93,7 @@ export class CollectionFreeFormRemoteCursors extends React.Component<CollectionV
</div>
);
}
- })
+ });
}
render() {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
index 81d21d89a..81f2146e4 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
@@ -1,5 +1,11 @@
@import "../../global_variables";
-
+.collectionfreeformview-measure {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ }
.collectionfreeformview {
position: absolute;
top: 0;
@@ -26,7 +32,7 @@
border: 0px solid $light-color-secondary;
border-radius: $border-radius;
box-sizing: border-box;
- position: relative;
+ position: absolute;
overflow: hidden;
top: 0;
left: 0;
@@ -41,12 +47,12 @@
.formattedTextBox-cont {
background: $light-color-secondary;
}
-
+
opacity: 0.99;
border: 0px solid transparent;
border-radius: $border-radius;
box-sizing: border-box;
- position:relative;
+ position:absolute;
overflow: hidden;
top: 0;
left: 0;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index c5178f69d..9914f3793 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1,4 +1,4 @@
-import { action, computed, observable, trace } from "mobx";
+import { action, computed, observable, trace, ObservableSet, runInAction } from "mobx";
import { observer } from "mobx-react";
import { Document } from "../../../../fields/Document";
import { FieldWaiting } from "../../../../fields/Field";
@@ -11,8 +11,8 @@ import { InkingCanvas } from "../../InkingCanvas";
import { CollectionFreeFormDocumentView } from "../../nodes/CollectionFreeFormDocumentView";
import { DocumentContentsView } from "../../nodes/DocumentContentsView";
import { DocumentViewProps } from "../../nodes/DocumentView";
-import { COLLECTION_BORDER_WIDTH } from "../CollectionView";
-import { CollectionViewBase } from "../CollectionViewBase";
+import { COLLECTION_BORDER_WIDTH } from "../CollectionBaseView";
+import { CollectionSubView } from "../CollectionSubView";
import { CollectionFreeFormLinksView } from "./CollectionFreeFormLinksView";
import "./CollectionFreeFormView.scss";
import { MarqueeView } from "./MarqueeView";
@@ -20,9 +20,14 @@ import React = require("react");
import v5 = require("uuid/v5");
import { CollectionFreeFormRemoteCursors } from "./CollectionFreeFormRemoteCursors";
import { PreviewCursor } from "./PreviewCursor";
+import { DocumentManager } from "../../../util/DocumentManager";
+import { SelectionManager } from "../../../util/SelectionManager";
+import { NumberField } from "../../../../fields/NumberField";
+import { Main } from "../../Main";
+import Measure from "react-measure";
@observer
-export class CollectionFreeFormView extends CollectionViewBase {
+export class CollectionFreeFormView extends CollectionSubView {
public _canvasRef = React.createRef<HTMLDivElement>();
private _selectOnLoaded: string = ""; // id of document that should be selected once it's loaded (used for click-to-type)
@@ -39,15 +44,20 @@ export class CollectionFreeFormView extends CollectionViewBase {
}
public selectDocuments = (docs: Document[]) => {
- this.props.CollectionView.SelectedDocs.length = 0;
- docs.map(d => this.props.CollectionView.SelectedDocs.push(d.Id));
+ SelectionManager.DeselectAll;
+ docs.map(doc => {
+ const dv = DocumentManager.Instance.getDocumentView(doc);
+ if (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) {
+ if (page === curPage || page === -1) {
active.push(doc);
}
return active;
@@ -58,39 +68,50 @@ export class CollectionFreeFormView extends CollectionViewBase {
@observable public DownY: number = 0;
@observable private _lastX: number = 0;
@observable private _lastY: number = 0;
+ @observable private _pwidth: number = 0;
+ @observable private _pheight: number = 0;
- @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 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); }
@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 nativeWidth() { return this.props.Document.GetNumber(KeyStore.NativeWidth, 0); }
@computed get nativeHeight() { return this.props.Document.GetNumber(KeyStore.NativeHeight, 0); }
@computed get zoomScaling() { return this.props.Document.GetNumber(KeyStore.Scale, 1); }
- @computed get centeringShiftX() { return !this.props.Document.GetNumber(KeyStore.NativeWidth, 0) ? this.props.panelWidth() / 2 : 0; } // shift so pan position is at center of window for non-overlay collections
- @computed get centeringShiftY() { return !this.props.Document.GetNumber(KeyStore.NativeHeight, 0) ? this.props.panelHeight() / 2 : 0; }// shift so pan position is at center of window for non-overlay collections
+ @computed get centeringShiftX() { return !this.props.Document.GetNumber(KeyStore.NativeWidth, 0) ? this._pwidth / 2 : 0; } // shift so pan position is at center of window for non-overlay collections
+ @computed get centeringShiftY() { return !this.props.Document.GetNumber(KeyStore.NativeHeight, 0) ? this._pheight / 2 : 0; }// shift so pan position is at center of window for non-overlay collections
@undoBatch
@action
drop = (e: Event, de: DragManager.DropEvent) => {
if (super.drop(e, de)) {
if (de.data instanceof DragManager.DocumentDragData) {
- let screenX = de.x - (de.data.xOffset as number || 0);
- let screenY = de.y - (de.data.yOffset as number || 0);
- const [x, y] = this.getTransform().transformPoint(screenX, screenY);
- let dragDoc = de.data.draggedDocuments[0];
- let dragX = dragDoc.GetNumber(KeyStore.X, 0);
- let dragY = dragDoc.GetNumber(KeyStore.Y, 0);
- de.data.draggedDocuments.map(d => {
- let docX = d.GetNumber(KeyStore.X, 0);
- let docY = d.GetNumber(KeyStore.Y, 0);
- d.SetNumber(KeyStore.X, x + (docX - dragX));
- d.SetNumber(KeyStore.Y, y + (docY - dragY));
- if (!d.GetNumber(KeyStore.Width, 0)) {
- d.SetNumber(KeyStore.Width, 300);
- d.SetNumber(KeyStore.Height, 300);
- }
- this.bringToFront(d);
- })
+ let droppedDocs = de.data.droppedDocuments;
+ let xoff = de.data.xOffset as number || 0;
+ let yoff = de.data.yOffset as number || 0;
+ if (droppedDocs.length) {
+ let screenX = de.x - xoff;
+ let screenY = de.y - yoff;
+ const [x, y] = this.getTransform().transformPoint(screenX, screenY);
+ let dragDoc = droppedDocs[0];
+ let dragX = dragDoc.GetNumber(KeyStore.X, 0);
+ let dragY = dragDoc.GetNumber(KeyStore.Y, 0);
+ droppedDocs.map(async d => {
+ let docX = d.GetNumber(KeyStore.X, 0);
+ let docY = d.GetNumber(KeyStore.Y, 0);
+ d.SetNumber(KeyStore.X, x + (docX - dragX));
+ d.SetNumber(KeyStore.Y, y + (docY - dragY));
+ let docW = await d.GetTAsync(KeyStore.Width, NumberField);
+ let docH = await d.GetTAsync(KeyStore.Height, NumberField);
+ if (!docW) {
+ d.SetNumber(KeyStore.Width, 300);
+ }
+ if (!docH) {
+ d.SetNumber(KeyStore.Height, 300);
+ }
+ this.bringToFront(d);
+ });
+ }
}
return true;
}
@@ -106,15 +127,16 @@ export class CollectionFreeFormView extends CollectionViewBase {
@action
onPointerDown = (e: React.PointerEvent): void => {
- if (((e.button === 2 && (!this.isAnnotationOverlay || this.zoomScaling != 1)) || e.button == 0) && this.props.active()) {
+ if (((e.button === 2 && (!this.isAnnotationOverlay || this.zoomScaling !== 1)) || e.button === 0) && this.props.active()) {
document.removeEventListener("pointermove", this.onPointerMove);
document.addEventListener("pointermove", this.onPointerMove);
document.removeEventListener("pointerup", this.onPointerUp);
document.addEventListener("pointerup", this.onPointerUp);
this._lastX = this.DownX = e.pageX;
this._lastY = this.DownY = e.pageY;
- if (this.props.isSelected())
+ if (this.props.isSelected()) {
e.stopPropagation();
+ }
}
}
@@ -128,7 +150,7 @@ export class CollectionFreeFormView extends CollectionViewBase {
@action
onPointerMove = (e: PointerEvent): void => {
if (!e.cancelBubble && this.props.active()) {
- if ((!this.isAnnotationOverlay || this.zoomScaling != 1) && !e.shiftKey) {
+ if ((!this.isAnnotationOverlay || this.zoomScaling !== 1) && !e.shiftKey) {
let x = this.props.Document.GetNumber(KeyStore.PanX, 0);
let y = this.props.Document.GetNumber(KeyStore.PanY, 0);
let [dx, dy] = this.getTransform().transformDirection(e.clientX - this._lastX, e.clientY - this._lastY);
@@ -157,17 +179,18 @@ export class CollectionFreeFormView extends CollectionViewBase {
e.stopPropagation();
e.preventDefault();
} else {
- // if (modes[e.deltaMode] == 'pixels') coefficient = 50;
- // else if (modes[e.deltaMode] == 'lines') coefficient = 1000; // This should correspond to line-height??
+ // if (modes[e.deltaMode] === 'pixels') coefficient = 50;
+ // else if (modes[e.deltaMode] === 'lines') coefficient = 1000; // This should correspond to line-height??
let transform = this.getTransform();
let deltaScale = (1 - (e.deltaY / coefficient));
- if (deltaScale * this.zoomScaling < 1 && this.isAnnotationOverlay)
+ if (deltaScale * this.zoomScaling < 1 && this.isAnnotationOverlay) {
deltaScale = 1 / this.zoomScaling;
+ }
let [x, y] = transform.transformPoint(e.clientX, e.clientY);
- let localTransform = this.getLocalTransform()
- localTransform = localTransform.inverse().scaleAbout(deltaScale, x, y)
+ let localTransform = this.getLocalTransform();
+ localTransform = localTransform.inverse().scaleAbout(deltaScale, x, y);
// console.log(localTransform)
this.props.Document.SetNumber(KeyStore.Scale, localTransform.Scale);
@@ -177,6 +200,7 @@ export class CollectionFreeFormView extends CollectionViewBase {
@action
private SetPan(panX: number, panY: number) {
+ Main.Instance.SetTextDoc(undefined, undefined);
var x1 = this.getLocalTransform().inverse().Scale;
const newPanX = Math.min((1 - 1 / x1) * this.nativeWidth, Math.max(0, panX));
const newPanY = Math.min((1 - 1 / x1) * this.nativeHeight, Math.max(0, panY));
@@ -207,7 +231,7 @@ export class CollectionFreeFormView extends CollectionViewBase {
}
return doc1.GetNumber(KeyStore.ZIndex, 0) - doc2.GetNumber(KeyStore.ZIndex, 0);
}).map((doc, index) => {
- doc.SetNumber(KeyStore.ZIndex, index + 1)
+ doc.SetNumber(KeyStore.ZIndex, index + 1);
});
}
@@ -234,17 +258,20 @@ export class CollectionFreeFormView extends CollectionViewBase {
getDocumentViewProps(document: Document): DocumentViewProps {
return {
Document: document,
- AddDocument: this.props.addDocument,
- RemoveDocument: this.props.removeDocument,
+ addDocument: this.props.addDocument,
+ removeDocument: this.props.removeDocument,
+ moveDocument: this.props.moveDocument,
ScreenToLocalTransform: this.getTransform,
isTopMost: false,
- SelectOnLoad: document.Id == this._selectOnLoaded,
+ selectOnLoad: document.Id === this._selectOnLoaded,
PanelWidth: document.Width,
PanelHeight: document.Height,
ContentScaling: this.noScaling,
- ContainingCollectionView: this.props.CollectionView,
- focus: this.focusDocument
- }
+ ContainingCollectionView: undefined,
+ focus: this.focusDocument,
+ parentActive: this.props.active,
+ onActiveChanged: this.props.active,
+ };
}
@computed
@@ -252,10 +279,11 @@ export class CollectionFreeFormView extends CollectionViewBase {
var curPage = this.props.Document.GetNumber(KeyStore.CurPage, -1);
return this.props.Document.GetList(this.props.fieldKey, [] as Document[]).filter(doc => doc).reduce((prev, doc) => {
var page = doc.GetNumber(KeyStore.Page, -1);
- if (page == curPage || page == -1)
+ if (page === curPage || page === -1) {
prev.push(<CollectionFreeFormDocumentView key={doc.Id} {...this.getDocumentViewProps(doc)} />);
+ }
return prev;
- }, [] as JSX.Element[])
+ }, [] as JSX.Element[]);
}
@computed
@@ -271,9 +299,9 @@ export class CollectionFreeFormView extends CollectionViewBase {
layoutKey={KeyStore.OverlayLayout} isTopMost={this.props.isTopMost} isSelected={() => false} select={() => { }} />);
}
- getTransform = (): Transform => this.props.ScreenToLocalTransform().translate(-COLLECTION_BORDER_WIDTH, -COLLECTION_BORDER_WIDTH).translate(-this.centeringShiftX, -this.centeringShiftY).transform(this.getLocalTransform())
- getContainerTransform = (): Transform => this.props.ScreenToLocalTransform().translate(-COLLECTION_BORDER_WIDTH, -COLLECTION_BORDER_WIDTH)
- getLocalTransform = (): Transform => Transform.Identity.scale(1 / this.scale).translate(this.panX, this.panY);
+ getTransform = (): Transform => this.props.ScreenToLocalTransform().translate(-COLLECTION_BORDER_WIDTH, -COLLECTION_BORDER_WIDTH).translate(-this.centeringShiftX, -this.centeringShiftY).transform(this.getLocalTransform());
+ getContainerTransform = (): Transform => this.props.ScreenToLocalTransform().translate(-COLLECTION_BORDER_WIDTH, -COLLECTION_BORDER_WIDTH);
+ getLocalTransform = (): Transform => Transform.Identity().scale(1 / this.scale).translate(this.panX, this.panY);
noScaling = () => 1;
childViews = () => this.views;
@@ -284,29 +312,34 @@ export class CollectionFreeFormView extends CollectionViewBase {
const pany: number = -this.props.Document.GetNumber(KeyStore.PanY, 0);
return (
- <div className={`collectionfreeformview${this.isAnnotationOverlay ? "-overlay" : "-container"}`}
- onPointerDown={this.onPointerDown} onPointerMove={(e) => super.setCursorPosition(this.getTransform().transformPoint(e.clientX, e.clientY))}
- onDrop={this.onDrop.bind(this)} onDragOver={this.onDragOver} onWheel={this.onPointerWheel}
- style={{ borderWidth: `${COLLECTION_BORDER_WIDTH}px` }} ref={this.createDropTarget}>
- <MarqueeView container={this} activeDocuments={this.getActiveDocuments} selectDocuments={this.selectDocuments}
- addDocument={this.addDocument} removeDocument={this.props.removeDocument}
- getContainerTransform={this.getContainerTransform} getTransform={this.getTransform}>
- <PreviewCursor container={this} addLiveTextDocument={this.addLiveTextBox}
- getContainerTransform={this.getContainerTransform} getTransform={this.getTransform} >
- <div className="collectionfreeformview" ref={this._canvasRef}
- style={{ transform: `translate(${dx}px, ${dy}px) scale(${this.zoomScaling}, ${this.zoomScaling}) translate(${panx}px, ${pany}px)` }}>
- {this.backgroundView}
- <CollectionFreeFormLinksView {...this.props}>
- <InkingCanvas getScreenTransform={this.getTransform} Document={this.props.Document} >
- {this.childViews}
- </InkingCanvas>
- </CollectionFreeFormLinksView>
- <CollectionFreeFormRemoteCursors {...this.props} />
+ <Measure onResize={(r: any) => runInAction(() => { this._pwidth = r.entry.width; this._pheight = r.entry.height; })}>
+ {({ measureRef }) => (
+ <div className={`collectionfreeformview-measure`} ref={measureRef}>
+ <div className={`collectionfreeformview${this.isAnnotationOverlay ? "-overlay" : "-container"}`}
+ onPointerDown={this.onPointerDown} onPointerMove={(e) => super.setCursorPosition(this.getTransform().transformPoint(e.clientX, e.clientY))}
+ onDrop={this.onDrop.bind(this)} onDragOver={this.onDragOver} onWheel={this.onPointerWheel}
+ style={{ borderWidth: `${COLLECTION_BORDER_WIDTH}px` }} ref={this.createDropTarget}>
+ <MarqueeView container={this} activeDocuments={this.getActiveDocuments} selectDocuments={this.selectDocuments}
+ addDocument={this.addDocument} removeDocument={this.props.removeDocument}
+ getContainerTransform={this.getContainerTransform} getTransform={this.getTransform}>
+ <PreviewCursor container={this} addLiveTextDocument={this.addLiveTextBox}
+ getContainerTransform={this.getContainerTransform} getTransform={this.getTransform} >
+ <div className="collectionfreeformview" ref={this._canvasRef}
+ style={{ transform: `translate(${dx}px, ${dy}px) scale(${this.zoomScaling}, ${this.zoomScaling}) translate(${panx}px, ${pany}px)` }}>
+ {this.backgroundView}
+ <CollectionFreeFormLinksView {...this.props}>
+ <InkingCanvas getScreenTransform={this.getTransform} Document={this.props.Document} >
+ {this.childViews}
+ </InkingCanvas>
+ </CollectionFreeFormLinksView>
+ <CollectionFreeFormRemoteCursors {...this.props} />
+ </div>
+ {this.overlayView}
+ </PreviewCursor>
+ </MarqueeView>
</div>
- {this.overlayView}
- </PreviewCursor>
- </MarqueeView>
- </div>
+ </div>)}
+ </Measure>
);
}
} \ No newline at end of file
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.scss b/src/client/views/collections/collectionFreeForm/MarqueeView.scss
index 1ee3b244b..0b406e722 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.scss
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.scss
@@ -1,6 +1,8 @@
.marqueeView {
position: absolute;
+ top:0;
+ left:0;
width:100%;
height:100%;
}
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 20132a4b1..1e6faafb3 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -37,7 +37,7 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
@action
cleanupInteractions = (all: boolean = false) => {
if (all) {
- document.removeEventListener("pointermove", this.onPointerMove, true)
+ document.removeEventListener("pointermove", this.onPointerMove, true);
document.removeEventListener("pointerup", this.onPointerUp, true);
} else {
this._used = true;
@@ -48,11 +48,11 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
@action
onPointerDown = (e: React.PointerEvent): void => {
- if (e.buttons == 1 && !e.altKey && !e.metaKey && this.props.container.props.active()) {
+ if (e.buttons === 1 && !e.altKey && !e.metaKey && this.props.container.props.active()) {
this._downX = this._lastX = e.pageX;
this._downY = this._lastY = e.pageY;
this._used = false;
- document.addEventListener("pointermove", this.onPointerMove, true)
+ document.addEventListener("pointermove", this.onPointerMove, true);
document.addEventListener("pointerup", this.onPointerUp, true);
document.addEventListener("keydown", this.marqueeCommand, true);
}
@@ -63,7 +63,7 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
this._lastX = e.pageX;
this._lastY = e.pageY;
if (!e.cancelBubble) {
- if (!this._used && e.buttons == 1 && !e.altKey && !e.metaKey &&
+ if (!this._used && e.buttons === 1 && !e.altKey && !e.metaKey &&
(Math.abs(this._lastX - this._downX) > MarqueeView.DRAG_THRESHOLD || Math.abs(this._lastY - this._downY) > MarqueeView.DRAG_THRESHOLD)) {
this._visible = true;
}
@@ -94,20 +94,20 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
let top = this._downY < this._lastY ? this._downY : this._lastY;
let topLeft = this.props.getTransform().transformPoint(left, top);
let size = this.props.getTransform().transformDirection(this._lastX - this._downX, this._lastY - this._downY);
- return { left: topLeft[0], top: topLeft[1], width: Math.abs(size[0]), height: Math.abs(size[1]) }
+ return { left: topLeft[0], top: topLeft[1], width: Math.abs(size[0]), height: Math.abs(size[1]) };
}
@action
marqueeCommand = (e: KeyboardEvent) => {
- if (e.key == "Backspace" || e.key == "Delete") {
+ if (e.key === "Backspace" || e.key === "Delete") {
this.marqueeSelect().map(d => this.props.removeDocument(d));
let ink = this.props.container.props.Document.GetT(KeyStore.Ink, InkField);
- if (ink && ink != FieldWaiting && ink.Data) {
+ if (ink && ink !== FieldWaiting) {
this.marqueeInkDelete(ink.Data);
}
this.cleanupInteractions();
}
- if (e.key == "c") {
+ if (e.key === "c") {
let bounds = this.Bounds;
let selected = this.marqueeSelect().map(d => {
this.props.removeDocument(d);
@@ -118,24 +118,24 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
return d;
});
let ink = this.props.container.props.Document.GetT(KeyStore.Ink, InkField);
- if (ink && ink != FieldWaiting && ink.Data) {
- //setTimeout(() => {
- let newCollection = Documents.FreeformDocument(selected, {
- x: bounds.left,
- y: bounds.top,
- panx: 0,
- pany: 0,
- width: bounds.width,
- height: bounds.height,
- backgroundColor: "Transparent",
- ink: this.marqueeInkSelect(ink.Data),
- title: "a nested collection"
- });
- this.props.addDocument(newCollection, false);
- this.marqueeInkDelete(ink.Data);
- }
+ let inkData = ink && ink !== FieldWaiting ? ink.Data : undefined;
+ //setTimeout(() => {
+ let newCollection = Documents.FreeformDocument(selected, {
+ x: bounds.left,
+ y: bounds.top,
+ panx: 0,
+ pany: 0,
+ width: bounds.width,
+ height: bounds.height,
+ backgroundColor: "Transparent",
+ ink: inkData ? this.marqueeInkSelect(inkData) : undefined,
+ title: "a nested collection"
+ });
+ this.props.addDocument(newCollection, false);
+ this.marqueeInkDelete(inkData);
// }, 100);
this.cleanupInteractions();
+ SelectionManager.DeselectAll();
}
}
@action
@@ -147,7 +147,7 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
if (InkingCanvas.IntersectStrokeRect(value, this.Bounds)) {
idata.set(key,
{
- pathData: value.pathData.map(val => { return { x: val.x + centerShiftX, y: val.y + centerShiftY } }),
+ pathData: value.pathData.map(val => ({ x: val.x + centerShiftX, y: val.y + centerShiftY })),
color: value.color,
width: value.width,
tool: value.tool,
@@ -159,15 +159,17 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
}
@action
- marqueeInkDelete(ink: Map<any, any>, ) {
+ marqueeInkDelete(ink?: Map<any, any>) {
// bcz: this appears to work but when you restart all the deleted strokes come back -- InkField isn't observing its changes so they aren't written to the DB.
// ink.forEach((value: StrokeData, key: string, map: any) =>
// InkingCanvas.IntersectStrokeRect(value, this.Bounds) && ink.delete(key));
- let idata = new Map();
- ink.forEach((value: StrokeData, key: string, map: any) =>
- !InkingCanvas.IntersectStrokeRect(value, this.Bounds) && idata.set(key, value));
- this.props.container.props.Document.SetDataOnPrototype(KeyStore.Ink, idata, InkField);
+ if (ink) {
+ let idata = new Map();
+ ink.forEach((value: StrokeData, key: string, map: any) =>
+ !InkingCanvas.IntersectStrokeRect(value, this.Bounds) && idata.set(key, value));
+ this.props.container.props.Document.SetDataOnPrototype(KeyStore.Ink, idata, InkField);
+ }
}
marqueeSelect() {
@@ -178,9 +180,10 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
var y = doc.GetNumber(KeyStore.Y, 0);
var w = doc.GetNumber(KeyStore.Width, 0);
var h = doc.GetNumber(KeyStore.Height, 0);
- if (this.intersectRect({ left: x, top: y, width: w, height: h }, selRect))
- selection.push(doc)
- })
+ if (this.intersectRect({ left: x, top: y, width: w, height: h }, selRect)) {
+ selection.push(doc);
+ }
+ });
return selection;
}
@@ -188,7 +191,7 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
get marqueeDiv() {
let p = this.props.getContainerTransform().transformPoint(this._downX < this._lastX ? this._downX : this._lastX, this._downY < this._lastY ? this._downY : this._lastY);
let v = this.props.getContainerTransform().transformDirection(this._lastX - this._downX, this._lastY - this._downY);
- return <div className="marquee" style={{ transform: `translate(${p[0]}px, ${p[1]}px)`, width: `${Math.abs(v[0])}`, height: `${Math.abs(v[1])}` }} />
+ return <div className="marquee" style={{ transform: `translate(${p[0]}px, ${p[1]}px)`, width: `${Math.abs(v[0])}`, height: `${Math.abs(v[1])}` }} />;
}
render() {
diff --git a/src/client/views/collections/collectionFreeForm/PreviewCursor.scss b/src/client/views/collections/collectionFreeForm/PreviewCursor.scss
index 21210be2b..7a67c29bf 100644
--- a/src/client/views/collections/collectionFreeForm/PreviewCursor.scss
+++ b/src/client/views/collections/collectionFreeForm/PreviewCursor.scss
@@ -3,9 +3,13 @@
color: black;
position: absolute;
transform-origin: left top;
+ top: 0;
+ left:0;
pointer-events: none;
}
.previewCursorView {
+ top: 0;
+ left:0;
position: absolute;
width:100%;
height:100%;
diff --git a/src/client/views/collections/collectionFreeForm/PreviewCursor.tsx b/src/client/views/collections/collectionFreeForm/PreviewCursor.tsx
index 93c98f7b0..8eabb020a 100644
--- a/src/client/views/collections/collectionFreeForm/PreviewCursor.tsx
+++ b/src/client/views/collections/collectionFreeForm/PreviewCursor.tsx
@@ -33,7 +33,7 @@ export class PreviewCursor extends React.Component<PreviewCursorProps> {
@action
onPointerDown = (e: React.PointerEvent) => {
- if (e.button == 0 && this.props.container.props.active()) {
+ if (e.button === 0 && this.props.container.props.active()) {
document.removeEventListener("keypress", this.onKeyPress, false);
this._showOnUp = true;
this.DownX = e.pageX;
@@ -90,7 +90,7 @@ export class PreviewCursor extends React.Component<PreviewCursorProps> {
{this.props.children}
<PreviewCursorPrompt setVisible={this.setVisible} getPoint={this.getPoint} getVisible={this.getVisible} />
</div>
- )
+ );
}
}
@@ -109,8 +109,9 @@ export class PreviewCursorPrompt extends React.Component<PromptProps> {
render() {
let p = this.props.getPoint();
- if (this.props.getVisible() && this._promptRef.current)
+ if (this.props.getVisible() && this._promptRef.current) {
this._promptRef.current.focus();
+ }
return <div className="previewCursor" id="previewCursor" onBlur={this.onBlur} tabIndex={0} ref={this._promptRef}
style={{ transform: `translate(${p[0]}px, ${p[1]}px)`, opacity: this.props.getVisible() ? 1 : 0 }}>
I