aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorlaurawilsonri <laura_wilson@brown.edu>2019-04-08 19:10:03 -0400
committerlaurawilsonri <laura_wilson@brown.edu>2019-04-08 19:10:03 -0400
commit74411165f21b6c616fa98e0676c6f4568c2d4564 (patch)
tree38079f721f8cdeba25e7bc0439aa5d6523927f1c
parent5d4414e8d2a17c75c808bce9343dba85fbb32440 (diff)
parenta72fcdd0ebc06a3c851007c6ed89ab13a9a0d835 (diff)
Merge branch 'master' of https://github.com/browngraphicslab/Dash-Web into richTextEditor
-rw-r--r--.vscode/settings.json3
-rw-r--r--package.json3
-rw-r--r--src/client/documents/Documents.ts4
-rw-r--r--src/client/northstar/dash-fields/HistogramField.ts2
-rw-r--r--src/client/northstar/dash-nodes/HistogramBox.scss10
-rw-r--r--src/client/northstar/dash-nodes/HistogramBox.tsx2
-rw-r--r--src/client/northstar/dash-nodes/HistogramBoxPrimitives.scss16
-rw-r--r--src/client/northstar/dash-nodes/HistogramBoxPrimitives.tsx81
-rw-r--r--src/client/northstar/operations/HistogramOperation.ts4
-rw-r--r--src/client/util/DocumentManager.ts2
-rw-r--r--src/client/util/DragManager.ts229
-rw-r--r--src/client/util/Scripting.ts97
-rw-r--r--src/client/util/SelectionManager.ts18
-rw-r--r--src/client/util/TooltipTextMenu.tsx288
-rw-r--r--src/client/util/Transform.ts14
-rw-r--r--src/client/util/UndoManager.ts4
-rw-r--r--src/client/views/DocumentDecorations.scss86
-rw-r--r--src/client/views/DocumentDecorations.tsx270
-rw-r--r--src/client/views/InkingCanvas.scss22
-rw-r--r--src/client/views/InkingCanvas.tsx56
-rw-r--r--src/client/views/InkingStroke.tsx9
-rw-r--r--src/client/views/Main.scss30
-rw-r--r--src/client/views/Main.tsx125
-rw-r--r--src/client/views/collections/CollectionDockingView.scss2
-rw-r--r--src/client/views/collections/CollectionDockingView.tsx2
-rw-r--r--src/client/views/collections/CollectionPDFView.scss2
-rw-r--r--src/client/views/collections/CollectionSchemaView.tsx24
-rw-r--r--src/client/views/collections/CollectionTreeView.scss86
-rw-r--r--src/client/views/collections/CollectionTreeView.tsx11
-rw-r--r--src/client/views/collections/CollectionVideoView.scss2
-rw-r--r--src/client/views/collections/CollectionViewBase.tsx29
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx8
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.scss2
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx88
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss6
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx22
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeView.scss2
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeView.tsx46
-rw-r--r--src/client/views/collections/collectionFreeForm/PreviewCursor.scss4
-rw-r--r--src/client/views/nodes/CollectionFreeFormDocumentView.tsx4
-rw-r--r--src/client/views/nodes/DocumentView.scss20
-rw-r--r--src/client/views/nodes/DocumentView.tsx365
-rw-r--r--src/client/views/nodes/FormattedTextBox.tsx151
-rw-r--r--src/client/views/nodes/KeyValueBox.tsx6
-rw-r--r--src/client/views/nodes/KeyValuePair.tsx6
-rw-r--r--src/client/views/nodes/PDFBox.scss4
-rw-r--r--src/client/views/nodes/PDFBox.tsx30
-rw-r--r--src/client/views/nodes/Sticky.tsx138
-rw-r--r--src/client/views/nodes/WebBox.scss2
-rw-r--r--src/fields/BooleanField.ts25
-rw-r--r--src/fields/Document.ts183
-rw-r--r--src/fields/KeyStore.ts19
-rw-r--r--src/fields/NumberField.ts2
-rw-r--r--src/server/Message.ts86
-rw-r--r--src/server/ServerUtil.ts94
-rw-r--r--src/server/database.ts46
-rw-r--r--src/server/public/files/.gitignore1
-rw-r--r--src/server/public/files/upload_a6a70d84ebb65febf7900e29f52cc86d.pdfbin0 -> 1043556 bytes
-rw-r--r--src/server/public/files/upload_e72669595eae4384a2a32196496f4f05.pdfbin548616 -> 0 bytes
-rw-r--r--tsconfig.json2
60 files changed, 1887 insertions, 1008 deletions
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 081b05b38..fc315ffaf 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -7,6 +7,7 @@
"**/.DS_Store": true,
},
"editor.formatOnSave": true,
+ "editor.detectIndentation": false,
"typescript.format.insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets": false,
"typescript.format.insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces": true
-}
+} \ No newline at end of file
diff --git a/package.json b/package.json
index 7bcf941b7..135bd4a91 100644
--- a/package.json
+++ b/package.json
@@ -25,7 +25,7 @@
"scss-loader": "0.0.1",
"style-loader": "^0.23.1",
"ts-node": "^7.0.1",
- "typescript": "^3.3.3333",
+ "typescript": "^3.4.1",
"webpack": "^4.29.6",
"webpack-cli": "^3.2.3",
"webpack-dev-middleware": "^3.6.1",
@@ -106,6 +106,7 @@
"express-validator": "^5.3.1",
"expressjs": "^1.0.1",
"flexlayout-react": "^0.3.3",
+ "font-awesome": "^4.7.0",
"formidable": "^1.2.1",
"golden-layout": "^1.5.9",
"html-to-image": "^0.1.0",
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 0bf275df8..7d2f9cde1 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -43,6 +43,7 @@ export interface DocumentOptions {
layoutKeys?: Key[];
viewType?: number;
backgroundColor?: string;
+ copyDraggedItems?: boolean;
}
export namespace Documents {
@@ -86,6 +87,7 @@ export namespace Documents {
if (options.ink !== undefined) { doc.Set(KeyStore.Ink, new InkField(options.ink)); }
if (options.layout !== undefined) { doc.SetText(KeyStore.Layout, options.layout); }
if (options.layoutKeys !== undefined) { doc.Set(KeyStore.LayoutKeys, new ListField(options.layoutKeys)); }
+ if (options.copyDraggedItems !== undefined) { doc.SetBoolean(KeyStore.CopyDraggedItems, options.copyDraggedItems); }
return doc;
}
@@ -136,7 +138,7 @@ export namespace Documents {
function GetPdfPrototype(): Document {
if (!pdfProto) {
pdfProto = setupPrototypeOptions(pdfProtoId, "PDF_PROTO", CollectionPDFView.LayoutString("AnnotationsKey"),
- { x: 0, y: 0, nativeWidth: 600, width: 300, layoutKeys: [KeyStore.Data, KeyStore.Annotations] });
+ { x: 0, y: 0, nativeWidth: 1200, width: 300, layoutKeys: [KeyStore.Data, KeyStore.Annotations] });
pdfProto.SetNumber(KeyStore.CurPage, 1);
pdfProto.SetText(KeyStore.BackgroundLayout, PDFBox.LayoutString());
}
diff --git a/src/client/northstar/dash-fields/HistogramField.ts b/src/client/northstar/dash-fields/HistogramField.ts
index 90be70b80..48731f52d 100644
--- a/src/client/northstar/dash-fields/HistogramField.ts
+++ b/src/client/northstar/dash-fields/HistogramField.ts
@@ -27,7 +27,7 @@ export class HistogramField extends BasicField<HistogramOperation> {
}
Copy(): Field {
- return new HistogramField(this.Data);
+ return new HistogramField(this.Data.Copy());
}
ToScriptString(): string {
diff --git a/src/client/northstar/dash-nodes/HistogramBox.scss b/src/client/northstar/dash-nodes/HistogramBox.scss
index b11840a65..e899cf15e 100644
--- a/src/client/northstar/dash-nodes/HistogramBox.scss
+++ b/src/client/northstar/dash-nodes/HistogramBox.scss
@@ -1,6 +1,8 @@
.histogrambox-container {
padding: 0vw;
position: absolute;
+ top: 0;
+ left:0;
text-align: center;
width: 100%;
height: 100%;
@@ -8,6 +10,7 @@
}
.histogrambox-xaxislabel {
position:absolute;
+ left:0;
width:100%;
text-align: center;
bottom:0;
@@ -19,14 +22,17 @@
position:absolute;
height:100%;
width: 25px;
+ left:0;
bottom:0;
background: lightgray;
}
.histogrambox-yaxislabel-text {
position:absolute;
- transform-origin: left;
+ left:0;
+ width: 1000px;
+ transform-origin: 10px 10px;
transform: rotate(-90deg);
- text-align: center;
+ text-align: left;
font-size: 14;
font-weight: bold;
bottom: calc(50% - 25px);
diff --git a/src/client/northstar/dash-nodes/HistogramBox.tsx b/src/client/northstar/dash-nodes/HistogramBox.tsx
index 49ebe5ebc..dd6e09900 100644
--- a/src/client/northstar/dash-nodes/HistogramBox.tsx
+++ b/src/client/northstar/dash-nodes/HistogramBox.tsx
@@ -148,7 +148,7 @@ export class HistogramBox extends React.Component<FieldViewProps> {
return (
<Measure onResize={(r: any) => runInAction(() => { this.PanelWidth = r.entry.width; this.PanelHeight = r.entry.height })}>
{({ measureRef }) =>
- <div className="histogrambox-container" ref={measureRef} style={{ transform: `translate(${-w / 2}px, ${-h / 2}px)` }}>
+ <div className="histogrambox-container" ref={measureRef} style={{ transform: `translate(-50%, -50%)` }}>
<div className="histogrambox-yaxislabel" onPointerDown={this.yLabelPointerDown} ref={this._dropYRef} >
<span className="histogrambox-yaxislabel-text">
{labelY}
diff --git a/src/client/northstar/dash-nodes/HistogramBoxPrimitives.scss b/src/client/northstar/dash-nodes/HistogramBoxPrimitives.scss
index ce9edd65e..26203612a 100644
--- a/src/client/northstar/dash-nodes/HistogramBoxPrimitives.scss
+++ b/src/client/northstar/dash-nodes/HistogramBoxPrimitives.scss
@@ -4,10 +4,11 @@
}
.histogramboxprimitives-border {
border: 3px;
- border-style: solid;
- border-color: white;
pointer-events: none;
position: absolute;
+ fill:"transparent";
+ stroke: white;
+ stroke-width: 1px;
}
.histogramboxprimitives-bar {
position: absolute;
@@ -23,8 +24,19 @@
width: 100%;
height: 100%;
}
+.histogramboxprimitives-svgContainer {
+ position: absolute;
+ top:0;
+ left:0;
+ width:100%;
+ height: 100%;
+}
.histogramboxprimitives-line {
position: absolute;
background: darkGray;
+ stroke: darkGray;
+ stroke-width: 1px;
+ width:100%;
+ height:100%;
opacity: 0.4;
} \ No newline at end of file
diff --git a/src/client/northstar/dash-nodes/HistogramBoxPrimitives.tsx b/src/client/northstar/dash-nodes/HistogramBoxPrimitives.tsx
index e9adb3ce5..0918bc0c4 100644
--- a/src/client/northstar/dash-nodes/HistogramBoxPrimitives.tsx
+++ b/src/client/northstar/dash-nodes/HistogramBoxPrimitives.tsx
@@ -1,5 +1,5 @@
import React = require("react")
-import { computed, observable, reaction, runInAction, trace } from "mobx";
+import { computed, observable, reaction, runInAction, trace, action } from "mobx";
import { observer } from "mobx-react";
import { Utils as DashUtils } from '../../../Utils';
import { FilterModel } from "../../northstar/core/filter/FilterModel";
@@ -11,6 +11,9 @@ import { StyleConstants } from "../../northstar/utils/StyleContants";
import { HistogramBinPrimitiveCollection, HistogramBinPrimitive } from "./HistogramBinPrimitiveCollection";
import { HistogramBox } from "./HistogramBox";
import "./HistogramBoxPrimitives.scss";
+import { JSXElement } from "babel-types";
+import { Utils } from "../utils/Utils";
+import { all } from "bluebird";
export interface HistogramPrimitivesProps {
HistoBox: HistogramBox;
@@ -23,20 +26,19 @@ export class HistogramBoxPrimitives extends React.Component<HistogramPrimitivesP
@computed get xaxislines() { return this.renderGridLinesAndLabels(0); }
@computed get yaxislines() { return this.renderGridLinesAndLabels(1); }
@computed get selectedPrimitives() { return this._selectedPrims.map(bp => this.drawRect(bp.Rect, bp.BarAxis, undefined, "border")); }
- @computed get binPrimitives() {
+ @computed get barPrimitives() {
let histoResult = this.props.HistoBox.HistogramResult;
if (!histoResult || !histoResult.bins || !this.props.HistoBox.VisualBinRanges.length)
return (null);
- trace();
let allBrushIndex = ModelHelpers.AllBrushIndex(histoResult);
- return Object.keys(histoResult.bins).reduce((prims, key) => {
+ return Object.keys(histoResult.bins).reduce((prims: JSX.Element[], key: string) => {
let drawPrims = new HistogramBinPrimitiveCollection(histoResult!.bins![key], this.props.HistoBox);
let toggle = this.getSelectionToggle(drawPrims.BinPrimitives, allBrushIndex,
ModelHelpers.GetBinFilterModel(histoResult!.bins![key], allBrushIndex, histoResult!, this.histoOp.X, this.histoOp.Y));
drawPrims.BinPrimitives.filter(bp => bp.DataValue && bp.BrushIndex !== allBrushIndex).map(bp =>
prims.push(...[{ r: bp.Rect, c: bp.Color }, { r: bp.MarginRect, c: StyleConstants.MARGIN_BARS_COLOR }].map(pair => this.drawRect(pair.r, bp.BarAxis, pair.c, "bar", toggle))));
return prims;
- }, [] as JSX.Element[]);
+ }, [] as JSX.Element[])
}
componentDidMount() {
@@ -44,39 +46,38 @@ export class HistogramBoxPrimitives extends React.Component<HistogramPrimitivesP
}
private getSelectionToggle(binPrimitives: HistogramBinPrimitive[], allBrushIndex: number, filterModel: FilterModel) {
- let allBrushPrim = ArrayUtil.FirstOrDefault(binPrimitives, bp => bp.BrushIndex == allBrushIndex);
- return !allBrushPrim ? () => { } : () => runInAction(() => {
+ let rawAllBrushPrim = ArrayUtil.FirstOrDefault(binPrimitives, bp => bp.BrushIndex == allBrushIndex);
+ if (!rawAllBrushPrim)
+ return () => { }
+ let allBrushPrim = rawAllBrushPrim;
+ return () => runInAction(() => {
if (ArrayUtil.Contains(this.histoOp.FilterModels, filterModel)) {
- this._selectedPrims.splice(this._selectedPrims.indexOf(allBrushPrim!), 1);
+ this._selectedPrims.splice(this._selectedPrims.indexOf(allBrushPrim), 1);
this.histoOp.RemoveFilterModels([filterModel]);
}
else {
- this._selectedPrims.push(allBrushPrim!);
+ this._selectedPrims.push(allBrushPrim);
this.histoOp.AddFilterModels([filterModel]);
}
})
}
private renderGridLinesAndLabels(axis: number) {
+ trace();
if (!this.props.HistoBox.SizeConverter.Initialized)
return (null);
let labels = this.props.HistoBox.VisualBinRanges[axis].GetLabels();
- return labels.reduce((prims, binLabel, i) => {
- let r = this.props.HistoBox.SizeConverter.DataToScreenRange(binLabel.minValue!, binLabel.maxValue!, axis);
- prims.push(this.drawLine(r.xFrom, r.yFrom, axis == 0 ? 0 : r.xTo - r.xFrom, axis == 0 ? r.yTo - r.yFrom : 0));
- if (i == labels.length - 1)
- prims.push(this.drawLine(axis == 0 ? r.xTo : r.xFrom, axis == 0 ? r.yFrom : r.yTo, axis == 0 ? 0 : r.xTo - r.xFrom, axis == 0 ? r.yTo - r.yFrom : 0));
- return prims;
- }, [] as JSX.Element[]);
+ return <svg className="histogramboxprimitives-svgContainer">
+ {labels.reduce((prims, binLabel, i) => {
+ let r = this.props.HistoBox.SizeConverter.DataToScreenRange(binLabel.minValue!, binLabel.maxValue!, axis);
+ prims.push(this.drawLine(r.xFrom, r.yFrom, axis == 0 ? 0 : r.xTo - r.xFrom, axis == 0 ? r.yTo - r.yFrom : 0));
+ if (i == labels.length - 1)
+ prims.push(this.drawLine(axis == 0 ? r.xTo : r.xFrom, axis == 0 ? r.yFrom : r.yTo, axis == 0 ? 0 : r.xTo - r.xFrom, axis == 0 ? r.yTo - r.yFrom : 0));
+ return prims;
+ }, [] as JSX.Element[])}
+ </svg>
}
- drawEntity(xFrom: number, yFrom: number, entity: JSX.Element) {
- let transXpercent = xFrom / this.renderDimension * 100;
- let transYpercent = yFrom / this.renderDimension * 100;
- return (<div key={DashUtils.GenerateGuid()} className={`histogramboxprimitives-placer`} style={{ transform: `translate(${transXpercent}%, ${transYpercent}%)` }}>
- {entity}
- </div>);
- }
drawLine(xFrom: number, yFrom: number, width: number, height: number) {
if (height < 0) {
yFrom += height;
@@ -86,10 +87,11 @@ export class HistogramBoxPrimitives extends React.Component<HistogramPrimitivesP
xFrom += width;
width = -width;
}
- let trans2Xpercent = width == 0 ? `1px` : `${(xFrom + width) / this.renderDimension * 100}%`;
- let trans2Ypercent = height == 0 ? `1px` : `${(yFrom + height) / this.renderDimension * 100}%`;
- let line = (<div className="histogramboxprimitives-line" style={{ width: trans2Xpercent, height: trans2Ypercent, }} />);
- return this.drawEntity(xFrom, yFrom, line);
+ let trans2Xpercent = `${(xFrom + width) / this.renderDimension * 100}%`;
+ let trans2Ypercent = `${(yFrom + height) / this.renderDimension * 100}%`;
+ let trans1Xpercent = `${xFrom / this.renderDimension * 100}%`
+ let trans1Ypercent = `${yFrom / this.renderDimension * 100}%`
+ return <line className="histogramboxprimitives-line" key={DashUtils.GenerateGuid()} x1={trans1Xpercent} x2={`${trans2Xpercent}`} y1={trans1Ypercent} y2={`${trans2Ypercent}`} />
}
drawRect(r: PIXIRectangle, barAxis: number, color: number | undefined, classExt: string, tapHandler: () => void = () => { }) {
if (r.height < 0) {
@@ -100,25 +102,22 @@ export class HistogramBoxPrimitives extends React.Component<HistogramPrimitivesP
r.x += r.width;
r.width = -r.width;
}
- let widthPercent = r.width / this.renderDimension * 100;
- let heightPercent = r.height / this.renderDimension * 100;
- let rect = (<div className={`histogramboxprimitives-${classExt}`} onPointerDown={(e: React.PointerEvent) => { if (e.button == 0) tapHandler() }}
- style={{
- borderBottomStyle: barAxis == 1 ? "none" : "solid",
- borderLeftStyle: barAxis == 0 ? "none" : "solid",
- width: `${widthPercent}%`,
- height: `${heightPercent}%`,
- background: color ? `${LABColor.RGBtoHexString(color)}` : ""
- }}
- />);
- return this.drawEntity(r.x, r.y, rect);
+ let transXpercent = `${r.x / this.renderDimension * 100}%`
+ let transYpercent = `${r.y / this.renderDimension * 100}%`
+ let widthXpercent = `${r.width / this.renderDimension * 100}%`;
+ let heightYpercent = `${r.height / this.renderDimension * 100}%`;
+ return (<rect className={`histogramboxprimitives-${classExt}`} key={DashUtils.GenerateGuid()} onPointerDown={(e: React.PointerEvent) => { if (e.button == 0) tapHandler() }}
+ x={transXpercent} width={`${widthXpercent}`} y={transYpercent} height={`${heightYpercent}`} fill={color ? `${LABColor.RGBtoHexString(color)}` : "transparent"} />);
}
render() {
+ trace();
return <div className="histogramboxprimitives-container">
{this.xaxislines}
{this.yaxislines}
- {this.binPrimitives}
- {this.selectedPrimitives}
+ <svg className="histogramboxprimitives-svgContainer">
+ {this.barPrimitives}
+ {this.selectedPrimitives}
+ </svg>
</div>
}
} \ No newline at end of file
diff --git a/src/client/northstar/operations/HistogramOperation.ts b/src/client/northstar/operations/HistogramOperation.ts
index e63de1632..840520235 100644
--- a/src/client/northstar/operations/HistogramOperation.ts
+++ b/src/client/northstar/operations/HistogramOperation.ts
@@ -42,6 +42,10 @@ export class HistogramOperation extends BaseOperation implements IBaseFilterCons
this.SchemaName = schemaName;
}
+ Copy(): HistogramOperation {
+ return new HistogramOperation(this.SchemaName, this.X, this.Y, this.V, this.Normalization);
+ }
+
Equals(other: Object): boolean {
throw new Error("Method not implemented.");
}
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index bf59fbb43..341959936 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -29,7 +29,7 @@ export class DocumentManager {
public getAllDocumentViews(collection: Document) {
return this.DocumentViews.filter(dv =>
- dv.props.ContainingCollectionView && dv.props.ContainingCollectionView.props.Document == collection);
+ dv.props.ContainingCollectionView && dv.props.ContainingCollectionView.props.Document.Id === collection.Id);
}
public getDocumentView(toFind: Document): DocumentView | null {
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 9ffe964ef..0ee7ed2b3 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -1,27 +1,36 @@
-import { DocumentDecorations } from "../views/DocumentDecorations";
-import { CollectionDockingView } from "../views/collections/CollectionDockingView";
-import { Document } from "../../fields/Document"
import { action } from "mobx";
-import { ImageField } from "../../fields/ImageField";
-import { KeyStore } from "../../fields/KeyStore";
+import { Document } from "../../fields/Document";
+import { CollectionDockingView } from "../views/collections/CollectionDockingView";
import { CollectionView } from "../views/collections/CollectionView";
+import { DocumentDecorations } from "../views/DocumentDecorations";
import { DocumentView } from "../views/nodes/DocumentView";
-export function setupDrag(_reference: React.RefObject<HTMLDivElement>, docFunc: () => Document, removeFunc: (containingCollection: CollectionView) => void = () => { }) {
- let onRowMove = action((e: PointerEvent): void => {
- e.stopPropagation();
- e.preventDefault();
-
- document.removeEventListener("pointermove", onRowMove);
- document.removeEventListener('pointerup', onRowUp);
- var dragData = new DragManager.DocumentDragData([docFunc()]);
- dragData.removeDocument = removeFunc;
- DragManager.StartDocumentDrag([_reference.current!], dragData);
- });
- let onRowUp = action((e: PointerEvent): void => {
- document.removeEventListener("pointermove", onRowMove);
- document.removeEventListener('pointerup', onRowUp);
- });
+export function setupDrag(
+ _reference: React.RefObject<HTMLDivElement>,
+ docFunc: () => Document,
+ removeFunc: (containingCollection: CollectionView) => void = () => { },
+ copyOnDrop: boolean = false
+) {
+ let onRowMove = action(
+ (e: PointerEvent): void => {
+ e.stopPropagation();
+ e.preventDefault();
+
+ // TODO: bcz -- this needs to have a drag threshold so that it doesn't trigger when just selecting.
+ document.removeEventListener("pointermove", onRowMove);
+ document.removeEventListener("pointerup", onRowUp);
+ var dragData = new DragManager.DocumentDragData([docFunc()]);
+ dragData.copyOnDrop = copyOnDrop;
+ dragData.removeDocument = removeFunc;
+ DragManager.StartDocumentDrag([_reference.current!], dragData, e.x, e.y);
+ }
+ );
+ let onRowUp = action(
+ (e: PointerEvent): void => {
+ document.removeEventListener("pointermove", onRowMove);
+ document.removeEventListener("pointerup", onRowUp);
+ }
+ );
let onItemDown = (e: React.PointerEvent) => {
// if (this.props.isSelected() || this.props.isTopMost) {
if (e.button == 0) {
@@ -30,11 +39,11 @@ export function setupDrag(_reference: React.RefObject<HTMLDivElement>, docFunc:
CollectionDockingView.Instance.StartOtherDrag([docFunc()], e);
} else {
document.addEventListener("pointermove", onRowMove);
- document.addEventListener('pointerup', onRowUp);
+ document.addEventListener("pointerup", onRowUp);
}
}
//}
- }
+ };
return onItemDown;
}
@@ -50,7 +59,9 @@ export namespace DragManager {
let dragDiv: HTMLDivElement;
export enum DragButtons {
- Left = 1, Right = 2, Both = Left | Right
+ Left = 1,
+ Right = 2,
+ Both = Left | Right
}
interface DragOptions {
@@ -63,8 +74,7 @@ export namespace DragManager {
(): void;
}
- export class DragCompleteEvent {
- }
+ export class DragCompleteEvent { }
export interface DragHandlers {
dragComplete: (e: DragCompleteEvent) => void;
@@ -74,19 +84,25 @@ export namespace DragManager {
handlers: DropHandlers;
}
export class DropEvent {
- constructor(readonly x: number, readonly y: number, readonly data: { [id: string]: any }) { }
+ constructor(
+ readonly x: number,
+ readonly y: number,
+ readonly data: { [id: string]: any }
+ ) { }
}
-
-
export interface DropHandlers {
drop: (e: Event, de: DropEvent) => void;
}
-
- export function MakeDropTarget(element: HTMLElement, options: DropOptions): DragDropDisposer {
+ export function MakeDropTarget(
+ element: HTMLElement,
+ options: DropOptions
+ ): DragDropDisposer {
if ("canDrop" in element.dataset) {
- throw new Error("Element is already droppable, can't make it droppable again");
+ throw new Error(
+ "Element is already droppable, can't make it droppable again"
+ );
}
element.dataset["canDrop"] = "true";
const handler = (e: Event) => {
@@ -96,7 +112,7 @@ export namespace DragManager {
element.addEventListener("dashOnDrop", handler);
return () => {
element.removeEventListener("dashOnDrop", handler);
- delete element.dataset["canDrop"]
+ delete element.dataset["canDrop"];
};
}
@@ -110,28 +126,53 @@ export namespace DragManager {
xOffset?: number;
yOffset?: number;
aliasOnDrop?: boolean;
+ copyOnDrop?: boolean;
removeDocument?: (collectionDrop: CollectionView) => void;
[id: string]: any;
}
- export function StartDocumentDrag(eles: HTMLElement[], dragData: DocumentDragData, options?: DragOptions) {
- StartDrag(eles, dragData, options, (dropData: { [id: string]: any }) => dropData.droppedDocuments = dragData.aliasOnDrop ? dragData.draggedDocuments.map(d => d.CreateAlias()) : dragData.draggedDocuments);
+ export function StartDocumentDrag(
+ eles: HTMLElement[],
+ dragData: DocumentDragData,
+ downX: number,
+ downY: number,
+ options?: DragOptions
+ ) {
+ StartDrag(eles, dragData, downX, downY, options,
+ (dropData: { [id: string]: any }) =>
+ (dropData.droppedDocuments = dragData.aliasOnDrop
+ ? dragData.draggedDocuments.map(d => d.CreateAlias())
+ : dragData.copyOnDrop ? dragData.draggedDocuments.map(d => d.Copy(true) as Document) :
+ dragData.draggedDocuments)
+ );
}
export class LinkDragData {
constructor(linkSourceDoc: DocumentView) {
this.linkSourceDocumentView = linkSourceDoc;
}
+ droppedDocuments: Document[] = [];
linkSourceDocumentView: DocumentView;
[id: string]: any;
}
- export function StartLinkDrag(ele: HTMLElement, dragData: LinkDragData, options?: DragOptions) {
- StartDrag([ele], dragData, options);
+ export function StartLinkDrag(
+ ele: HTMLElement,
+ dragData: LinkDragData,
+ downX: number, downY: number,
+ options?: DragOptions
+ ) {
+ StartDrag([ele], dragData, downX, downY, options);
}
- function StartDrag(eles: HTMLElement[], dragData: { [id: string]: any }, options?: DragOptions, finishDrag?: (dropData: { [id: string]: any }) => void) {
+ function StartDrag(
+ eles: HTMLElement[],
+ dragData: { [id: string]: any },
+ downX: number, downY: number,
+ options?: DragOptions,
+ finishDrag?: (dropData: { [id: string]: any }) => void
+ ) {
if (!dragDiv) {
dragDiv = document.createElement("div");
- dragDiv.className = "dragManager-dragDiv"
+ dragDiv.className = "dragManager-dragDiv";
DragManager.Root().appendChild(dragDiv);
}
@@ -140,40 +181,49 @@ export namespace DragManager {
let xs: number[] = [];
let ys: number[] = [];
- const docs: Document[] = dragData instanceof DocumentDragData ? dragData.draggedDocuments : [];
+ const docs: Document[] =
+ dragData instanceof DocumentDragData ? dragData.draggedDocuments : [];
let dragElements = eles.map(ele => {
- const w = ele.offsetWidth, h = ele.offsetHeight;
+ const w = ele.offsetWidth,
+ h = ele.offsetHeight;
const rect = ele.getBoundingClientRect();
- const scaleX = rect.width / w, scaleY = rect.height / h;
- let x = rect.left, y = rect.top;
- xs.push(x); ys.push(y);
- scaleXs.push(scaleX); scaleYs.push(scaleY);
+ const scaleX = rect.width / w,
+ scaleY = rect.height / h;
+ let x = rect.left,
+ y = rect.top;
+ xs.push(x);
+ ys.push(y);
+ scaleXs.push(scaleX);
+ scaleYs.push(scaleY);
let dragElement = ele.cloneNode(true) as HTMLElement;
dragElement.style.opacity = "0.7";
dragElement.style.position = "absolute";
+ dragElement.style.margin = "0";
+ dragElement.style.top = "0";
dragElement.style.bottom = "";
- dragElement.style.left = "";
+ dragElement.style.left = "0";
dragElement.style.transformOrigin = "0 0";
dragElement.style.zIndex = "1000";
dragElement.style.transform = `translate(${x}px, ${y}px) scale(${scaleX}, ${scaleY})`;
dragElement.style.width = `${rect.width / scaleX}px`;
dragElement.style.height = `${rect.height / scaleY}px`;
- // bcz: PDFs don't show up if you clone them because they contain a canvas.
+ // bcz: if PDFs are rendered with svg's, then this code isn't needed
+ // bcz: PDFs don't show up if you clone them when rendered using a canvas.
// however, PDF's have a thumbnail field that contains an image of their canvas.
// So we replace the pdf's canvas with the image thumbnail
- if (docs.length) {
- var pdfBox = dragElement.getElementsByClassName("pdfBox-cont")[0] as HTMLElement;
- let thumbnail = docs[0].GetT(KeyStore.Thumbnail, ImageField);
- if (pdfBox && pdfBox.childElementCount && thumbnail) {
- let img = new Image();
- img!.src = thumbnail.toString();
- img!.style.position = "absolute";
- img!.style.width = `${rect.width / scaleX}px`;
- img!.style.height = `${rect.height / scaleY}px`;
- pdfBox.replaceChild(img!, pdfBox.children[0])
- }
- }
+ // if (docs.length) {
+ // var pdfBox = dragElement.getElementsByClassName("pdfBox-cont")[0] as HTMLElement;
+ // let thumbnail = docs[0].GetT(KeyStore.Thumbnail, ImageField);
+ // if (pdfBox && pdfBox.childElementCount && thumbnail) {
+ // let img = new Image();
+ // img!.src = thumbnail.toString();
+ // img!.style.position = "absolute";
+ // img!.style.width = `${rect.width / scaleX}px`;
+ // img!.style.height = `${rect.height / scaleY}px`;
+ // pdfBox.replaceChild(img!, pdfBox.children[0]);
+ // }
+ // }
dragDiv.appendChild(dragElement);
return dragElement;
@@ -187,8 +237,10 @@ export namespace DragManager {
hideSource = options.hideSource();
}
}
- eles.map(ele => ele.hidden = hideSource);
+ eles.map(ele => (ele.hidden = hideSource));
+ let lastX = downX;
+ let lastY = downY;
const moveHandler = (e: PointerEvent) => {
e.stopPropagation();
e.preventDefault();
@@ -196,17 +248,29 @@ export namespace DragManager {
dragData.aliasOnDrop = e.ctrlKey || e.altKey;
if (e.shiftKey) {
abortDrag();
- CollectionDockingView.Instance.StartOtherDrag(docs, { pageX: e.pageX, pageY: e.pageY, preventDefault: () => { }, button: 0 });
+ CollectionDockingView.Instance.StartOtherDrag(docs, {
+ pageX: e.pageX,
+ pageY: e.pageY,
+ preventDefault: () => { },
+ button: 0
+ });
}
- dragElements.map((dragElement, i) => dragElement.style.transform = `translate(${xs[i] += e.movementX}px, ${ys[i] += e.movementY}px) scale(${scaleXs[i]}, ${scaleYs[i]})`);
+ let moveX = e.pageX - lastX;
+ let moveY = e.pageY - lastY;
+ lastX = e.pageX;
+ lastY = e.pageY;
+ dragElements.map((dragElement, i) => (dragElement.style.transform =
+ `translate(${(xs[i] += moveX)}px, ${(ys[i] += moveY)}px)
+ scale(${scaleXs[i]}, ${scaleYs[i]})`)
+ );
};
const abortDrag = () => {
document.removeEventListener("pointermove", moveHandler, true);
document.removeEventListener("pointerup", upHandler);
dragElements.map(dragElement => dragDiv.removeChild(dragElement));
- eles.map(ele => ele.hidden = false);
- }
+ eles.map(ele => (ele.hidden = false));
+ };
const upHandler = (e: PointerEvent) => {
abortDrag();
FinishDrag(eles, e, dragData, options, finishDrag);
@@ -215,32 +279,37 @@ export namespace DragManager {
document.addEventListener("pointerup", upHandler);
}
- function FinishDrag(dragEles: HTMLElement[], e: PointerEvent, dragData: { [index: string]: any }, options?: DragOptions, finishDrag?: (dragData: { [index: string]: any }) => void) {
+ function FinishDrag(
+ dragEles: HTMLElement[],
+ e: PointerEvent,
+ dragData: { [index: string]: any },
+ options?: DragOptions,
+ finishDrag?: (dragData: { [index: string]: any }) => void
+ ) {
let removed = dragEles.map(dragEle => {
let parent = dragEle.parentElement;
- if (parent)
- parent.removeChild(dragEle);
+ if (parent) parent.removeChild(dragEle);
return [dragEle, parent];
});
const target = document.elementFromPoint(e.x, e.y);
removed.map(r => {
let dragEle: HTMLElement = r[0]!;
let parent: HTMLElement | null = r[1];
- if (parent)
- parent.appendChild(dragEle);
+ if (parent) parent.appendChild(dragEle);
});
if (target) {
- if (finishDrag)
- finishDrag(dragData);
-
- target.dispatchEvent(new CustomEvent<DropEvent>("dashOnDrop", {
- bubbles: true,
- detail: {
- x: e.x,
- y: e.y,
- data: dragData
- }
- }));
+ if (finishDrag) finishDrag(dragData);
+
+ target.dispatchEvent(
+ new CustomEvent<DropEvent>("dashOnDrop", {
+ bubbles: true,
+ detail: {
+ x: e.x,
+ y: e.y,
+ data: dragData
+ }
+ })
+ );
if (options) {
options.handlers.dragComplete({});
@@ -248,4 +317,4 @@ export namespace DragManager {
}
DocumentDecorations.Instance.Hidden = false;
}
-} \ No newline at end of file
+}
diff --git a/src/client/util/Scripting.ts b/src/client/util/Scripting.ts
index 4e97b9401..7c0649a6a 100644
--- a/src/client/util/Scripting.ts
+++ b/src/client/util/Scripting.ts
@@ -18,41 +18,57 @@ import * as typescriptlib from '!!raw-loader!./type_decls.d'
import { Documents } from "../documents/Documents";
import { Key } from "../../fields/Key";
+export interface ScriptSucccess {
+ success: true;
+ result: any;
+}
+
+export interface ScriptError {
+ success: false;
+ error: any;
+}
+
+export type ScriptResult = ScriptSucccess | ScriptError;
-export interface ExecutableScript {
- (): any;
+export interface CompileSuccess {
+ compiled: true;
+ run(args?: { [name: string]: any }): ScriptResult;
+}
- compiled: boolean;
+export interface CompileError {
+ compiled: false;
+ errors: any[];
}
-function Compile(script: string | undefined, diagnostics: Opt<any[]>, scope: { [name: string]: any }): ExecutableScript {
- const compiled = !(diagnostics && diagnostics.some(diag => diag.category == ts.DiagnosticCategory.Error));
+export type CompiledScript = CompileSuccess | CompileError;
+
+function Run(script: string | undefined, customParams: string[], diagnostics: any[]): CompiledScript {
+ const errors = diagnostics.some(diag => diag.category == ts.DiagnosticCategory.Error);
+ if (errors || !script) {
+ return { compiled: false, errors: diagnostics };
+ }
- let func: () => Opt<Field>;
- if (compiled && script) {
- let fieldTypes = [Document, NumberField, TextField, ImageField, RichTextField, ListField, Key];
- let paramNames = ["KeyStore", "Documents", ...fieldTypes.map(fn => fn.name)];
- let params: any[] = [KeyStore, Documents, ...fieldTypes]
- for (let prop in scope) {
- if (prop === "this") {
+ let fieldTypes = [Document, NumberField, TextField, ImageField, RichTextField, ListField, Key];
+ let paramNames = ["KeyStore", "Documents", ...fieldTypes.map(fn => fn.name)];
+ let params: any[] = [KeyStore, Documents, ...fieldTypes]
+ let compiledFunction = new Function(...paramNames, `return ${script}`);
+ let run = (args: { [name: string]: any } = {}): ScriptResult => {
+ let argsArray: any[] = [];
+ for (let name of customParams) {
+ if (name === "this") {
continue;
}
- paramNames.push(prop);
- params.push(scope[prop]);
+ argsArray.push(args[name]);
+ }
+ let thisParam = args["this"];
+ try {
+ const result = compiledFunction.apply(thisParam, params).apply(thisParam, argsArray);
+ return { success: true, result };
+ } catch (error) {
+ return { success: false, error };
}
- let thisParam = scope["this"];
- let compiledFunction = new Function(...paramNames, script);
- func = function (): Opt<Field> {
- return compiledFunction.apply(thisParam, params)
- };
- } else {
- func = () => undefined;
}
-
- return Object.assign(func,
- {
- compiled
- });
+ return { compiled: true, run };
}
interface File {
@@ -108,20 +124,39 @@ class ScriptingCompilerHost {
}
}
-export function CompileScript(script: string, scope?: { [name: string]: any }, addReturn: boolean = false): ExecutableScript {
+export interface ScriptOptions {
+ requiredType?: string;
+ addReturn?: boolean;
+ params?: { [name: string]: string };
+}
+
+export function CompileScript(script: string, { requiredType = "", addReturn = false, params = {} }: ScriptOptions = {}): CompiledScript {
let host = new ScriptingCompilerHost;
- let funcScript = `(function() {
+ let paramArray: string[] = [];
+ if ("this" in params) {
+ paramArray.push("this");
+ }
+ for (const key in params) {
+ if (key === "this") continue;
+ paramArray.push(key);
+ }
+ let paramString = paramArray.map(key => `${key}: ${params[key]}`).join(", ");
+ let funcScript = `(function(${paramString})${requiredType ? `: ${requiredType}` : ''} {
${addReturn ? `return ${script};` : script}
- }).apply(this)`
+ })`;
host.writeFile("file.ts", funcScript);
host.writeFile('node_modules/typescript/lib/lib.d.ts', typescriptlib);
let program = ts.createProgram(["file.ts"], {}, host);
let testResult = program.emit();
- let outputText = "return " + host.readFile("file.js");
+ let outputText = host.readFile("file.js");
let diagnostics = ts.getPreEmitDiagnostics(program).concat(testResult.diagnostics);
- return Compile(outputText, diagnostics, scope || {});
+ return Run(outputText, paramArray, diagnostics);
+}
+
+export function OrLiteralType(returnType: string): string {
+ return `${returnType} | string | number`;
}
export function ToField(data: any): Opt<Field> {
diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts
index 1354e32e1..79d4ceb25 100644
--- a/src/client/util/SelectionManager.ts
+++ b/src/client/util/SelectionManager.ts
@@ -1,6 +1,7 @@
import { observable, action } from "mobx";
import { DocumentView } from "../views/nodes/DocumentView";
-import { Document } from "../../fields/Document"
+import { Document } from "../../fields/Document";
+import { Main } from "../views/Main";
export namespace SelectionManager {
class Manager {
@@ -15,15 +16,15 @@ export namespace SelectionManager {
}
if (manager.SelectedDocuments.indexOf(doc) === -1) {
- manager.SelectedDocuments.push(doc)
+ manager.SelectedDocuments.push(doc);
}
}
}
- const manager = new Manager;
+ const manager = new Manager();
export function SelectDoc(doc: DocumentView, ctrlPressed: boolean): void {
- manager.SelectDoc(doc, ctrlPressed)
+ manager.SelectDoc(doc, ctrlPressed);
}
export function IsSelected(doc: DocumentView): boolean {
@@ -35,16 +36,15 @@ export namespace SelectionManager {
if (except) {
for (let i = 0; i < manager.SelectedDocuments.length; i++) {
let view = manager.SelectedDocuments[i];
- if (view.props.Document == except)
- found = view;
+ if (view.props.Document == except) found = view;
}
}
manager.SelectedDocuments.length = 0;
- if (found)
- manager.SelectedDocuments.push(found);
+ if (found) manager.SelectedDocuments.push(found);
+ Main.Instance.SetTextDoc(undefined, undefined);
}
export function SelectedDocuments(): Array<DocumentView> {
return manager.SelectedDocuments;
}
-} \ No newline at end of file
+}
diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx
index 219e7c94d..82e9d1bac 100644
--- a/src/client/util/TooltipTextMenu.tsx
+++ b/src/client/util/TooltipTextMenu.tsx
@@ -13,7 +13,7 @@ const { toggleMark, setBlockType, wrapIn } = require("prosemirror-commands");
import { library } from '@fortawesome/fontawesome-svg-core'
import { wrapInList, bulletList, liftListItem, listItem } from 'prosemirror-schema-list'
import {
- faListUl,
+ faListUl,
} from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
@@ -22,159 +22,147 @@ const SVG = "http://www.w3.org/2000/svg"
//appears above a selection of text in a RichTextBox to give user options such as Bold, Italics, etc.
export class TooltipTextMenu {
- private tooltip: HTMLElement;
- private num_icons = 0;
-
- constructor(view: EditorView) {
- this.tooltip = document.createElement("div");
- this.tooltip.className = "tooltipMenu";
-
- //add the div which is the tooltip
- view.dom.parentNode!.appendChild(this.tooltip);
-
- //add additional icons
- library.add(faListUl);
-
- //add the buttons to the tooltip
- let items = [
- { command: toggleMark(schema.marks.strong), dom: this.icon("B", "strong") },
- { command: toggleMark(schema.marks.em), dom: this.icon("i", "em") },
- { command: toggleMark(schema.marks.underline), dom: this.icon("U", "underline") },
- { command: toggleMark(schema.marks.strikethrough), dom: this.icon("S", "strikethrough") },
- { command: toggleMark(schema.marks.superscript), dom: this.icon("s", "superscript") },
- { command: toggleMark(schema.marks.subscript), dom: this.icon("s", "subscript") },
- { command: wrapInList(schema.nodes.bullet_list), dom: this.icon(":", "bullets") },
- { command: toggleMark(schema.marks.timesNewRoman), dom: this.icon("x", "TNR") },
- { command: lift, dom: this.icon("<", "lift") },
- ]
- //add menu items
- items.forEach(({ dom, command }) => {
- this.tooltip.appendChild(dom);
- });
-
- //add dropdowns
- let cut = arr => arr.filter(x => x);
- let config = {
+ private tooltip: HTMLElement;
+ private num_icons = 0;
+
+ constructor(view: EditorView) {
+ this.tooltip = document.createElement("div");
+ this.tooltip.className = "tooltipMenu";
+
+ //add the div which is the tooltip
+ view.dom.parentNode!.appendChild(this.tooltip);
+
+ //add additional icons
+ library.add(faListUl);
+
+ //add the buttons to the tooltip
+ let items = [
+ { command: toggleMark(schema.marks.strong), dom: this.icon("B", "strong") },
+ { command: toggleMark(schema.marks.em), dom: this.icon("i", "em") },
+ { command: toggleMark(schema.marks.underline), dom: this.icon("U", "underline") },
+ { command: toggleMark(schema.marks.strikethrough), dom: this.icon("S", "strikethrough") },
+ { command: toggleMark(schema.marks.superscript), dom: this.icon("s", "superscript") },
+ { command: toggleMark(schema.marks.subscript), dom: this.icon("s", "subscript") },
+ { command: wrapInList(schema.nodes.bullet_list), dom: this.icon(":", "bullets") },
+ { command: toggleMark(schema.marks.timesNewRoman), dom: this.icon("x", "TNR") },
+ { command: lift, dom: this.icon("<", "lift") },
+ ]
+ //add menu items
+ items.forEach(({ dom, command }) => {
+ this.tooltip.appendChild(dom);
+ });
+
+ //add dropdowns
+
+ //pointer down handler to activate button effects
+ this.tooltip.addEventListener("pointerdown", e => {
+ e.preventDefault();
+ view.focus();
+ //update view of icons
+ this.num_icons = 0;
+ items.forEach(({ command, dom }) => {
+ if (e.srcElement && dom.contains(e.srcElement as Node)) {
+ //let active = command(view.state, view.dispatch, view);
+ let active = command(view.state, view.dispatch, view);
+ //uncomment this if we want the bullet button to disappear if current selection is bulleted
+ //dom.style.display = active ? "" : "none";
+ }
+ })
+ })
+
+ this.update(view, undefined);
+ }
+ // Helper function to create menu icons
+ icon(text: string, name: string) {
+ let span = document.createElement("span");
+ span.className = "menuicon " + name;
+ span.title = name;
+ span.textContent = text;
+ return span;
}
- let tnr = new MenuItem({
- title: "tnr",
- label: "Times New Roman",
- css: "font-family: Times New Roman, Times, serif; ",
- enable(state) { return canInsert(state, hr) },
- run(state, dispatch) { dispatch(state.tr.replaceSelectionWith(hr.create())) }
- })
- new Dropdown(cut([schema.marks.timesNewRoman, r.insertHorizontalRule]), { label: "Insert" })
-
- //pointer down handler to activate button effects
- this.tooltip.addEventListener("pointerdown", e => {
- e.preventDefault();
- view.focus();
- //update view of icons
- this.num_icons = 0;
- items.forEach(({ command, dom }) => {
- if (e.srcElement && dom.contains(e.srcElement as Node)) {
- //let active = command(view.state, view.dispatch, view);
- let active = command(view.state, view.dispatch, view);
- //uncomment this if we want the bullet button to disappear if current selection is bulleted
- //dom.style.display = active ? "" : "none";
+
+ //adapted this method - use it to check if block has a tag (ie bulleting)
+ blockActive(type: NodeType<Schema<string, string>>, state: EditorState) {
+ let attrs = {};
+
+ if (state.selection instanceof NodeSelection) {
+ const sel: NodeSelection = state.selection;
+ let $from = sel.$from;
+ let to = sel.to;
+ let node = sel.node;
+
+ if (node) {
+ return node.hasMarkup(type, attrs);
+ }
+
+ return to <= $from.end() && $from.parent.hasMarkup(type, attrs);
}
- })
- })
-
- this.update(view, undefined);
- }
-
- // Helper function to create menu icons
- icon(text: string, name: string) {
- let span = document.createElement("span");
- span.className = "menuicon " + name;
- span.title = name;
- span.textContent = text;
- return span;
- }
-
- //adapted this method - use it to check if block has a tag (ie bulleting)
- blockActive(type: NodeType<Schema<string, string>>, state: EditorState) {
- let attrs = {};
-
- if (state.selection instanceof NodeSelection) {
- const sel: NodeSelection = state.selection;
- let $from = sel.$from;
- let to = sel.to;
- let node = sel.node;
-
- if (node) {
- return node.hasMarkup(type, attrs);
- }
-
- return to <= $from.end() && $from.parent.hasMarkup(type, attrs);
}
- }
-
- //this doesn't currently work but could be used to use icons for buttons
- unorderedListIcon(): HTMLSpanElement {
- let span = document.createElement("span");
- //let icon = document.createElement("FontAwesomeIcon");
- //icon.className = "menuicon";
- //icon.style.color = "white";
- //span.appendChild(<i style={{ color: "white" }} icon="list-ul" size="lg" />);
- let i = document.createElement("i");
- i.className = "fa falist-ul";
- span.appendChild(i);
- //span.appendChild(icon);
- //return liftItem.spec.icon.sty
-
- //let sym = document.createElementNS(SVG, "symbol")
- // sym.id = name
- //sym.style.color = "white";
- //width then height
- //sym.setAttribute("viewBox", "0 0 " + 1024 + " " + 1024);
- //let path = sym.appendChild(document.createElementNS(SVG, "path"));
- //path.setAttribute("d", "M219 310v329q0 7-5 12t-12 5q-8 0-13-5l-164-164q-5-5-5-13t5-13l164-164q5-5 13-5 7 0 12 5t5 12zM1024 749v109q0 7-5 12t-12 5h-987q-7 0-12-5t-5-12v-109q0-7 5-12t12-5h987q7 0 12 5t5 12zM1024 530v109q0 7-5 12t-12 5h-621q-7 0-12-5t-5-12v-109q0-7 5-12t12-5h621q7 0 12 5t5 12zM1024 310v109q0 7-5 12t-12 5h-621q-7 0-12-5t-5-12v-109q0-7 5-12t12-5h621q7 0 12 5t5 12zM1024 91v109q0 7-5 12t-12 5h-987q-7 0-12-5t-5-12v-109q0-7 5-12t12-5h987q7 0 12 5t5 12z");
- //span.appendChild(sym);
- return span;
- }
-
- // Create an icon for a heading at the given level
- heading(level: number) {
- return {
- command: setBlockType(schema.nodes.heading, { level }),
- dom: this.icon("H" + level, "heading")
+
+ //this doesn't currently work but could be used to use icons for buttons
+ unorderedListIcon(): HTMLSpanElement {
+ let span = document.createElement("span");
+ //let icon = document.createElement("FontAwesomeIcon");
+ //icon.className = "menuicon";
+ //icon.style.color = "white";
+ //span.appendChild(<i style={{ color: "white" }} icon="list-ul" size="lg" />);
+ let i = document.createElement("i");
+ i.className = "fa falist-ul";
+ span.appendChild(i);
+ //span.appendChild(icon);
+ //return liftItem.spec.icon.sty
+
+ //let sym = document.createElementNS(SVG, "symbol")
+ // sym.id = name
+ //sym.style.color = "white";
+ //width then height
+ //sym.setAttribute("viewBox", "0 0 " + 1024 + " " + 1024);
+ //let path = sym.appendChild(document.createElementNS(SVG, "path"));
+ //path.setAttribute("d", "M219 310v329q0 7-5 12t-12 5q-8 0-13-5l-164-164q-5-5-5-13t5-13l164-164q5-5 13-5 7 0 12 5t5 12zM1024 749v109q0 7-5 12t-12 5h-987q-7 0-12-5t-5-12v-109q0-7 5-12t12-5h987q7 0 12 5t5 12zM1024 530v109q0 7-5 12t-12 5h-621q-7 0-12-5t-5-12v-109q0-7 5-12t12-5h621q7 0 12 5t5 12zM1024 310v109q0 7-5 12t-12 5h-621q-7 0-12-5t-5-12v-109q0-7 5-12t12-5h621q7 0 12 5t5 12zM1024 91v109q0 7-5 12t-12 5h-987q-7 0-12-5t-5-12v-109q0-7 5-12t12-5h987q7 0 12 5t5 12z");
+ //span.appendChild(sym);
+ return span;
+ }
+
+ // Create an icon for a heading at the given level
+ heading(level: number) {
+ return {
+ command: setBlockType(schema.nodes.heading, { level }),
+ dom: this.icon("H" + level, "heading")
+ }
}
- }
-
- //updates the tooltip menu when the selection changes
- update(view: EditorView, lastState: EditorState | undefined) {
- let state = view.state
- // Don't do anything if the document/selection didn't change
- if (lastState && lastState.doc.eq(state.doc) &&
- lastState.selection.eq(state.selection)) return
-
- // Hide the tooltip if the selection is empty
- if (state.selection.empty) {
- this.tooltip.style.display = "none"
- return
+
+ //updates the tooltip menu when the selection changes
+ update(view: EditorView, lastState: EditorState | undefined) {
+ let state = view.state
+ // Don't do anything if the document/selection didn't change
+ if (lastState && lastState.doc.eq(state.doc) &&
+ lastState.selection.eq(state.selection)) return
+
+ // Hide the tooltip if the selection is empty
+ if (state.selection.empty) {
+ this.tooltip.style.display = "none"
+ return
+ }
+
+ // Otherwise, reposition it and update its content
+ this.tooltip.style.display = ""
+ let { from, to } = state.selection
+ let start = view.coordsAtPos(from), end = view.coordsAtPos(to)
+ // The box in which the tooltip is positioned, to use as base
+ let box = this.tooltip.offsetParent!.getBoundingClientRect()
+ // Find a center-ish x position from the selection endpoints (when
+ // crossing lines, end may be more to the left)
+ let left = Math.max((start.left + end.left) / 2, start.left + 3)
+ this.tooltip.style.left = (left - box.left) + "px"
+ //let width = Math.abs(start.left - end.left) / 2;
+ let width = 8 * 16 + 15;
+ let mid = Math.min(start.left, end.left) + width;
+
+ //THIS WIDTH IS 15 * NUMBER OF ICONS + 15
+ this.tooltip.style.width = width + "px";
+ this.tooltip.style.bottom = (box.bottom - start.top) + "px";
}
- // Otherwise, reposition it and update its content
- this.tooltip.style.display = ""
- let { from, to } = state.selection
- let start = view.coordsAtPos(from), end = view.coordsAtPos(to)
- // The box in which the tooltip is positioned, to use as base
- let box = this.tooltip.offsetParent!.getBoundingClientRect()
- // Find a center-ish x position from the selection endpoints (when
- // crossing lines, end may be more to the left)
- let left = Math.max((start.left + end.left) / 2, start.left + 3)
- this.tooltip.style.left = (left - box.left) + "px"
- //let width = Math.abs(start.left - end.left) / 2;
- let width = 8 * 16 + 15;
- let mid = Math.min(start.left, end.left) + width;
-
- //THIS WIDTH IS 15 * NUMBER OF ICONS + 15
- this.tooltip.style.width = width + "px";
- this.tooltip.style.bottom = (box.bottom - start.top) + "px";
- }
-
- destroy() { this.tooltip.remove() }
-} \ No newline at end of file
+ destroy() { this.tooltip.remove() }
+}
diff --git a/src/client/util/Transform.ts b/src/client/util/Transform.ts
index 3e1039166..060c5da82 100644
--- a/src/client/util/Transform.ts
+++ b/src/client/util/Transform.ts
@@ -17,45 +17,45 @@ export class Transform {
this._scale = scale;
}
- translate = (x: number, y: number): Transform => {
+ translate = (x: number, y: number): this => {
this._translateX += x;
this._translateY += y;
return this;
}
- scale = (scale: number): Transform => {
+ scale = (scale: number): this => {
this._scale *= scale;
this._translateX *= scale;
this._translateY *= scale;
return this;
}
- scaleAbout = (scale: number, x: number, y: number): Transform => {
+ scaleAbout = (scale: number, x: number, y: number): this => {
this._translateX += x * this._scale - x * this._scale * scale;
this._translateY += y * this._scale - y * this._scale * scale;
this._scale *= scale;
return this;
}
- transform = (transform: Transform): Transform => {
+ transform = (transform: Transform): this => {
this._translateX = transform._translateX + transform._scale * this._translateX;
this._translateY = transform._translateY + transform._scale * this._translateY;
this._scale *= transform._scale;
return this;
}
- preTranslate = (x: number, y: number): Transform => {
+ preTranslate = (x: number, y: number): this => {
this._translateX += this._scale * x;
this._translateY += this._scale * y;
return this;
}
- preScale = (scale: number): Transform => {
+ preScale = (scale: number): this => {
this._scale *= scale;
return this;
}
- preTransform = (transform: Transform): Transform => {
+ preTransform = (transform: Transform): this => {
this._translateX += transform._translateX * this._scale;
this._translateY += transform._translateY * this._scale;
this._scale *= transform._scale;
diff --git a/src/client/util/UndoManager.ts b/src/client/util/UndoManager.ts
index 6d1b2f1b8..eb13ff1ee 100644
--- a/src/client/util/UndoManager.ts
+++ b/src/client/util/UndoManager.ts
@@ -1,6 +1,7 @@
import { observable, action } from "mobx";
import 'source-map-support/register'
import { Without } from "../../Utils";
+import { string } from "prop-types";
function getBatchName(target: any, key: string | symbol): string {
let keyName = key.toString();
@@ -84,6 +85,9 @@ export namespace UndoManager {
export function GetOpenBatches(): Without<Batch, 'end'>[] {
return openBatches;
}
+ export function TraceOpenBatches() {
+ console.log(`Open batches:\n\t${openBatches.map(batch => batch.batchName).join("\n\t")}\n`);
+ }
export class Batch {
private disposed: boolean = false;
diff --git a/src/client/views/DocumentDecorations.scss b/src/client/views/DocumentDecorations.scss
index 7a43f3087..c4e4aed8e 100644
--- a/src/client/views/DocumentDecorations.scss
+++ b/src/client/views/DocumentDecorations.scss
@@ -1,39 +1,91 @@
@import "global_variables";
+
#documentDecorations-container {
position: absolute;
+ top: 0;
+ left:0;
display: grid;
z-index: 1000;
- grid-template-rows: 8px 1fr 8px 30px;
- grid-template-columns: 8px 1fr 8px;
+ grid-template-rows: 20px 8px 1fr 8px;
+ grid-template-columns: 8px 8px 1fr 8px 8px;
pointer-events: none;
+
#documentDecorations-centerCont {
+ grid-column:3;
background: none;
}
+
.documentDecorations-resizer {
pointer-events: auto;
background: $alt-accent;
opacity: 0.8;
}
+
+ #documentDecorations-topLeftResizer,
+ #documentDecorations-leftResizer,
+ #documentDecorations-bottomLeftResizer {
+ grid-column: 1
+ }
+
+ #documentDecorations-topResizer,
+ #documentDecorations-bottomResizer {
+ grid-column-start: 2;
+ grid-column-end: 5;
+ }
+
+ #documentDecorations-bottomRightResizer,
+ #documentDecorations-topRightResizer,
+ #documentDecorations-rightResizer {
+ grid-column-start:5;
+ grid-column-end:7;
+ }
+
#documentDecorations-topLeftResizer,
#documentDecorations-bottomRightResizer {
cursor: nwse-resize;
}
+
#documentDecorations-topRightResizer,
#documentDecorations-bottomLeftResizer {
cursor: nesw-resize;
}
+
#documentDecorations-topResizer,
#documentDecorations-bottomResizer {
cursor: ns-resize;
}
+
#documentDecorations-leftResizer,
#documentDecorations-rightResizer {
cursor: ew-resize;
}
+ .title{
+ width:100%;
+ background: lightblue;
+ grid-column-start:3;
+ grid-column-end: 4;
+ pointer-events: auto;
+ }
}
+.documentDecorations-closeButton {
+ background:$alt-accent;
+ opacity: 0.8;
+ grid-column-start: 4;
+ grid-column-end: 6;
+ pointer-events: all;
+ text-align: center;
+}
+.documentDecorations-minimizeButton {
+ background:$alt-accent;
+ opacity: 0.8;
+ grid-column-start: 1;
+ grid-column-end: 3;
+ pointer-events: all;
+ text-align: center;
+}
.documentDecorations-background {
- background:lightblue;
+ background: lightblue;
position: absolute;
opacity: 0.1;
}
@@ -70,7 +122,8 @@
// }
// }
.linkFlyout {
- grid-column: 1/4
+ grid-column: 1/4;
+ margin-left: 25px;
}
.linkButton-empty:hover {
@@ -85,6 +138,31 @@
cursor: pointer;
}
+.linkButton-linker {
+ position:absolute;
+ bottom:0px;
+ left: 0px;
+ height: 20px;
+ width: 20px;
+ margin-top: 10px;
+ margin-right: 5px;
+ border-radius: 50%;
+ opacity: 0.9;
+ pointer-events: auto;
+ color: $dark-color;
+ border: $dark-color 1px solid;
+ text-transform: uppercase;
+ letter-spacing: 2px;
+ font-size: 75%;
+ transition: transform 0.2s;
+ text-align: center;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+.linkButton-tray {
+ grid-column: 1/4;
+}
.linkButton-empty {
height: 20px;
width: 20px;
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index faba3b6ea..c7e4a269a 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -1,37 +1,84 @@
-import { action, computed, observable, trace } from "mobx";
+import { action, computed, observable, trace, runInAction } from "mobx";
import { observer } from "mobx-react";
-import { KeyStore } from '../../fields/KeyStore';
+import { Key } from "../../fields/Key";
+//import ContentEditable from 'react-contenteditable'
+import { KeyStore } from "../../fields/KeyStore";
import { ListField } from "../../fields/ListField";
import { NumberField } from "../../fields/NumberField";
+import { Document } from "../../fields/Document";
+import { TextField } from "../../fields/TextField";
import { DragManager } from "../util/DragManager";
import { SelectionManager } from "../util/SelectionManager";
import { CollectionView } from "./collections/CollectionView";
import './DocumentDecorations.scss';
+import { DocumentView } from "./nodes/DocumentView";
import { LinkMenu } from "./nodes/LinkMenu";
import React = require("react");
+import { FieldWaiting } from "../../fields/Field";
const higflyout = require("@hig/flyout");
export const { anchorPoints } = higflyout;
export const Flyout = higflyout.default;
@observer
-export class DocumentDecorations extends React.Component {
+export class DocumentDecorations extends React.Component<{}, { value: string }> {
static Instance: DocumentDecorations
private _resizer = ""
private _isPointerDown = false;
-
+ private keyinput: React.RefObject<HTMLInputElement>;
+ private _documents: DocumentView[] = SelectionManager.SelectedDocuments();
private _resizeBorderWidth = 16;
+ private _linkBoxHeight = 30;
+ private _titleHeight = 20;
private _linkButton = React.createRef<HTMLDivElement>();
+ private _linkerButton = React.createRef<HTMLDivElement>();
+ //@observable private _title: string = this._documents[0].props.Document.Title;
+ @observable private _title: string = this._documents.length > 0 ? this._documents[0].props.Document.Title : "";
+ @observable private _fieldKey: Key = KeyStore.Title;
@observable private _hidden = false;
+ @observable private _opacity = 1;
@observable private _dragging = false;
+
constructor(props: Readonly<{}>) {
super(props)
-
DocumentDecorations.Instance = this
+ this.handleChange = this.handleChange.bind(this);
+ this.keyinput = React.createRef();
+ }
+
+ @action
+ handleChange = (event: any) => {
+ this._title = event.target.value;
+ };
+
+ @action
+ enterPressed = (e: any) => {
+ var key = e.keyCode || e.which;
+ // enter pressed
+ if (key == 13) {
+ var text = e.target.value;
+ if (text[0] == '#') {
+ let command = text.slice(1, text.length);
+ this._fieldKey = new Key(command)
+ // if (command == "Title" || command == "title") {
+ // this._fieldKey = KeyStore.Title;
+ // }
+ // else if (command == "Width" || command == "width") {
+ // this._fieldKey = KeyStore.Width;
+ // }
+ this._title = "changed"
+ // TODO: Change field with switch statement
+ }
+ else {
+ this._title = "changed";
+ }
+ e.target.blur();
+ }
}
@computed
get Bounds(): { x: number, y: number, b: number, r: number } {
+ this._documents = SelectionManager.SelectedDocuments();
return SelectionManager.SelectedDocuments().reduce((bounds, documentView) => {
if (documentView.props.isTopMost) {
return bounds;
@@ -79,12 +126,14 @@ export class DocumentDecorations extends React.Component {
this._dragging = true;
document.removeEventListener("pointermove", this.onBackgroundMove);
document.removeEventListener("pointerup", this.onBackgroundUp);
- DragManager.StartDocumentDrag(SelectionManager.SelectedDocuments().map(docView => (docView as any)._mainCont!.current!), dragData, {
- handlers: {
- dragComplete: action(() => this._dragging = false),
- },
- hideSource: true
- })
+ DragManager.StartDocumentDrag(SelectionManager.SelectedDocuments().map(docView => (docView as any)._mainCont!.current!), dragData,
+ e.x, e.y,
+ {
+ handlers: {
+ dragComplete: action(() => this._dragging = false),
+ },
+ hideSource: true
+ })
e.stopPropagation();
}
@@ -95,6 +144,53 @@ export class DocumentDecorations extends React.Component {
e.preventDefault();
}
+ onCloseDown = (e: React.PointerEvent): void => {
+ e.stopPropagation();
+ if (e.button === 0) {
+ document.removeEventListener("pointermove", this.onCloseMove);
+ document.addEventListener("pointermove", this.onCloseMove);
+ document.removeEventListener("pointerup", this.onCloseUp);
+ document.addEventListener("pointerup", this.onCloseUp);
+ }
+ }
+ onCloseMove = (e: PointerEvent): void => {
+ e.stopPropagation();
+ if (e.button === 0) {
+ }
+ }
+ @action
+ onCloseUp = (e: PointerEvent): void => {
+ e.stopPropagation();
+ if (e.button === 0) {
+ SelectionManager.SelectedDocuments().map(dv => dv.props.RemoveDocument && dv.props.RemoveDocument(dv.props.Document));
+ SelectionManager.DeselectAll();
+ document.removeEventListener("pointermove", this.onCloseMove);
+ document.removeEventListener("pointerup", this.onCloseUp);
+ }
+ }
+ onMinimizeDown = (e: React.PointerEvent): void => {
+ e.stopPropagation();
+ if (e.button === 0) {
+ document.removeEventListener("pointermove", this.onMinimizeMove);
+ document.addEventListener("pointermove", this.onMinimizeMove);
+ document.removeEventListener("pointerup", this.onMinimizeUp);
+ document.addEventListener("pointerup", this.onMinimizeUp);
+ }
+ }
+ onMinimizeMove = (e: PointerEvent): void => {
+ e.stopPropagation();
+ if (e.button === 0) {
+ }
+ }
+ onMinimizeUp = (e: PointerEvent): void => {
+ e.stopPropagation();
+ if (e.button === 0) {
+ SelectionManager.SelectedDocuments().map(dv => dv.minimize());
+ document.removeEventListener("pointermove", this.onMinimizeMove);
+ document.removeEventListener("pointerup", this.onMinimizeUp);
+ }
+ }
+
onPointerDown = (e: React.PointerEvent): void => {
e.stopPropagation();
if (e.button === 0) {
@@ -107,18 +203,40 @@ export class DocumentDecorations extends React.Component {
}
}
- onLinkButtonDown = (e: React.PointerEvent): void => {
- // if ()
- // let linkMenu = new LinkMenu(SelectionManager.SelectedDocuments()[0]);
- // linkMenu.Hidden = false;
- console.log("down");
+ onLinkerButtonDown = (e: React.PointerEvent): void => {
+ e.stopPropagation();
+ document.removeEventListener("pointermove", this.onLinkerButtonMoved)
+ document.addEventListener("pointermove", this.onLinkerButtonMoved);
+ document.removeEventListener("pointerup", this.onLinkerButtonUp)
+ document.addEventListener("pointerup", this.onLinkerButtonUp);
+ }
+ onLinkerButtonUp = (e: PointerEvent): void => {
+ document.removeEventListener("pointermove", this.onLinkerButtonMoved)
+ document.removeEventListener("pointerup", this.onLinkerButtonUp)
+ e.stopPropagation();
+ }
+ onLinkerButtonMoved = (e: PointerEvent): void => {
+ if (this._linkerButton.current != null) {
+ document.removeEventListener("pointermove", this.onLinkerButtonMoved)
+ document.removeEventListener("pointerup", this.onLinkerButtonUp)
+ let dragData = new DragManager.LinkDragData(SelectionManager.SelectedDocuments()[0]);
+ DragManager.StartLinkDrag(this._linkerButton.current, dragData, e.pageX, e.pageY, {
+ handlers: {
+ dragComplete: action(() => { }),
+ },
+ hideSource: false
+ })
+ }
+ e.stopPropagation();
+ }
+
+ onLinkButtonDown = (e: React.PointerEvent): void => {
e.stopPropagation();
document.removeEventListener("pointermove", this.onLinkButtonMoved)
document.addEventListener("pointermove", this.onLinkButtonMoved);
document.removeEventListener("pointerup", this.onLinkButtonUp)
document.addEventListener("pointerup", this.onLinkButtonUp);
-
}
onLinkButtonUp = (e: PointerEvent): void => {
@@ -127,18 +245,35 @@ export class DocumentDecorations extends React.Component {
e.stopPropagation();
}
-
- onLinkButtonMoved = (e: PointerEvent): void => {
+ onLinkButtonMoved = async (e: PointerEvent) => {
if (this._linkButton.current != null) {
document.removeEventListener("pointermove", this.onLinkButtonMoved)
- document.removeEventListener("pointerup", this.onLinkButtonUp)
- let dragData = new DragManager.LinkDragData(SelectionManager.SelectedDocuments()[0]);
- DragManager.StartLinkDrag(this._linkButton.current, dragData, {
- handlers: {
- dragComplete: action(() => { }),
- },
- hideSource: false
- })
+ document.removeEventListener("pointerup", this.onLinkButtonUp);
+
+ let sourceDoc = SelectionManager.SelectedDocuments()[0].props.Document;
+ let srcTarg = sourceDoc.GetT(KeyStore.Prototype, Document)
+ let draggedDocs = (srcTarg && srcTarg != FieldWaiting) ?
+ srcTarg.GetList(KeyStore.LinkedToDocs, [] as Document[]).map(linkDoc =>
+ (linkDoc.GetT(KeyStore.LinkedToDocs, Document)) as Document) : [];
+ let draggedFromDocs = (srcTarg && srcTarg != FieldWaiting) ?
+ srcTarg.GetList(KeyStore.LinkedFromDocs, [] as Document[]).map(linkDoc =>
+ (linkDoc.GetT(KeyStore.LinkedFromDocs, Document)) as Document) : [];
+ draggedDocs.push(...draggedFromDocs);
+ if (draggedDocs.length) {
+ let moddrag = [] as Document[];
+ for (let i = 0; i < draggedDocs.length; i++) {
+ let doc = await draggedDocs[i].GetTAsync(KeyStore.AnnotationOn, Document);
+ if (doc)
+ moddrag.push(doc);
+ }
+ let dragData = new DragManager.DocumentDragData(moddrag);
+ DragManager.StartDocumentDrag([this._linkButton.current], dragData, e.x, e.y, {
+ handlers: {
+ dragComplete: action(() => { }),
+ },
+ hideSource: false
+ })
+ }
}
e.stopPropagation();
}
@@ -230,6 +365,19 @@ export class DocumentDecorations extends React.Component {
}
}
+ getValue = (): string => {
+ if (this._title === "changed" && this._documents.length > 0) {
+ let field = this._documents[0].props.Document.Get(this._fieldKey);
+ if (field instanceof TextField) {
+ return (field as TextField).GetValue();
+ }
+ else if (field instanceof NumberField) {
+ return (field as NumberField).GetValue().toString();
+ }
+ }
+ return this._title;
+ }
+
changeFlyoutContent = (): void => {
}
@@ -238,6 +386,8 @@ export class DocumentDecorations extends React.Component {
// }
render() {
var bounds = this.Bounds;
+ // console.log(this._documents.length)
+ // let test = this._documents[0].props.Document.Title;
if (this.Hidden) {
return (null);
}
@@ -255,43 +405,45 @@ export class DocumentDecorations extends React.Component {
linkButton = (<Flyout
anchorPoint={anchorPoints.RIGHT_TOP}
content={
- <LinkMenu docView={selFirst} changeFlyout={this.changeFlyoutContent}>
- </LinkMenu>
+ <LinkMenu docView={selFirst} changeFlyout={this.changeFlyoutContent} />
}>
<div className={"linkButton-" + (selFirst.props.Document.GetData(KeyStore.LinkedToDocs, ListField, []).length ? "nonempty" : "empty")} onPointerDown={this.onLinkButtonDown} >{linkCount}</div>
</Flyout>);
}
- return (
- <div className="documentDecorations">
- <div className="documentDecorations-background" style={{
- width: (bounds.r - bounds.x + this._resizeBorderWidth) + "px",
- height: (bounds.b - bounds.y + this._resizeBorderWidth) + "px",
- left: bounds.x - this._resizeBorderWidth / 2,
- top: bounds.y - this._resizeBorderWidth / 2,
- pointerEvents: this._dragging ? "none" : "all",
- zIndex: SelectionManager.SelectedDocuments().length > 1 ? 1000 : 0,
- }} onPointerDown={this.onBackgroundDown} onContextMenu={(e: React.MouseEvent) => { e.preventDefault(); e.stopPropagation() }} >
- </div>
- <div id="documentDecorations-container" style={{
- width: (bounds.r - bounds.x + this._resizeBorderWidth) + "px",
- height: (bounds.b - bounds.y + this._resizeBorderWidth + 30) + "px",
- left: bounds.x - this._resizeBorderWidth / 2,
- top: bounds.y - this._resizeBorderWidth / 2,
- }}>
- <div id="documentDecorations-topLeftResizer" className="documentDecorations-resizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
- <div id="documentDecorations-topResizer" className="documentDecorations-resizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
- <div id="documentDecorations-topRightResizer" className="documentDecorations-resizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
- <div id="documentDecorations-leftResizer" className="documentDecorations-resizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
- <div id="documentDecorations-centerCont"></div>
- <div id="documentDecorations-rightResizer" className="documentDecorations-resizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
- <div id="documentDecorations-bottomLeftResizer" className="documentDecorations-resizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
- <div id="documentDecorations-bottomResizer" className="documentDecorations-resizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
- <div id="documentDecorations-bottomRightResizer" className="documentDecorations-resizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
-
- <div title="View Links" className="linkFlyout" ref={this._linkButton}>{linkButton}</div>
-
- </div >
+ return (<div className="documentDecorations">
+ <div className="documentDecorations-background" style={{
+ width: (bounds.r - bounds.x + this._resizeBorderWidth) + "px",
+ height: (bounds.b - bounds.y + this._resizeBorderWidth) + "px",
+ left: bounds.x - this._resizeBorderWidth / 2,
+ top: bounds.y - this._resizeBorderWidth / 2,
+ pointerEvents: this._dragging ? "none" : "all",
+ zIndex: SelectionManager.SelectedDocuments().length > 1 ? 1000 : 0,
+ }} onPointerDown={this.onBackgroundDown} onContextMenu={(e: React.MouseEvent) => { e.preventDefault(); e.stopPropagation() }} >
</div>
+ <div id="documentDecorations-container" style={{
+ width: (bounds.r - bounds.x + this._resizeBorderWidth) + "px",
+ height: (bounds.b - bounds.y + this._resizeBorderWidth + this._linkBoxHeight + this._titleHeight) + "px",
+ left: bounds.x - this._resizeBorderWidth / 2,
+ top: bounds.y - this._resizeBorderWidth / 2 - this._titleHeight,
+ opacity: this._opacity
+ }}>
+ <div className="documentDecorations-minimizeButton" onPointerDown={this.onMinimizeDown}>...</div>
+ <input ref={this.keyinput} className="title" type="text" name="dynbox" value={this.getValue()} onChange={this.handleChange} onPointerDown={this.onPointerDown} onKeyPress={this.enterPressed} />
+ <div className="documentDecorations-closeButton" onPointerDown={this.onCloseDown}>X</div>
+ <div id="documentDecorations-topLeftResizer" className="documentDecorations-resizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
+ <div id="documentDecorations-topResizer" className="documentDecorations-resizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
+ <div id="documentDecorations-topRightResizer" className="documentDecorations-resizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
+ <div id="documentDecorations-leftResizer" className="documentDecorations-resizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
+ <div id="documentDecorations-centerCont"></div>
+ <div id="documentDecorations-rightResizer" className="documentDecorations-resizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
+ <div id="documentDecorations-bottomLeftResizer" className="documentDecorations-resizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
+ <div id="documentDecorations-bottomResizer" className="documentDecorations-resizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
+ <div id="documentDecorations-bottomRightResizer" className="documentDecorations-resizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
+
+ <div title="View Links" className="linkFlyout" ref={this._linkButton}> {linkButton} </div>
+ <div className="linkButton-linker" ref={this._linkerButton} onPointerDown={this.onLinkerButtonDown}>∞</div>
+ </div >
+ </div>
)
}
} \ No newline at end of file
diff --git a/src/client/views/InkingCanvas.scss b/src/client/views/InkingCanvas.scss
index 214c70280..42ae38c73 100644
--- a/src/client/views/InkingCanvas.scss
+++ b/src/client/views/InkingCanvas.scss
@@ -1,19 +1,25 @@
@import "global_variables";
+.inkingCanvas {
+ opacity:0.99;
+}
.inkingCanvas-paths-ink, .inkingCanvas-paths-markers, .inkingCanvas-noSelect, .inkingCanvas-canSelect {
position: absolute;
- top: -50000px;
- left: -50000px;
- width: 100000px;
- height: 100000px;
- .inkingCanvas-children {
- transform: translate(50000px, 50000px);
- pointer-events: none;
- }
+ top: 0;
+ left:0;
+ width: 8192px;
+ height: 8192px;
cursor:"crosshair";
pointer-events: auto;
}
+.inkingCanvas-canSelect,
+.inkingCanvas-noSelect {
+ top:-50000px;
+ left:-50000px;
+ width: 100000px;
+ height: 100000px;
+}
.inkingCanvas-noSelect {
pointer-events: none;
cursor: "arrow";
diff --git a/src/client/views/InkingCanvas.tsx b/src/client/views/InkingCanvas.tsx
index 3189e8a5f..45ca52d00 100644
--- a/src/client/views/InkingCanvas.tsx
+++ b/src/client/views/InkingCanvas.tsx
@@ -1,4 +1,4 @@
-import { action, computed, trace } from "mobx";
+import { action, computed, trace, observable, runInAction } from "mobx";
import { observer } from "mobx-react";
import { Document } from "../../fields/Document";
import { FieldWaiting } from "../../fields/Field";
@@ -14,26 +14,40 @@ import React = require("react");
interface InkCanvasProps {
getScreenTransform: () => Transform;
Document: Document;
+ children: () => JSX.Element[];
}
@observer
export class InkingCanvas extends React.Component<InkCanvasProps> {
- static InkOffset: number = 50000;
+ maxCanvasDim = 8192 / 2; // 1/2 of the maximum canvas dimension for Chrome
+ @observable inkMidX: number = 0;
+ @observable inkMidY: number = 0;
private _currentStrokeId: string = "";
public static IntersectStrokeRect(stroke: StrokeData, selRect: { left: number, top: number, width: number, height: number }): boolean {
- return stroke.pathData.reduce((inside, val) => inside ||
- (selRect.left < val.x - InkingCanvas.InkOffset && selRect.left + selRect.width > val.x - InkingCanvas.InkOffset &&
- selRect.top < val.y - InkingCanvas.InkOffset && selRect.top + selRect.height > val.y - InkingCanvas.InkOffset)
- , false as boolean);
+ return stroke.pathData.reduce((inside: boolean, val) => inside ||
+ (selRect.left < val.x && selRect.left + selRect.width > val.x &&
+ selRect.top < val.y && selRect.top + selRect.height > val.y)
+ , false);
+ }
+
+ componentDidMount() {
+ this.props.Document.GetTAsync(KeyStore.Ink, InkField, ink => runInAction(() => {
+ if (ink) {
+ let bounds = Array.from(ink.Data).reduce(([mix, max, miy, may], [id, strokeData]) =>
+ strokeData.pathData.reduce(([mix, max, miy, may], p) =>
+ [Math.min(mix, p.x), Math.max(max, p.x), Math.min(miy, p.y), Math.max(may, p.y)],
+ [mix, max, miy, may]),
+ [Number.MAX_VALUE, Number.MIN_VALUE, Number.MAX_VALUE, Number.MIN_VALUE]);
+ this.inkMidX = (bounds[0] + bounds[1]) / 2;
+ this.inkMidY = (bounds[2] + bounds[3]) / 2;
+ }
+ }));
}
@computed
get inkData(): StrokeMap {
let map = this.props.Document.GetT(KeyStore.Ink, InkField);
- if (!map || map === FieldWaiting) {
- return new Map;
- }
- return new Map(map.Data);
+ return !map || map === FieldWaiting ? new Map : new Map(map.Data);
}
set inkData(value: StrokeMap) {
@@ -63,9 +77,15 @@ export class InkingCanvas extends React.Component<InkCanvasProps> {
}
}
+ @action
onPointerUp = (e: PointerEvent): void => {
document.removeEventListener("pointermove", this.onPointerMove, true);
document.removeEventListener("pointerup", this.onPointerUp, true);
+ let coord = this.relativeCoordinatesForEvent(e.clientX, e.clientY);
+ if (Math.abs(coord.x - this.inkMidX) > 500 || Math.abs(coord.y - this.inkMidY) > 500) {
+ this.inkMidX = coord.x;
+ this.inkMidY = coord.y;
+ }
e.stopPropagation();
e.preventDefault();
}
@@ -87,8 +107,6 @@ export class InkingCanvas extends React.Component<InkCanvasProps> {
relativeCoordinatesForEvent = (ex: number, ey: number): { x: number, y: number } => {
let [x, y] = this.props.getScreenTransform().transformPoint(ex, ey);
- x += InkingCanvas.InkOffset;
- y += InkingCanvas.InkOffset;
return { x, y };
}
@@ -101,30 +119,32 @@ export class InkingCanvas extends React.Component<InkCanvasProps> {
@computed
get drawnPaths() {
- // parse data from server
let curPage = this.props.Document.GetNumber(KeyStore.CurPage, -1)
let paths = Array.from(this.inkData).reduce((paths, [id, strokeData]) => {
if (strokeData.page == -1 || strokeData.page == curPage)
paths.push(<InkingStroke key={id} id={id} line={strokeData.pathData}
+ offsetX={this.maxCanvasDim - this.inkMidX}
+ offsetY={this.maxCanvasDim - this.inkMidY}
color={strokeData.color} width={strokeData.width}
tool={strokeData.tool} deleteCallback={this.removeLine} />)
return paths;
}, [] as JSX.Element[]);
- return [<svg className={`inkingCanvas-paths-markers`} key="Markers" >
+ return [<svg className={`inkingCanvas-paths-markers`} key="Markers"
+ style={{ left: `${this.inkMidX - this.maxCanvasDim}px`, top: `${this.inkMidY - this.maxCanvasDim}px` }} >
{paths.filter(path => path.props.tool == InkTool.Highlighter)}
</svg>,
- <svg className={`inkingCanvas-paths-ink`} key="Pens" >
+ <svg className={`inkingCanvas-paths-ink`} key="Pens"
+ style={{ left: `${this.inkMidX - this.maxCanvasDim}px`, top: `${this.inkMidY - this.maxCanvasDim}px` }}>
{paths.filter(path => path.props.tool != InkTool.Highlighter)}
</svg>];
}
render() {
let svgCanvasStyle = InkingControl.Instance.selectedTool != InkTool.None ? "canSelect" : "noSelect";
-
return (
<div className="inkingCanvas" >
- <svg className={`inkingCanvas-${svgCanvasStyle}`} onPointerDown={this.onPointerDown} />
- {(this.props.children as any)() /* bcz: is there a better way to know that children is a function? */}
+ <div className={`inkingCanvas-${svgCanvasStyle}`} onPointerDown={this.onPointerDown} />
+ {this.props.children()}
{this.drawnPaths}
</div >
)
diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx
index 52111c711..615f8af7e 100644
--- a/src/client/views/InkingStroke.tsx
+++ b/src/client/views/InkingStroke.tsx
@@ -6,6 +6,8 @@ import React = require("react");
interface StrokeProps {
+ offsetX: number;
+ offsetY: number;
id: string;
line: Array<{ x: number, y: number }>;
color: string;
@@ -28,7 +30,7 @@ export class InkingStroke extends React.Component<StrokeProps> {
}
parseData = (line: Array<{ x: number, y: number }>): string => {
- return !line.length ? "" : "M " + line.map(p => p.x + " " + p.y).join(" L ");
+ return !line.length ? "" : "M " + line.map(p => (p.x + this.props.offsetX) + " " + (p.y + this.props.offsetY)).join(" L ");
}
createStyle() {
@@ -43,14 +45,13 @@ export class InkingStroke extends React.Component<StrokeProps> {
}
}
-
render() {
let pathStyle = this.createStyle();
let pathData = this.parseData(this.props.line);
+ let pointerEvents: any = InkingControl.Instance.selectedTool == InkTool.Eraser ? "all" : "none";
return (
- <path className={(this._strokeTool === InkTool.Highlighter) ? "highlight" : ""}
- d={pathData} style={{ ...pathStyle, pointerEvents: "all" }} strokeLinejoin="round" strokeLinecap="round"
+ <path d={pathData} style={{ ...pathStyle, pointerEvents: pointerEvents }} strokeLinejoin="round" strokeLinecap="round"
onPointerOver={this.deleteStroke} onPointerDown={this.deleteStroke} />
)
}
diff --git a/src/client/views/Main.scss b/src/client/views/Main.scss
index 698a9c617..fe7f007b0 100644
--- a/src/client/views/Main.scss
+++ b/src/client/views/Main.scss
@@ -4,9 +4,12 @@ html,
body {
width: 100%;
height: 100%;
- overflow: hidden;
+ overflow: auto;
font-family: $sans-serif;
margin: 0;
+ position:absolute;
+ top: 0;
+ left:0;
}
#dash-title {
@@ -154,15 +157,40 @@ button:hover {
cursor: pointer;
}
}
+#root {
+ overflow: visible;
+}
#main-div {
width:100%;
height:100%;
position:absolute;
+ top: 0;
+ left:0;
+ overflow: scroll;
+}
+.mainDiv-textInput {
+ background:pink;
+ width: 200px;
+ height: 200px;
+ position:absolute;
+ overflow: visible;
+ top: 0;
+ left: 0;
+ .formattedTextBox-cont {
+ background:pink;
+ width: 100%;
+ height: 100%;
+ position:absolute;
+ top: 0;
+ left: 0;
+ }
}
#mainContent-div {
width:100%;
height:100%;
position:absolute;
+ top: 0;
+ left:0;
}
#add-options-content {
display: table;
diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index 6f66f8f38..c9467e130 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -1,56 +1,42 @@
-import { action, configure, observable, runInAction, trace, computed, reaction } from 'mobx';
+import { IconName, library } from '@fortawesome/fontawesome-svg-core';
+import { faFilePdf, faFilm, faFont, faGlobeAsia, faImage, faMusic, faObjectGroup, faPenNib, faRedoAlt, faTable, faTree, faUndoAlt } from '@fortawesome/free-solid-svg-icons';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { action, computed, configure, observable, runInAction, trace } from 'mobx';
+import { observer } from 'mobx-react';
import "normalize.css";
import * as React from 'react';
import * as ReactDOM from 'react-dom';
+import Measure from 'react-measure';
+import * as request from 'request';
import { Document } from '../../fields/Document';
+import { Field, FieldWaiting, Opt } from '../../fields/Field';
import { KeyStore } from '../../fields/KeyStore';
-import "./Main.scss";
+import { ListField } from '../../fields/ListField';
+import { WorkspacesMenu } from '../../server/authentication/controllers/WorkspacesMenu';
+import { CurrentUserUtils } from '../../server/authentication/models/current_user_utils';
import { MessageStore } from '../../server/Message';
+import { RouteStore } from '../../server/RouteStore';
+import { ServerUtils } from '../../server/ServerUtil';
import { Utils } from '../../Utils';
-import * as request from 'request'
-import * as rp from 'request-promise'
import { Documents } from '../documents/Documents';
+import { ColumnAttributeModel } from '../northstar/core/attribute/AttributeModel';
+import { AttributeTransformationModel } from '../northstar/core/attribute/AttributeTransformationModel';
+import { Gateway, Settings } from '../northstar/manager/Gateway';
+import { AggregateFunction, Catalog } from '../northstar/model/idea/idea';
+import '../northstar/model/ModelExtensions';
+import { HistogramOperation } from '../northstar/operations/HistogramOperation';
+import '../northstar/utils/Extensions';
import { Server } from '../Server';
import { setupDrag } from '../util/DragManager';
import { Transform } from '../util/Transform';
import { UndoManager } from '../util/UndoManager';
-import { WorkspacesMenu } from '../../server/authentication/controllers/WorkspacesMenu';
import { CollectionDockingView } from './collections/CollectionDockingView';
import { ContextMenu } from './ContextMenu';
import { DocumentDecorations } from './DocumentDecorations';
-import { DocumentView } from './nodes/DocumentView';
-import "./Main.scss";
-import { observer } from 'mobx-react';
import { InkingControl } from './InkingControl';
-import { RouteStore } from '../../server/RouteStore';
-import { library, IconName } from '@fortawesome/fontawesome-svg-core';
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { faFont } from '@fortawesome/free-solid-svg-icons';
-import { faImage } from '@fortawesome/free-solid-svg-icons';
-import { faFilePdf } from '@fortawesome/free-solid-svg-icons';
-import { faObjectGroup } from '@fortawesome/free-solid-svg-icons';
-import { faTable } from '@fortawesome/free-solid-svg-icons';
-import { faGlobeAsia } from '@fortawesome/free-solid-svg-icons';
-import { faUndoAlt } from '@fortawesome/free-solid-svg-icons';
-import { faRedoAlt } from '@fortawesome/free-solid-svg-icons';
-import { faPenNib } from '@fortawesome/free-solid-svg-icons';
-import { faFilm } from '@fortawesome/free-solid-svg-icons';
-import { faMusic } from '@fortawesome/free-solid-svg-icons';
-import { faTree } from '@fortawesome/free-solid-svg-icons';
-import Measure from 'react-measure';
-import { DashUserModel } from '../../server/authentication/models/user_model';
-import { ServerUtils } from '../../server/ServerUtil';
-import { CurrentUserUtils } from '../../server/authentication/models/current_user_utils';
-import { Field, Opt, FieldWaiting } from '../../fields/Field';
-import { ListField } from '../../fields/ListField';
-import { Gateway, Settings } from '../northstar/manager/Gateway';
-import { Catalog, Schema, Attribute, AttributeGroup, AggregateFunction } from '../northstar/model/idea/idea';
-import { ArrayUtil } from '../northstar/utils/ArrayUtil';
-import '../northstar/model/ModelExtensions'
-import '../northstar/utils/Extensions'
-import { HistogramOperation } from '../northstar/operations/HistogramOperation';
-import { AttributeTransformationModel } from '../northstar/core/attribute/AttributeTransformationModel';
-import { ColumnAttributeModel } from '../northstar/core/attribute/AttributeModel';
+import "./Main.scss";
+import { DocumentView } from './nodes/DocumentView';
+import { FormattedTextBox } from './nodes/FormattedTextBox';
@observer
export class Main extends React.Component {
@@ -218,6 +204,30 @@ export class Main extends React.Component {
focusDocument = (doc: Document) => { }
noScaling = () => 1;
+ @observable _textDoc?: Document = undefined;
+ _textRect: any;
+ @action
+ SetTextDoc(textDoc?: Document, div?: HTMLDivElement) {
+ this._textDoc = undefined;
+ this._textDoc = textDoc;
+ if (div)
+ this._textRect = div.getBoundingClientRect();
+ }
+
+ @computed
+ get activeTextBox() {
+ if (this._textDoc) {
+ let x: number = this._textRect.x;
+ let y: number = this._textRect.y;
+ let w: number = this._textRect.width;
+ let h: number = this._textRect.height;
+ return <div className="mainDiv-textInput" style={{ transform: `translate(${x}px, ${y}px)`, width: `${w}px`, height: `${h}px` }} >
+ <FormattedTextBox fieldKey={KeyStore.Archives} doc={this._textDoc} isSelected={() => true} select={() => { }} isTopMost={true} selectOnLoad={true} bindings={undefined} />
+ </ div>
+ }
+ else return (null);
+ }
+
@computed
get mainContent() {
return !this.mainContainer ? (null) :
@@ -246,7 +256,7 @@ export class Main extends React.Component {
let addTextNode = action(() => Documents.TextDocument({ width: 200, height: 200, title: "a text note" }))
let addColNode = action(() => Documents.FreeformDocument([], { width: 200, height: 200, title: "a freeform collection" }));
let addSchemaNode = action(() => Documents.SchemaDocument([], { width: 200, height: 200, title: "a schema collection" }));
- let addTreeNode = action(() => Documents.TreeDocument(this._northstarSchemas, { width: 250, height: 400, title: "northstar schemas" }));
+ let addTreeNode = action(() => Documents.TreeDocument(this._northstarSchemas, { width: 250, height: 400, title: "northstar schemas", copyDraggedItems: true }));
let addVideoNode = action(() => Documents.VideoDocument(videourl, { width: 200, height: 200, title: "video node" }));
let addPDFNode = action(() => Documents.PdfDocument(pdfurl, { width: 200, height: 200, title: "a schema collection" }));
let addImageNode = action(() => Documents.ImageDocument(imgurl, { width: 200, height: 200, title: "an image of a cat" }));
@@ -311,24 +321,27 @@ export class Main extends React.Component {
isShown={this.areWorkspacesShown} toggle={this.toggleWorkspaces} />
}
return (
- <div id="main-div">
- <DocumentDecorations />
- <Measure onResize={(r: any) => runInAction(() => {
- this.pwidth = r.entry.width;
- this.pheight = r.entry.height;
- })}>
- {({ measureRef }) =>
- <div ref={measureRef} id="mainContent-div">
- {this.mainContent}
- </div>
- }
- </Measure>
- <ContextMenu />
- {this.nodesMenu}
- {this.miscButtons}
- {workspaceMenu}
- <InkingControl />
- </div>
+ [
+ <div id="main-div">
+ <DocumentDecorations />
+ <Measure onResize={(r: any) => runInAction(() => {
+ this.pwidth = r.entry.width;
+ this.pheight = r.entry.height;
+ })}>
+ {({ measureRef }) =>
+ <div ref={measureRef} id="mainContent-div">
+ {this.mainContent}
+ </div>
+ }
+ </Measure>
+ <ContextMenu />
+ {this.nodesMenu}
+ {this.miscButtons}
+ {workspaceMenu}
+ <InkingControl />
+ </div>,
+ this.activeTextBox
+ ]
);
}
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..921ee4591 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -200,7 +200,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
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]),
+ DragManager.StartDocumentDrag([tab], new DragManager.DocumentDragData([f as Document]), e.pageX, e.pageY,
{
handlers: {
dragComplete: action(() => { }),
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/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 0ff6c3b40..b10aaba98 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -86,12 +86,10 @@ export class CollectionSchemaView extends CollectionViewBase {
)
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 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;
@@ -118,12 +116,22 @@ export class CollectionSchemaView extends CollectionViewBase {
return field || "";
}}
SetValue={(value: string) => {
- return applyToDoc(props.doc, value);
+ let script = CompileScript(value, { addReturn: true, params: { this: "Document" } });
+ if (!script.compiled) {
+ return false;
+ }
+ return applyToDoc(props.doc, script.run);
}}
OnFillDown={(value: string) => {
+ let script = CompileScript(value, { addReturn: true, params: { this: "Document" } });
+ 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));
}
})
}}>
diff --git a/src/client/views/collections/CollectionTreeView.scss b/src/client/views/collections/CollectionTreeView.scss
index 8ec996326..f2affbf55 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..0b12f11fd 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -18,6 +18,7 @@ import React = require("react")
export interface TreeViewProps {
document: Document;
deleteDoc: (doc: Document) => void;
+ copyOnDrag: boolean;
}
export enum BulletType {
@@ -63,7 +64,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, (containingCollection: CollectionView) => this.props.deleteDoc(this.props.document), this.props.copyOnDrag);
let editableView = (titleString: string) =>
(<EditableView
display={"inline"}
@@ -85,13 +86,12 @@ class TreeView extends React.Component<TreeViewProps> {
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} />)}
+ {children.Data.map(value => <TreeView key={value.Id} document={value} deleteDoc={this.remove} copyOnDrag={this.props.copyOnDrag} />)}
</ul>
}
else bulletType = BulletType.Collapsed;
@@ -118,10 +118,11 @@ 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} copyOnDrag={copyOnDrag} />)
)
return (
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/CollectionViewBase.tsx b/src/client/views/collections/CollectionViewBase.tsx
index 316d20c9d..7cf49e215 100644
--- a/src/client/views/collections/CollectionViewBase.tsx
+++ b/src/client/views/collections/CollectionViewBase.tsx
@@ -17,6 +17,8 @@ import { NumberField } from "../../../fields/NumberField";
import request = require("request");
import { ServerUtils } from "../../../server/ServerUtil";
import { Server } from "../../Server";
+import { CollectionDockingView } from "./CollectionDockingView";
+import { runReactions } from "mobx/lib/internal";
export interface CollectionViewProps {
fieldKey: Key;
@@ -80,17 +82,36 @@ 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) {
+ if (added && de.data.removeDocument && !de.data.aliasOnDrop && !de.data.copyOnDrop) {
de.data.removeDocument(this.props.CollectionView);
}
e.stopPropagation();
return added;
}
+ if (de.data instanceof DragManager.LinkDragData) {
+ let sourceDoc: Document = de.data.linkSourceDocumentView.props.Document;
+ if (sourceDoc) runInAction(() => {
+ let srcTarg = sourceDoc.GetT(KeyStore.Prototype, Document)
+ if (srcTarg && srcTarg != FieldWaiting) {
+ let linkDocs = srcTarg.GetList(KeyStore.LinkedToDocs, [] as Document[]);
+ linkDocs.map(linkDoc => {
+ let targDoc = linkDoc.GetT(KeyStore.LinkedToDocs, Document);
+ if (targDoc && targDoc != FieldWaiting) {
+ let dropdoc = targDoc.MakeDelegate();
+ de.data.droppedDocuments.push(dropdoc);
+ this.props.addDocument(dropdoc, false);
+ }
+ })
+ }
+ })
+ return true;
+ }
return false;
}
@@ -107,6 +128,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')) {
@@ -156,7 +178,6 @@ export class CollectionViewBase extends React.Component<SubCollectionViewProps>
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) => {
let type = res.headers["content-type"];
if (type) {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
index e84f0c5ad..3dfd74ec8 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
@@ -23,10 +23,10 @@ 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}` }}
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..1189dd4e8 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";
@@ -11,54 +11,60 @@ import { CollectionViewProps } from "../CollectionViewBase";
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];
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
index 81d21d89a..79d520069 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
@@ -26,7 +26,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 +41,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..2a86d0ee1 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -20,6 +20,8 @@ import React = require("react");
import v5 = require("uuid/v5");
import { CollectionFreeFormRemoteCursors } from "./CollectionFreeFormRemoteCursors";
import { PreviewCursor } from "./PreviewCursor";
+import { NumberField } from "../../../../fields/NumberField";
+import { Main } from "../../Main";
@observer
export class CollectionFreeFormView extends CollectionViewBase {
@@ -73,20 +75,27 @@ export class CollectionFreeFormView extends CollectionViewBase {
@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);
+ let droppedDocs = de.data.droppedDocuments as Document[];
+ let xoff = de.data.xOffset as number || 0;
+ let yoff = de.data.yOffset as number || 0;
+ if (droppedDocs && droppedDocs.length) {
+ let screenX = de.x - xoff;
+ let screenY = de.y - yoff;
const [x, y] = this.getTransform().transformPoint(screenX, screenY);
- let dragDoc = de.data.draggedDocuments[0];
+ let dragDoc = de.data.droppedDocuments[0];
let dragX = dragDoc.GetNumber(KeyStore.X, 0);
let dragY = dragDoc.GetNumber(KeyStore.Y, 0);
- de.data.draggedDocuments.map(d => {
+ 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));
- if (!d.GetNumber(KeyStore.Width, 0)) {
+ 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);
@@ -177,6 +186,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));
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..df150a045 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -102,7 +102,7 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
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();
@@ -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
@@ -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() {
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/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index d52b662bd..1a0f0cbbd 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -66,8 +66,12 @@ export class CollectionFreeFormDocumentView extends React.Component<DocumentView
return <DocumentView {...this.props}
ContentScaling={this.contentScaling}
ScreenToLocalTransform={this.getTransform}
+ PanelWidth={this.panelWidth}
+ PanelHeight={this.panelHeight}
/>
}
+ panelWidth = () => this.props.Document.GetBoolean(KeyStore.Minimized, false) ? 10 : this.props.PanelWidth();
+ panelHeight = () => this.props.Document.GetBoolean(KeyStore.Minimized, false) ? 10 : this.props.PanelHeight();
render() {
return (
diff --git a/src/client/views/nodes/DocumentView.scss b/src/client/views/nodes/DocumentView.scss
index 85a115f1c..5126e69f9 100644
--- a/src/client/views/nodes/DocumentView.scss
+++ b/src/client/views/nodes/DocumentView.scss
@@ -1,23 +1,43 @@
@import "../global_variables";
+
.documentView-node {
position: absolute;
+ top: 0;
+ left:0;
background: $light-color; //overflow: hidden;
+
&.minimized {
width: 30px;
height: 30px;
}
+
.top {
background: #232323;
height: 20px;
cursor: pointer;
}
+
.content {
padding: 20px 20px;
height: auto;
box-sizing: border-box;
}
+
.scroll-box {
overflow-y: scroll;
height: calc(100% - 20px);
}
}
+
+.minimized-box {
+ height: 10px;
+ width: 10px;
+ border-radius: 2px;
+ background: $dark-color
+}
+
+.minimized-box:hover {
+ background: $main-accent;
+ transform: scale(1.15);
+ cursor: pointer;
+} \ No newline at end of file
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 1195128dc..7514e782d 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -1,11 +1,13 @@
import { action, computed, IReactionDisposer, reaction, runInAction } from "mobx";
import { observer } from "mobx-react";
import { Document } from "../../../fields/Document";
-import { Field, Opt, FieldWaiting } from "../../../fields/Field";
+import { Field, FieldWaiting, Opt } from "../../../fields/Field";
import { Key } from "../../../fields/Key";
import { KeyStore } from "../../../fields/KeyStore";
import { ListField } from "../../../fields/ListField";
+import { BooleanField } from "../../../fields/BooleanField";
import { TextField } from "../../../fields/TextField";
+import { ServerUtils } from "../../../server/ServerUtil";
import { Utils } from "../../../Utils";
import { Documents } from "../../documents/Documents";
import { DocumentManager } from "../../util/DocumentManager";
@@ -18,8 +20,6 @@ import { ContextMenu } from "../ContextMenu";
import { DocumentContentsView } from "./DocumentContentsView";
import "./DocumentView.scss";
import React = require("react");
-import { ServerUtils } from "../../../server/ServerUtil";
-
export interface DocumentViewProps {
ContainingCollectionView: Opt<CollectionView>;
@@ -35,8 +35,8 @@ export interface DocumentViewProps {
SelectOnLoad: boolean;
}
export interface JsxArgs extends DocumentViewProps {
- Keys: { [name: string]: Key }
- Fields: { [name: string]: Field }
+ Keys: { [name: string]: Key };
+ Fields: { [name: string]: Field };
}
/*
@@ -55,16 +55,16 @@ Example usage of this function:
}
*/
export function FakeJsxArgs(keys: string[], fields: string[] = []): JsxArgs {
- let Keys: { [name: string]: any } = {}
- let Fields: { [name: string]: any } = {}
+ let Keys: { [name: string]: any } = {};
+ let Fields: { [name: string]: any } = {};
for (const key of keys) {
- let fn = () => { }
- Object.defineProperty(fn, "name", { value: key + "Key" })
+ let fn = () => { };
+ Object.defineProperty(fn, "name", { value: key + "Key" });
Keys[key] = fn;
}
for (const field of fields) {
- let fn = () => { }
- Object.defineProperty(fn, "name", { value: field })
+ let fn = () => { };
+ Object.defineProperty(fn, "name", { value: field });
Fields[field] = fn;
}
let args: JsxArgs = {
@@ -85,53 +85,94 @@ export interface JsxBindings {
[prop: string]: any;
}
-
-
@observer
export class DocumentView extends React.Component<DocumentViewProps> {
private _mainCont = React.createRef<HTMLDivElement>();
private _downX: number = 0;
private _downY: number = 0;
+
private _reactionDisposer: Opt<IReactionDisposer>;
- @computed get active(): boolean { return SelectionManager.IsSelected(this) || !this.props.ContainingCollectionView || this.props.ContainingCollectionView.active(); }
- @computed get topMost(): boolean { return !this.props.ContainingCollectionView || this.props.ContainingCollectionView.collectionViewType == CollectionViewType.Docking; }
- @computed get layout(): string { return this.props.Document.GetText(KeyStore.Layout, "<p>Error loading layout data</p>"); }
- @computed get layoutKeys(): Key[] { return this.props.Document.GetData(KeyStore.LayoutKeys, ListField, new Array<Key>()); }
- @computed get layoutFields(): Key[] { return this.props.Document.GetData(KeyStore.LayoutFields, ListField, new Array<Key>()); }
- screenRect = (): ClientRect | DOMRect => this._mainCont.current ? this._mainCont.current.getBoundingClientRect() : new DOMRect();
+ @computed get active(): boolean {
+ return (
+ SelectionManager.IsSelected(this) ||
+ !this.props.ContainingCollectionView ||
+ this.props.ContainingCollectionView.active()
+ );
+ }
+ @computed get topMost(): boolean {
+ return (
+ !this.props.ContainingCollectionView ||
+ this.props.ContainingCollectionView.collectionViewType ==
+ CollectionViewType.Docking
+ );
+ }
+ @computed get layout(): string {
+ return this.props.Document.GetText(
+ KeyStore.Layout,
+ "<p>Error loading layout data</p>"
+ );
+ }
+ @computed get layoutKeys(): Key[] {
+ return this.props.Document.GetData(
+ KeyStore.LayoutKeys,
+ ListField,
+ new Array<Key>()
+ );
+ }
+ @computed get layoutFields(): Key[] {
+ return this.props.Document.GetData(
+ KeyStore.LayoutFields,
+ ListField,
+ new Array<Key>()
+ );
+ }
+ screenRect = (): ClientRect | DOMRect =>
+ this._mainCont.current
+ ? this._mainCont.current.getBoundingClientRect()
+ : new DOMRect();
onPointerDown = (e: React.PointerEvent): void => {
this._downX = e.clientX;
this._downY = e.clientY;
if (e.shiftKey && e.buttons === 2) {
if (this.props.isTopMost) {
this.startDragging(e.pageX, e.pageY, e.altKey || e.ctrlKey);
- }
- else CollectionDockingView.Instance.StartOtherDrag([this.props.Document], e);
+ } else
+ CollectionDockingView.Instance.StartOtherDrag([this.props.Document], e);
e.stopPropagation();
} else {
if (this.active && !e.isDefaultPrevented()) {
e.stopPropagation();
- document.removeEventListener("pointermove", this.onPointerMove)
+ document.removeEventListener("pointermove", this.onPointerMove);
document.addEventListener("pointermove", this.onPointerMove);
- document.removeEventListener("pointerup", this.onPointerUp)
+ document.removeEventListener("pointerup", this.onPointerUp);
document.addEventListener("pointerup", this.onPointerUp);
}
}
- }
+ };
private dropDisposer?: DragManager.DragDropDisposer;
componentDidMount() {
if (this._mainCont.current) {
- this.dropDisposer = DragManager.MakeDropTarget(this._mainCont.current, { handlers: { drop: this.drop.bind(this) } });
+ this.dropDisposer = DragManager.MakeDropTarget(this._mainCont.current, {
+ handlers: { drop: this.drop.bind(this) }
+ });
}
- runInAction(() => DocumentManager.Instance.DocumentViews.push(this))
+ runInAction(() => DocumentManager.Instance.DocumentViews.push(this));
this._reactionDisposer = reaction(
- () => this.props.ContainingCollectionView && this.props.ContainingCollectionView.SelectedDocs.slice(),
+ () =>
+ this.props.ContainingCollectionView &&
+ this.props.ContainingCollectionView.SelectedDocs.slice(),
() => {
- if (this.props.ContainingCollectionView && this.props.ContainingCollectionView.SelectedDocs.indexOf(this.props.Document.Id) != -1)
+ if (
+ this.props.ContainingCollectionView &&
+ this.props.ContainingCollectionView.SelectedDocs.indexOf(
+ this.props.Document.Id
+ ) != -1
+ )
SelectionManager.SelectDoc(this, true);
- });
+ }
+ );
}
componentDidUpdate() {
@@ -139,7 +180,9 @@ export class DocumentView extends React.Component<DocumentViewProps> {
this.dropDisposer();
}
if (this._mainCont.current) {
- this.dropDisposer = DragManager.MakeDropTarget(this._mainCont.current, { handlers: { drop: this.drop.bind(this) } });
+ this.dropDisposer = DragManager.MakeDropTarget(this._mainCont.current, {
+ handlers: { drop: this.drop.bind(this) }
+ });
}
}
@@ -147,7 +190,12 @@ export class DocumentView extends React.Component<DocumentViewProps> {
if (this.dropDisposer) {
this.dropDisposer();
}
- runInAction(() => DocumentManager.Instance.DocumentViews.splice(DocumentManager.Instance.DocumentViews.indexOf(this), 1))
+ runInAction(() =>
+ DocumentManager.Instance.DocumentViews.splice(
+ DocumentManager.Instance.DocumentViews.indexOf(this),
+ 1
+ )
+ );
if (this._reactionDisposer) {
this._reactionDisposer();
}
@@ -155,22 +203,28 @@ export class DocumentView extends React.Component<DocumentViewProps> {
startDragging(x: number, y: number, dropAliasOfDraggedDoc: boolean) {
if (this._mainCont.current) {
- const [left, top] = this.props.ScreenToLocalTransform().inverse().transformPoint(0, 0);
+ const [left, top] = this.props
+ .ScreenToLocalTransform()
+ .inverse()
+ .transformPoint(0, 0);
let dragData = new DragManager.DocumentDragData([this.props.Document]);
dragData.aliasOnDrop = dropAliasOfDraggedDoc;
dragData.xOffset = x - left;
dragData.yOffset = y - top;
dragData.removeDocument = (dropCollectionView: CollectionView) => {
- if (this.props.RemoveDocument && this.props.ContainingCollectionView !== dropCollectionView) {
+ if (
+ this.props.RemoveDocument &&
+ this.props.ContainingCollectionView !== dropCollectionView
+ ) {
this.props.RemoveDocument(this.props.Document);
}
- }
- DragManager.StartDocumentDrag([this._mainCont.current], dragData, {
+ };
+ DragManager.StartDocumentDrag([this._mainCont.current], dragData, x, y, {
handlers: {
- dragComplete: action(() => { }),
+ dragComplete: action(() => { })
},
hideSource: !dropAliasOfDraggedDoc
- })
+ });
}
}
@@ -178,52 +232,77 @@ export class DocumentView extends React.Component<DocumentViewProps> {
if (e.cancelBubble) {
return;
}
- if (Math.abs(this._downX - e.clientX) > 3 || Math.abs(this._downY - e.clientY) > 3) {
- document.removeEventListener("pointermove", this.onPointerMove)
+ if (
+ Math.abs(this._downX - e.clientX) > 3 ||
+ Math.abs(this._downY - e.clientY) > 3
+ ) {
+ document.removeEventListener("pointermove", this.onPointerMove);
document.removeEventListener("pointerup", this.onPointerUp);
if (!this.topMost || e.buttons == 2 || e.altKey) {
- this.startDragging(e.x, e.y, e.ctrlKey || e.altKey);
+ this.startDragging(this._downX, this._downY, e.ctrlKey || e.altKey);
}
}
e.stopPropagation();
e.preventDefault();
- }
+ };
onPointerUp = (e: PointerEvent): void => {
- document.removeEventListener("pointermove", this.onPointerMove)
- document.removeEventListener("pointerup", this.onPointerUp)
+ document.removeEventListener("pointermove", this.onPointerMove);
+ document.removeEventListener("pointerup", this.onPointerUp);
e.stopPropagation();
- if (Math.abs(e.clientX - this._downX) < 4 && Math.abs(e.clientY - this._downY) < 4) {
+ if (!SelectionManager.IsSelected(this) &&
+ Math.abs(e.clientX - this._downX) < 4 &&
+ Math.abs(e.clientY - this._downY) < 4
+ ) {
SelectionManager.SelectDoc(this, e.ctrlKey);
}
- }
+ };
stopPropogation = (e: React.SyntheticEvent) => {
e.stopPropagation();
- }
+ };
deleteClicked = (): void => {
if (this.props.RemoveDocument) {
this.props.RemoveDocument(this.props.Document);
}
- }
+ };
fieldsClicked = (e: React.MouseEvent): void => {
if (this.props.AddDocument) {
- this.props.AddDocument(Documents.KVPDocument(this.props.Document, { width: 300, height: 300 }), false);
+ this.props.AddDocument(
+ Documents.KVPDocument(this.props.Document, { width: 300, height: 300 }),
+ false
+ );
}
- }
+ };
fullScreenClicked = (e: React.MouseEvent): void => {
CollectionDockingView.Instance.OpenFullScreen(this.props.Document);
ContextMenu.Instance.clearItems();
- ContextMenu.Instance.addItem({ description: "Close Full Screen", event: this.closeFullScreenClicked });
- ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15)
- }
+ ContextMenu.Instance.addItem({
+ description: "Close Full Screen",
+ event: this.closeFullScreenClicked
+ });
+ ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15);
+ };
closeFullScreenClicked = (e: React.MouseEvent): void => {
CollectionDockingView.Instance.CloseFullScreen();
ContextMenu.Instance.clearItems();
- ContextMenu.Instance.addItem({ description: "Full Screen", event: this.fullScreenClicked })
- ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15)
- }
+ ContextMenu.Instance.addItem({
+ description: "Full Screen",
+ event: this.fullScreenClicked
+ });
+ ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15);
+ };
+
+ @action
+ public minimize = (): void => {
+ this.props.Document.SetData(
+ KeyStore.Minimized,
+ true as boolean,
+ BooleanField
+ );
+ SelectionManager.DeselectAll();
+ };
@action
drop = (e: Event, de: DragManager.DropEvent) => {
@@ -235,23 +314,37 @@ export class DocumentView extends React.Component<DocumentViewProps> {
}
let linkDoc: Document = new Document();
- destDoc.GetTAsync(KeyStore.Prototype, Document).then((protoDest) =>
- sourceDoc.GetTAsync(KeyStore.Prototype, Document).then((protoSrc) => runInAction(() => {
- linkDoc.Set(KeyStore.Title, new TextField("New Link"));
- linkDoc.Set(KeyStore.LinkDescription, new TextField(""));
- linkDoc.Set(KeyStore.LinkTags, new TextField("Default"));
+ destDoc.GetTAsync(KeyStore.Prototype, Document).then(protoDest =>
+ sourceDoc.GetTAsync(KeyStore.Prototype, Document).then(protoSrc =>
+ runInAction(() => {
+ linkDoc.Set(KeyStore.Title, new TextField("New Link"));
+ linkDoc.Set(KeyStore.LinkDescription, new TextField(""));
+ linkDoc.Set(KeyStore.LinkTags, new TextField("Default"));
- let dstTarg = (protoDest ? protoDest : destDoc);
- let srcTarg = (protoSrc ? protoSrc : sourceDoc);
- linkDoc.Set(KeyStore.LinkedToDocs, dstTarg);
- linkDoc.Set(KeyStore.LinkedFromDocs, srcTarg);
- dstTarg.GetOrCreateAsync(KeyStore.LinkedFromDocs, ListField, field => { (field as ListField<Document>).Data.push(linkDoc) })
- srcTarg.GetOrCreateAsync(KeyStore.LinkedToDocs, ListField, field => { (field as ListField<Document>).Data.push(linkDoc) })
- }))
- )
+ let dstTarg = protoDest ? protoDest : destDoc;
+ let srcTarg = protoSrc ? protoSrc : sourceDoc;
+ linkDoc.Set(KeyStore.LinkedToDocs, dstTarg);
+ linkDoc.Set(KeyStore.LinkedFromDocs, srcTarg);
+ dstTarg.GetOrCreateAsync(
+ KeyStore.LinkedFromDocs,
+ ListField,
+ field => {
+ (field as ListField<Document>).Data.push(linkDoc);
+ }
+ );
+ srcTarg.GetOrCreateAsync(
+ KeyStore.LinkedToDocs,
+ ListField,
+ field => {
+ (field as ListField<Document>).Data.push(linkDoc);
+ }
+ );
+ })
+ )
+ );
e.stopPropagation();
}
- }
+ };
onDrop = (e: React.DragEvent) => {
if (e.isDefaultPrevented()) {
@@ -265,22 +358,43 @@ export class DocumentView extends React.Component<DocumentViewProps> {
e.stopPropagation();
e.preventDefault();
}
- }
+ };
@action
onContextMenu = (e: React.MouseEvent): void => {
e.stopPropagation();
- let moved = Math.abs(this._downX - e.clientX) > 3 || Math.abs(this._downY - e.clientY) > 3;
+ let moved =
+ Math.abs(this._downX - e.clientX) > 3 ||
+ Math.abs(this._downY - e.clientY) > 3;
if (moved || e.isDefaultPrevented()) {
- e.preventDefault()
+ e.preventDefault();
return;
}
- e.preventDefault()
+ e.preventDefault();
- ContextMenu.Instance.addItem({ description: "Full Screen", event: this.fullScreenClicked })
- ContextMenu.Instance.addItem({ description: "Fields", event: this.fieldsClicked })
- ContextMenu.Instance.addItem({ description: "Center", event: () => this.props.focus(this.props.Document) })
- ContextMenu.Instance.addItem({ description: "Open Right", event: () => CollectionDockingView.Instance.AddRightSplit(this.props.Document) })
+ if (!this.isMinimized()) {
+ ContextMenu.Instance.addItem({
+ description: "Minimize",
+ event: this.minimize
+ });
+ }
+ ContextMenu.Instance.addItem({
+ description: "Full Screen",
+ event: this.fullScreenClicked
+ });
+ ContextMenu.Instance.addItem({
+ description: "Fields",
+ event: this.fieldsClicked
+ });
+ ContextMenu.Instance.addItem({
+ description: "Center",
+ event: () => this.props.focus(this.props.Document)
+ });
+ ContextMenu.Instance.addItem({
+ description: "Open Right",
+ event: () =>
+ CollectionDockingView.Instance.AddRightSplit(this.props.Document)
+ });
ContextMenu.Instance.addItem({
description: "Copy URL",
event: () => {
@@ -294,48 +408,95 @@ export class DocumentView extends React.Component<DocumentViewProps> {
}
});
//ContextMenu.Instance.addItem({ description: "Docking", event: () => this.props.Document.SetNumber(KeyStore.ViewType, CollectionViewType.Docking) })
- ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15)
+ ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15);
if (!this.topMost) {
// DocumentViews should stop propagation of this event
e.stopPropagation();
}
- ContextMenu.Instance.addItem({ description: "Delete", event: this.deleteClicked })
- ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15)
+ ContextMenu.Instance.addItem({
+ description: "Delete",
+ event: this.deleteClicked
+ });
+ ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15);
SelectionManager.SelectDoc(this, e.ctrlKey);
- }
+ };
+ isMinimized = () => {
+ let field = this.props.Document.GetT(KeyStore.Minimized, BooleanField);
+ if (field && field !== FieldWaiting) {
+ return field.Data;
+ }
+ };
+
+ @action
+ expand = () => {
+ this.props.Document.SetData(
+ KeyStore.Minimized,
+ false as boolean,
+ BooleanField
+ );
+ };
isSelected = () => {
return SelectionManager.IsSelected(this);
- }
+ };
select = (ctrlPressed: boolean) => {
- SelectionManager.SelectDoc(this, ctrlPressed)
- }
+ SelectionManager.SelectDoc(this, ctrlPressed);
+ };
render() {
if (!this.props.Document) {
- return (null);
+ return null;
}
+
var scaling = this.props.ContentScaling();
var nativeWidth = this.props.Document.GetNumber(KeyStore.NativeWidth, 0);
var nativeHeight = this.props.Document.GetNumber(KeyStore.NativeHeight, 0);
- var backgroundcolor = this.props.Document.GetText(KeyStore.BackgroundColor, "");
- return (
- <div className="documentView-node" ref={this._mainCont}
- style={{
- background: backgroundcolor,
- width: nativeWidth > 0 ? nativeWidth.toString() + "px" : "100%",
- height: nativeHeight > 0 ? nativeHeight.toString() + "px" : "100%",
- transformOrigin: "left top",
- transform: `scale(${scaling} , ${scaling})`
- }}
- onDrop={this.onDrop}
- onContextMenu={this.onContextMenu}
- onPointerDown={this.onPointerDown} >
- <DocumentContentsView {...this.props} isSelected={this.isSelected} select={this.select} layoutKey={KeyStore.Layout} />
- </div >
- )
+
+ if (this.isMinimized()) {
+ return (
+ <div
+ className="minimized-box"
+ ref={this._mainCont}
+ style={{
+ transformOrigin: "left top",
+ transform: `scale(${scaling} , ${scaling})`
+ }}
+ onClick={this.expand}
+ onDrop={this.onDrop}
+ onPointerDown={this.onPointerDown}
+ />
+ );
+ } else {
+ var backgroundcolor = this.props.Document.GetText(
+ KeyStore.BackgroundColor,
+ ""
+ );
+ return (
+ <div
+ className="documentView-node"
+ ref={this._mainCont}
+ style={{
+ background: backgroundcolor,
+ width: nativeWidth > 0 ? nativeWidth.toString() + "px" : "100%",
+ height: nativeHeight > 0 ? nativeHeight.toString() + "px" : "100%",
+ transformOrigin: "left top",
+ transform: `scale(${scaling} , ${scaling})`
+ }}
+ onDrop={this.onDrop}
+ onContextMenu={this.onContextMenu}
+ onPointerDown={this.onPointerDown}
+ >
+ <DocumentContentsView
+ {...this.props}
+ isSelected={this.isSelected}
+ select={this.select}
+ layoutKey={KeyStore.Layout}
+ />
+ </div>
+ );
+ }
}
-} \ No newline at end of file
+}
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index 512ad7d70..e59179874 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -2,29 +2,26 @@ import { action, IReactionDisposer, reaction } from "mobx";
import { baseKeymap } from "prosemirror-commands";
import { history, redo, undo } from "prosemirror-history";
import { keymap } from "prosemirror-keymap";
-import { schema } from "../../util/RichTextSchema";
-import { EditorState, Transaction, } from "prosemirror-state";
+import { EditorState, Plugin, Transaction } from "prosemirror-state";
import { EditorView } from "prosemirror-view";
-import { Opt, FieldWaiting } from "../../../fields/Field";
-import "./FormattedTextBox.scss";
-import React = require("react")
+import { FieldWaiting, Opt } from "../../../fields/Field";
+import { KeyStore } from "../../../fields/KeyStore";
import { RichTextField } from "../../../fields/RichTextField";
-import { FieldViewProps, FieldView } from "./FieldView";
-import { Plugin } from 'prosemirror-state'
-import { Decoration, DecorationSet } from 'prosemirror-view'
-import { TooltipTextMenu } from "../../util/TooltipTextMenu"
-import { ContextMenu } from "../../views/ContextMenu";
import { inpRules } from "../../util/RichTextRules";
+import { schema } from "../../util/RichTextSchema";
+import { TooltipTextMenu } from "../../util/TooltipTextMenu";
+import { ContextMenu } from "../../views/ContextMenu";
+import { Main } from "../Main";
+import { FieldView, FieldViewProps } from "./FieldView";
+import "./FormattedTextBox.scss";
+import React = require("react");
const { buildMenuItems } = require("prosemirror-example-setup");
const { menuBar } = require("prosemirror-menu");
-
-
-
// FormattedTextBox: Displays an editable plain text node that maps to a specified Key of a Document
//
// HTML Markup: <FormattedTextBox Doc={Document's ID} FieldKey={Key's name + "Key"}
-//
+//
// In Code, the node's HTML is specified in the document's parameterized structure as:
// document.SetField(KeyStore.Layout, "<FormattedTextBox doc={doc} fieldKey={<KEYNAME>Key} />");
// and the node's binding to the specified document KEYNAME as:
@@ -33,16 +30,18 @@ const { menuBar } = require("prosemirror-menu");
// 'fieldKey' property to the Key stored in LayoutKeys
// and 'doc' property to the document that is being rendered
//
-// When rendered() by React, this extracts the TextController from the Document stored at the
-// specified Key and assigns it to an HTML input node. When changes are made to this node,
+// When rendered() by React, this extracts the TextController from the Document stored at the
+// specified Key and assigns it to an HTML input node. When changes are made to this node,
// this will edit the document and assign the new value to that field.
//]
export class FormattedTextBox extends React.Component<FieldViewProps> {
-
- public static LayoutString(fieldStr: string = "DataKey") { return FieldView.LayoutString(FormattedTextBox, fieldStr) }
+ public static LayoutString(fieldStr: string = "DataKey") {
+ return FieldView.LayoutString(FormattedTextBox, fieldStr);
+ }
private _ref: React.RefObject<HTMLDivElement>;
private _editorView: Opt<EditorView>;
private _reactionDisposer: Opt<IReactionDisposer>;
+ private _inputReactionDisposer: Opt<IReactionDisposer>;
constructor(props: FieldViewProps) {
super(props);
@@ -55,14 +54,19 @@ export class FormattedTextBox extends React.Component<FieldViewProps> {
if (this._editorView) {
const state = this._editorView.state.apply(tx);
this._editorView.updateState(state);
- const { doc, fieldKey } = this.props;
- doc.SetDataOnPrototype(fieldKey, JSON.stringify(state.toJSON()), RichTextField);
+ this.FieldDoc.SetDataOnPrototype(
+ this.FieldKey,
+ JSON.stringify(state.toJSON()),
+ RichTextField
+ );
// doc.SetData(fieldKey, JSON.stringify(state.toJSON()), RichTextField);
}
- }
+ };
+
+ get FieldDoc() { return this.props.fieldKey === KeyStore.Archives ? Main.Instance._textDoc! : this.props.doc; }
+ get FieldKey() { return this.props.fieldKey === KeyStore.Archives ? KeyStore.Data : this.props.fieldKey; }
componentDidMount() {
- let state: EditorState;
const config = {
schema,
inpRules, //these currently don't do anything, but could eventually be helpful
@@ -74,7 +78,37 @@ export class FormattedTextBox extends React.Component<FieldViewProps> {
]
};
- let field = this.props.doc.GetT(this.props.fieldKey, RichTextField);
+ if (this.props.fieldKey === KeyStore.Archives) {
+ this._inputReactionDisposer = reaction(() => Main.Instance._textDoc && Main.Instance._textDoc.Id,
+ () => {
+ if (this._editorView)
+ this._editorView!.destroy();
+
+ this.setupEditor(config);
+ }
+ )
+ }
+
+ this._reactionDisposer = reaction(
+ () => {
+ const field = this.FieldDoc.GetT(this.FieldKey, RichTextField);
+ return field && field != FieldWaiting ? field.Data : undefined;
+ },
+ field => {
+ if (field && this._editorView) {
+ this._editorView.updateState(
+ EditorState.fromJSON(config, JSON.parse(field))
+ );
+ }
+ }
+ );
+ this.setupEditor(config);
+ }
+
+ private setupEditor(config: any) {
+
+ let state: EditorState;
+ let field = this.FieldDoc.GetT(this.FieldKey, RichTextField);
if (field && field != FieldWaiting && field.Data) {
state = EditorState.fromJSON(config, JSON.parse(field.Data));
} else {
@@ -87,14 +121,6 @@ export class FormattedTextBox extends React.Component<FieldViewProps> {
});
}
- this._reactionDisposer = reaction(() => {
- const field = this.props.doc.GetT(this.props.fieldKey, RichTextField);
- return field && field != FieldWaiting ? field.Data : undefined;
- }, (field) => {
- if (field && this._editorView) {
- this._editorView.updateState(EditorState.fromJSON(config, JSON.parse(field)));
- }
- })
if (this.props.selectOnLoad) {
this.props.select();
this._editorView!.focus();
@@ -108,6 +134,9 @@ export class FormattedTextBox extends React.Component<FieldViewProps> {
if (this._reactionDisposer) {
this._reactionDisposer();
}
+ if (this._inputReactionDisposer) {
+ this._inputReactionDisposer();
+ }
}
shouldComponentUpdate() {
@@ -117,21 +146,41 @@ export class FormattedTextBox extends React.Component<FieldViewProps> {
@action
onChange(e: React.ChangeEvent<HTMLInputElement>) {
const { fieldKey, doc } = this.props;
- doc.SetOnPrototype(fieldKey, new RichTextField(e.target.value))
+ doc.SetOnPrototype(fieldKey, new RichTextField(e.target.value));
// doc.SetData(fieldKey, e.target.value, RichTextField);
}
onPointerDown = (e: React.PointerEvent): void => {
if (e.buttons === 1 && this.props.isSelected() && !e.altKey) {
e.stopPropagation();
}
+ if (e.buttons === 1 && this.props.fieldKey !== KeyStore.Archives)
+ e.preventDefault();
+ };
+ onPointerUp = (e: React.PointerEvent): void => {
+ if (e.buttons === 1 && this.props.isSelected() && !e.altKey) {
+ e.stopPropagation();
+ }
+ if (this.props.fieldKey !== KeyStore.Archives) {
+ e.preventDefault();
+ Main.Instance.SetTextDoc(this.props.doc, this._ref.current!);
+ }
+ };
+
+ onFocused = (e: React.FocusEvent): void => {
+ if (this.props.fieldKey !== KeyStore.Archives) {
+ Main.Instance.SetTextDoc(this.props.doc, this._ref.current!);
+ }
}
//REPLACE THIS WITH CAPABILITIES SPECIFIC TO THIS TYPE OF NODE
- textCapability = (e: React.MouseEvent): void => {
- }
+ textCapability = (e: React.MouseEvent): void => { };
specificContextMenu = (e: React.MouseEvent): void => {
- ContextMenu.Instance.addItem({ description: "Text Capability", event: this.textCapability });
+ ContextMenu.Instance.addItem({
+ description: "Text Capability",
+ event: this.textCapability
+ });
+
// ContextMenu.Instance.addItem({
// description: "Submenu",
// items: [
@@ -144,19 +193,18 @@ export class FormattedTextBox extends React.Component<FieldViewProps> {
// ]
// })
// e.stopPropagation()
-
- }
+ };
onPointerWheel = (e: React.WheelEvent): void => {
e.stopPropagation();
- }
+ };
tooltipMenuPlugin() {
return new Plugin({
view(_editorView) {
- return new TooltipTextMenu(_editorView)
+ return new TooltipTextMenu(_editorView);
}
- })
+ });
}
onKeyPress(e: React.KeyboardEvent) {
e.stopPropagation();
@@ -165,13 +213,18 @@ export class FormattedTextBox extends React.Component<FieldViewProps> {
// (e.nativeEvent as any).DASHFormattedTextBoxHandled = true;
}
render() {
- return (<div className="formattedTextBox-cont"
- onKeyDown={this.onKeyPress}
- onKeyPress={this.onKeyPress}
- onPointerDown={this.onPointerDown}
- onContextMenu={this.specificContextMenu}
- // tfs: do we need this event handler
- onWheel={this.onPointerWheel}
- ref={this._ref} />)
+ return (
+ <div
+ className="formattedTextBox-cont"
+ onKeyDown={this.onKeyPress}
+ onKeyPress={this.onKeyPress}
+ onPointerUp={this.onPointerUp}
+ onPointerDown={this.onPointerDown}
+ onContextMenu={this.specificContextMenu}
+ // tfs: do we need this event handler
+ onWheel={this.onPointerWheel}
+ ref={this._ref}
+ />
+ );
}
-} \ No newline at end of file
+}
diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx
index 283c1f732..9bd6c1052 100644
--- a/src/client/views/nodes/KeyValueBox.tsx
+++ b/src/client/views/nodes/KeyValueBox.tsx
@@ -40,11 +40,13 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
}
let realDoc = doc;
- let script = CompileScript(this._valueInput, undefined, true);
+ let script = CompileScript(this._valueInput, { addReturn: true });
if (!script.compiled) {
return;
}
- let field = script();
+ let res = script.run();
+ if (!res.success) return;
+ const field = res.result;
if (field instanceof Field) {
realDoc.Set(new Key(this._keyInput), field);
} else {
diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx
index 7ed5ee272..5647f45bf 100644
--- a/src/client/views/nodes/KeyValuePair.tsx
+++ b/src/client/views/nodes/KeyValuePair.tsx
@@ -76,11 +76,13 @@ export class KeyValuePair extends React.Component<KeyValuePairProps> {
return field || "";
}}
SetValue={(value: string) => {
- let script = CompileScript(value, undefined, true);
+ let script = CompileScript(value, { addReturn: true });
if (!script.compiled) {
return false;
}
- let field = script();
+ let res = script.run();
+ if (!res.success) return false;
+ const field = res.result;
if (field instanceof Field) {
props.doc.Set(props.fieldKey, field);
return true;
diff --git a/src/client/views/nodes/PDFBox.scss b/src/client/views/nodes/PDFBox.scss
index ad947afd5..830dfe6c6 100644
--- a/src/client/views/nodes/PDFBox.scss
+++ b/src/client/views/nodes/PDFBox.scss
@@ -1,12 +1,16 @@
.react-pdf__Page {
transform-origin: left top;
position: absolute;
+ top: 0;
+ left:0;
}
.react-pdf__Document {
position: absolute;
}
.pdfBox-buttonTray {
position:absolute;
+ top: 0;
+ left:0;
z-index: 25;
}
.pdfBox-contentContainer {
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 12208714d..7039b0c41 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -1,5 +1,5 @@
import * as htmlToImage from "html-to-image";
-import { action, computed, observable, reaction, IReactionDisposer, trace, keys } from 'mobx';
+import { action, computed, IReactionDisposer, observable, reaction } from 'mobx';
import { observer } from "mobx-react";
import 'react-image-lightbox/style.css';
import Measure from "react-measure";
@@ -10,6 +10,7 @@ import { FieldWaiting, Opt } from '../../../fields/Field';
import { ImageField } from '../../../fields/ImageField';
import { KeyStore } from '../../../fields/KeyStore';
import { PDFField } from '../../../fields/PDFField';
+import { RouteStore } from "../../../server/RouteStore";
import { Utils } from '../../../Utils';
import { Annotation } from './Annotation';
import { FieldView, FieldViewProps } from './FieldView';
@@ -17,8 +18,7 @@ import "./ImageBox.scss";
import "./PDFBox.scss";
import { Sticky } from './Sticky'; //you should look at sticky and annotation, because they are used here
import React = require("react")
-import { RouteStore } from "../../../server/RouteStore";
-import { NumberField } from "../../../fields/NumberField";
+import { SelectionManager } from "../../util/SelectionManager";
/** ALSO LOOK AT: Annotation.tsx, Sticky.tsx
* This method renders PDF and puts all kinds of functionalities such as annotation, highlighting,
@@ -58,6 +58,8 @@ export class PDFBox extends React.Component<FieldViewProps> {
private _mainDiv = React.createRef<HTMLDivElement>()
private _pdf = React.createRef<HTMLCanvasElement>();
+ @observable private _renderAsSvg = true;
+
//very useful for keeping track of X and y position throughout the PDF Canvas
private initX: number = 0;
private initY: number = 0;
@@ -92,9 +94,9 @@ export class PDFBox extends React.Component<FieldViewProps> {
componentDidMount() {
this._reactionDisposer = reaction(
- () => [this.curPage, this.thumbnailPage],
+ () => [SelectionManager.SelectedDocuments().slice()],
() => {
- if (this.curPage > 0 && this.thumbnailPage > 0 && this.curPage != this.thumbnailPage) {
+ if (this.curPage > 0 && this.thumbnailPage > 0 && this.curPage != this.thumbnailPage && !this.props.isSelected()) {
this.saveThumbnail();
this._interactive = true;
}
@@ -376,18 +378,21 @@ export class PDFBox extends React.Component<FieldViewProps> {
@action
saveThumbnail = () => {
+ this._renderAsSvg = false;
setTimeout(() => {
var me = this;
- htmlToImage.toPng(this._mainDiv.current!,
- { width: me.props.doc.GetNumber(KeyStore.NativeWidth, 0), height: me.props.doc.GetNumber(KeyStore.NativeHeight, 0), quality: 0.5 })
- .then(function (dataUrl: string) {
+ let nwidth = me.props.doc.GetNumber(KeyStore.NativeWidth, 0);
+ let nheight = me.props.doc.GetNumber(KeyStore.NativeHeight, 0);
+ htmlToImage.toPng(this._mainDiv.current!, { width: nwidth, height: nheight, quality: 1 })
+ .then(action((dataUrl: string) => {
me.props.doc.SetData(KeyStore.Thumbnail, new URL(dataUrl), ImageField);
me.props.doc.SetNumber(KeyStore.ThumbnailPage, me.props.doc.GetNumber(KeyStore.CurPage, -1));
- })
+ me._renderAsSvg = true;
+ }))
.catch(function (error: any) {
console.error('oops, something went wrong!', error);
});
- }, 1000);
+ }, 250);
}
@action
@@ -427,9 +432,6 @@ export class PDFBox extends React.Component<FieldViewProps> {
this.props.doc.SetNumber(KeyStore.Height, nativeHeight / nativeWidth * this.props.doc.GetNumber(KeyStore.Width, 0));
this.props.doc.SetNumber(KeyStore.NativeHeight, nativeHeight);
}
- if (!this.props.doc.GetT(KeyStore.Thumbnail, ImageField)) {
- this.saveThumbnail();
- }
}
@computed
@@ -439,7 +441,7 @@ export class PDFBox extends React.Component<FieldViewProps> {
let pdfUrl = this.props.doc.GetT(this.props.fieldKey, PDFField);
let xf = this.props.doc.GetNumber(KeyStore.NativeHeight, 0) / renderHeight;
return <div className="pdfBox-contentContainer" key="container" style={{ transform: `scale(${xf}, ${xf})` }}>
- <Document file={window.origin + RouteStore.corsProxy + `/${pdfUrl}`}>
+ <Document file={window.origin + RouteStore.corsProxy + `/${pdfUrl}`} renderMode={this._renderAsSvg ? "svg" : ""}>
<Measure onResize={this.setScaling}>
{({ measureRef }) =>
<div className="pdfBox-page" ref={measureRef}>
diff --git a/src/client/views/nodes/Sticky.tsx b/src/client/views/nodes/Sticky.tsx
index d57dd5c0b..4a4d69e90 100644
--- a/src/client/views/nodes/Sticky.tsx
+++ b/src/client/views/nodes/Sticky.tsx
@@ -1,83 +1,83 @@
-import 'react-image-lightbox/style.css'; // This only needs to be imported once in your app
-import React = require("react")
-import { observer } from "mobx-react"
-import 'react-pdf/dist/Page/AnnotationLayer.css'
+import "react-image-lightbox/style.css"; // This only needs to be imported once in your app
+import React = require("react");
+import { observer } from "mobx-react";
+import "react-pdf/dist/Page/AnnotationLayer.css";
interface IProps {
- Height: number;
- Width: number;
- X: number;
- Y: number;
+ Height: number;
+ Width: number;
+ X: number;
+ Y: number;
}
/**
- * Sticky, also known as area highlighting, is used to highlight large selection of the PDF file.
- * Improvements that could be made: maybe store line array and store that somewhere for future rerendering.
- *
- * Written By: Andrew Kim
+ * Sticky, also known as area highlighting, is used to highlight large selection of the PDF file.
+ * Improvements that could be made: maybe store line array and store that somewhere for future rerendering.
+ *
+ * Written By: Andrew Kim
*/
@observer
export class Sticky extends React.Component<IProps> {
+ private initX: number = 0;
+ private initY: number = 0;
- private initX: number = 0;
- private initY: number = 0;
+ private _ref = React.createRef<HTMLCanvasElement>();
+ private ctx: any; //context that keeps track of sticky canvas
- private _ref = React.createRef<HTMLCanvasElement>();
- private ctx: any; //context that keeps track of sticky canvas
-
- /**
- * drawing. Registers the first point that user clicks when mouse button is pressed down on canvas
- */
- drawDown = (e: React.PointerEvent) => {
- if (this._ref.current) {
- this.ctx = this._ref.current.getContext("2d");
- let mouse = e.nativeEvent;
- this.initX = mouse.offsetX;
- this.initY = mouse.offsetY;
- this.ctx.beginPath();
- this.ctx.lineTo(this.initX, this.initY);
- this.ctx.strokeStyle = "black";
- document.addEventListener("pointermove", this.drawMove);
- document.addEventListener("pointerup", this.drawUp);
- }
+ /**
+ * drawing. Registers the first point that user clicks when mouse button is pressed down on canvas
+ */
+ drawDown = (e: React.PointerEvent) => {
+ if (this._ref.current) {
+ this.ctx = this._ref.current.getContext("2d");
+ let mouse = e.nativeEvent;
+ this.initX = mouse.offsetX;
+ this.initY = mouse.offsetY;
+ this.ctx.beginPath();
+ this.ctx.lineTo(this.initX, this.initY);
+ this.ctx.strokeStyle = "black";
+ document.addEventListener("pointermove", this.drawMove);
+ document.addEventListener("pointerup", this.drawUp);
}
+ };
- //when user drags
- drawMove = (e: PointerEvent): void => {
- //x and y mouse movement
- let x = this.initX += e.movementX,
- y = this.initY += e.movementY;
- //connects the point
- this.ctx.lineTo(x, y);
- this.ctx.stroke();
-
- }
+ //when user drags
+ drawMove = (e: PointerEvent): void => {
+ //x and y mouse movement
+ let x = (this.initX += e.movementX),
+ y = (this.initY += e.movementY);
+ //connects the point
+ this.ctx.lineTo(x, y);
+ this.ctx.stroke();
+ };
- /**
- * when user lifts the mouse, the drawing ends
- */
- drawUp = (e: PointerEvent) => {
- this.ctx.closePath();
- console.log(this.ctx);
- document.removeEventListener("pointermove", this.drawMove);
- }
+ /**
+ * when user lifts the mouse, the drawing ends
+ */
+ drawUp = (e: PointerEvent) => {
+ this.ctx.closePath();
+ console.log(this.ctx);
+ document.removeEventListener("pointermove", this.drawMove);
+ };
- render() {
- return (
- <div onPointerDown={this.drawDown}>
- <canvas ref={this._ref} height={this.props.Height} width={this.props.Width}
- style={{
- position: "absolute",
- top: "20px",
- left: "0px",
- zIndex: 1,
- background: "yellow",
- transform: `translate(${this.props.X}px, ${this.props.Y}px)`,
- opacity: 0.4
- }}
- />
-
- </div>
- );
- }
-} \ No newline at end of file
+ render() {
+ return (
+ <div onPointerDown={this.drawDown}>
+ <canvas
+ ref={this._ref}
+ height={this.props.Height}
+ width={this.props.Width}
+ style={{
+ position: "absolute",
+ top: "20px",
+ left: "0px",
+ zIndex: 1,
+ background: "yellow",
+ transform: `translate(${this.props.X}px, ${this.props.Y}px)`,
+ opacity: 0.4
+ }}
+ />
+ </div>
+ );
+ }
+}
diff --git a/src/client/views/nodes/WebBox.scss b/src/client/views/nodes/WebBox.scss
index a535b2638..c73bc0c47 100644
--- a/src/client/views/nodes/WebBox.scss
+++ b/src/client/views/nodes/WebBox.scss
@@ -2,6 +2,8 @@
.webBox-cont {
padding: 0vw;
position: absolute;
+ top: 0;
+ left:0;
width: 100%;
height: 100%;
overflow: scroll;
diff --git a/src/fields/BooleanField.ts b/src/fields/BooleanField.ts
new file mode 100644
index 000000000..d319b4021
--- /dev/null
+++ b/src/fields/BooleanField.ts
@@ -0,0 +1,25 @@
+import { BasicField } from "./BasicField";
+import { FieldId } from "./Field";
+import { Types } from "../server/Message";
+
+export class BooleanField extends BasicField<boolean> {
+ constructor(data: boolean = false as boolean, id?: FieldId, save: boolean = true as boolean) {
+ super(data, save, id);
+ }
+
+ ToScriptString(): string {
+ return `new BooleanField("${this.Data}")`;
+ }
+
+ Copy() {
+ return new BooleanField(this.Data);
+ }
+
+ ToJson(): { type: Types; data: boolean; _id: string } {
+ return {
+ type: Types.Boolean,
+ data: this.Data,
+ _id: this.Id
+ };
+ }
+}
diff --git a/src/fields/Document.ts b/src/fields/Document.ts
index 85ff6ddcb..02004678d 100644
--- a/src/fields/Document.ts
+++ b/src/fields/Document.ts
@@ -1,6 +1,6 @@
-import { Key } from "./Key"
+import { Key } from "./Key";
import { KeyStore } from "./KeyStore";
-import { Field, Cast, FieldWaiting, FieldValue, FieldId, Opt } from "./Field"
+import { Field, Cast, FieldWaiting, FieldValue, FieldId, Opt } from "./Field";
import { NumberField } from "./NumberField";
import { ObservableMap, computed, action, runInAction } from "mobx";
import { TextField } from "./TextField";
@@ -9,17 +9,24 @@ import { Server } from "../client/Server";
import { Types } from "../server/Message";
import { UndoManager } from "../client/util/UndoManager";
import { HtmlField } from "./HtmlField";
+import { BooleanField } from "./BooleanField";
+import { allLimit } from "async";
+import { prototype } from "nodemailer/lib/smtp-pool";
+import { HistogramField } from "../client/northstar/dash-fields/HistogramField";
export class Document extends Field {
//TODO tfs: We should probably store FieldWaiting in fields when we request it from the server so that we don't set up multiple server gets for the same document and field
- public fields: ObservableMap<string, { key: Key, field: Field }> = new ObservableMap();
+ public fields: ObservableMap<
+ string,
+ { key: Key; field: Field }
+ > = new ObservableMap();
public _proxies: ObservableMap<string, FieldId> = new ObservableMap();
constructor(id?: string, save: boolean = true) {
- super(id)
+ super(id);
if (save) {
- Server.UpdateField(this)
+ Server.UpdateField(this);
}
}
@@ -30,9 +37,22 @@ export class Document extends Field {
}
}
- public Width = () => { return this.GetNumber(KeyStore.Width, 0) }
- public Height = () => { return this.GetNumber(KeyStore.Height, this.GetNumber(KeyStore.NativeWidth, 0) ? this.GetNumber(KeyStore.NativeHeight, 0) / this.GetNumber(KeyStore.NativeWidth, 0) * this.GetNumber(KeyStore.Width, 0) : 0) }
- public Scale = () => { return this.GetNumber(KeyStore.Scale, 1) }
+ public Width = () => {
+ return this.GetNumber(KeyStore.Width, 0);
+ };
+ public Height = () => {
+ return this.GetNumber(
+ KeyStore.Height,
+ this.GetNumber(KeyStore.NativeWidth, 0)
+ ? (this.GetNumber(KeyStore.NativeHeight, 0) /
+ this.GetNumber(KeyStore.NativeWidth, 0)) *
+ this.GetNumber(KeyStore.Width, 0)
+ : 0
+ );
+ };
+ public Scale = () => {
+ return this.GetNumber(KeyStore.Scale, 1);
+ };
@computed
public get Title(): string {
@@ -43,8 +63,7 @@ export class Document extends Field {
else return "-waiting-";
let parTitle = this.GetT(KeyStore.Title, TextField);
if (parTitle)
- if (parTitle != FieldWaiting)
- return parTitle.Data + ".alias";
+ if (parTitle != FieldWaiting) return parTitle.Data + ".alias";
else return "-waiting-.alias";
return "-untitled-";
}
@@ -57,7 +76,7 @@ export class Document extends Field {
/**
* Get the field in the document associated with the given key. If the
* associated field has not yet been filled in from the server, a request
- * to the server will automatically be sent, the value will be filled in
+ * to the server will automatically be sent, the value will be filled in
* when the request is completed, and {@link Field.ts#FieldWaiting} will be returned.
* @param key - The key of the value to get
* @param ignoreProto - If true, ignore any prototype this document
@@ -65,7 +84,7 @@ export class Document extends Field {
* If false (default), search up the prototype chain, starting at this document,
* for a document that has a field associated with the given key, and return the first
* one found.
- *
+ *
* @returns If the document does not have a field associated with the given key, returns `undefined`.
* If the document does have an associated field, but the field has not been fetched from the server, returns {@link Field.ts#FieldWaiting}.
* If the document does have an associated field, and the field has not been fetched from the server, returns the associated field.
@@ -78,10 +97,10 @@ export class Document extends Field {
} else if (this._proxies.has(key.Id)) {
Server.GetDocumentField(this, key);
/*
- The field might have been instantly filled from the cache
- Maybe we want to just switch back to returning the value
- from Server.GetDocumentField if it's in the cache
- */
+ The field might have been instantly filled from the cache
+ Maybe we want to just switch back to returning the value
+ from Server.GetDocumentField if it's in the cache
+ */
if (this.fields.has(key.Id)) {
field = this.fields.get(key.Id)!.field;
} else {
@@ -97,10 +116,10 @@ export class Document extends Field {
if (curProxy) {
Server.GetDocumentField(doc, key);
/*
- The field might have been instantly filled from the cache
- Maybe we want to just switch back to returning the value
- from Server.GetDocumentField if it's in the cache
- */
+ The field might have been instantly filled from the cache
+ Maybe we want to just switch back to returning the value
+ from Server.GetDocumentField if it's in the cache
+ */
if (this.fields.has(key.Id)) {
field = this.fields.get(key.Id)!.field;
} else {
@@ -108,7 +127,10 @@ export class Document extends Field {
}
break;
}
- if ((doc.fields.has(KeyStore.Prototype.Id) || doc._proxies.has(KeyStore.Prototype.Id))) {
+ if (
+ doc.fields.has(KeyStore.Prototype.Id) ||
+ doc._proxies.has(KeyStore.Prototype.Id)
+ ) {
doc = doc.GetPrototype();
} else {
break;
@@ -118,8 +140,7 @@ export class Document extends Field {
break;
}
}
- if (doc == FieldWaiting)
- field = FieldWaiting;
+ if (doc == FieldWaiting) field = FieldWaiting;
}
return field;
@@ -148,20 +169,28 @@ export class Document extends Field {
} else {
callback(undefined);
}
- })
+ });
} else {
callback(undefined);
}
}
GetTAsync<T extends Field>(key: Key, ctor: { new(): T }): Promise<Opt<T>>;
- GetTAsync<T extends Field>(key: Key, ctor: { new(): T }, callback: (field: Opt<T>) => void): void;
- GetTAsync<T extends Field>(key: Key, ctor: { new(): T }, callback?: (field: Opt<T>) => void): Promise<Opt<T>> | void {
+ GetTAsync<T extends Field>(
+ key: Key,
+ ctor: { new(): T },
+ callback: (field: Opt<T>) => void
+ ): void;
+ GetTAsync<T extends Field>(
+ key: Key,
+ ctor: { new(): T },
+ callback?: (field: Opt<T>) => void
+ ): Promise<Opt<T>> | void {
let fn = (cb: (field: Opt<T>) => void) => {
- return this.GetAsync(key, (field) => {
+ return this.GetAsync(key, field => {
cb(Cast(field, ctor));
});
- }
+ };
if (callback) {
fn(callback);
} else {
@@ -175,10 +204,14 @@ export class Document extends Field {
* or the field associated with the given key is not of the given type.
* @param ctor - Constructor of the field type to get. E.g., TextField, ImageField, etc.
*/
- GetOrCreateAsync<T extends Field>(key: Key, ctor: { new(): T }, callback: (field: T) => void): void {
+ GetOrCreateAsync<T extends Field>(
+ key: Key,
+ ctor: { new(): T },
+ callback: (field: T) => void
+ ): void {
//This currently doesn't deal with prototypes
if (this._proxies.has(key.Id)) {
- Server.GetDocumentField(this, key, (field) => {
+ Server.GetDocumentField(this, key, field => {
if (field && field instanceof ctor) {
callback(field);
} else {
@@ -201,7 +234,11 @@ export class Document extends Field {
* @returns Same as {@link Document#Get}, except will return `undefined`
* if there is an associated field but it is of the wrong type.
*/
- GetT<T extends Field = Field>(key: Key, ctor: { new(...args: any[]): T }, ignoreProto: boolean = false): FieldValue<T> {
+ GetT<T extends Field = Field>(
+ key: Key,
+ ctor: { new(...args: any[]): T },
+ ignoreProto: boolean = false
+ ): FieldValue<T> {
var getfield = this.Get(key, ignoreProto);
if (getfield != FieldWaiting) {
return Cast(getfield, ctor);
@@ -209,7 +246,11 @@ export class Document extends Field {
return FieldWaiting;
}
- GetOrCreate<T extends Field>(key: Key, ctor: { new(): T }, ignoreProto: boolean = false): T {
+ GetOrCreate<T extends Field>(
+ key: Key,
+ ctor: { new(): T },
+ ignoreProto: boolean = false
+ ): T {
const field = this.GetT(key, ctor, ignoreProto);
if (field && field != FieldWaiting) {
return field;
@@ -219,9 +260,13 @@ export class Document extends Field {
return newField;
}
- GetData<T, U extends Field & { Data: T }>(key: Key, ctor: { new(): U }, defaultVal: T): T {
+ GetData<T, U extends Field & { Data: T }>(
+ key: Key,
+ ctor: { new(): U },
+ defaultVal: T
+ ): T {
let val = this.Get(key);
- let vval = (val && val instanceof ctor) ? val.Data : defaultVal;
+ let vval = val && val instanceof ctor ? val.Data : defaultVal;
return vval;
}
@@ -229,6 +274,10 @@ export class Document extends Field {
return this.GetData(key, HtmlField, defaultVal);
}
+ GetBoolean(key: Key, defaultVal: boolean): boolean {
+ return this.GetData(key, BooleanField, defaultVal);
+ }
+
GetNumber(key: Key, defaultVal: number): number {
return this.GetData(key, NumberField, defaultVal);
}
@@ -238,7 +287,7 @@ export class Document extends Field {
}
GetList<T extends Field>(key: Key, defaultVal: T[]): T[] {
- return this.GetData<T[], ListField<T>>(key, ListField, defaultVal)
+ return this.GetData<T[], ListField<T>>(key, ListField, defaultVal);
}
@action
@@ -246,16 +295,15 @@ export class Document extends Field {
let old = this.fields.get(key.Id);
let oldField = old ? old.field : undefined;
if (setOnPrototype) {
- this.SetOnPrototype(key, field)
- }
- else {
+ this.SetOnPrototype(key, field);
+ } else {
if (field) {
this.fields.set(key.Id, { key, field });
- this._proxies.set(key.Id, field.Id)
+ this._proxies.set(key.Id, field.Id);
// Server.AddDocumentField(this, key, field);
} else {
this.fields.delete(key.Id);
- this._proxies.delete(key.Id)
+ this._proxies.delete(key.Id);
// Server.DeleteDocumentField(this, key);
}
Server.UpdateField(this);
@@ -264,22 +312,22 @@ export class Document extends Field {
UndoManager.AddEvent({
undo: () => this.Set(key, oldField, setOnPrototype),
redo: () => this.Set(key, field, setOnPrototype)
- })
+ });
}
}
@action
SetOnPrototype(key: Key, field: Field | undefined): void {
this.GetTAsync(KeyStore.Prototype, Document, (f: Opt<Document>) => {
- f && f.Set(key, field)
- })
+ f && f.Set(key, field);
+ });
}
@action
SetDataOnPrototype<T, U extends Field & { Data: T }>(key: Key, value: T, ctor: { new(): U }, replaceWrongType = true) {
this.GetTAsync(KeyStore.Prototype, Document, (f: Opt<Document>) => {
- f && f.SetData(key, value, ctor)
- })
+ f && f.SetData(key, value, ctor, replaceWrongType);
+ });
}
@action
@@ -298,7 +346,10 @@ export class Document extends Field {
SetText(key: Key, value: string, replaceWrongType = true) {
this.SetData(key, value, TextField, replaceWrongType);
}
-
+ @action
+ SetBoolean(key: Key, value: boolean, replaceWrongType = true) {
+ this.SetData(key, value, BooleanField, replaceWrongType);
+ }
@action
SetNumber(key: Key, value: number, replaceWrongType = true) {
this.SetData(key, value, NumberField, replaceWrongType);
@@ -319,12 +370,12 @@ export class Document extends Field {
}
CreateAlias(id?: string): Document {
- let alias = new Document(id)
+ let alias = new Document(id);
this.GetTAsync(KeyStore.Prototype, Document, (f: Opt<Document>) => {
- f && alias.Set(KeyStore.Prototype, f)
- })
+ f && alias.Set(KeyStore.Prototype, f);
+ });
- return alias
+ return alias;
}
MakeDelegate(id?: string): Document {
@@ -348,15 +399,33 @@ export class Document extends Field {
return title;
//throw new Error("Method not implemented.");
}
- Copy(): Field {
- throw new Error("Method not implemented.");
+ Copy(copyProto?: boolean, id?: string): Field {
+ let copy = new Document();
+ this._proxies.forEach((fieldid, keyid) => { // copy each prototype field
+ let key = KeyStore.KeyLookup(keyid);
+ if (key) {
+ this.GetAsync(key, (field: Opt<Field>) => {
+ if (key === KeyStore.Prototype && copyProto) { // handle prototype field specially
+ if (field instanceof Document) {
+ copy.Set(key, field.Copy(false)); // only copying one level of prototypes for now...
+ }
+ }
+ else
+ if (field instanceof Document) // ... TODO bcz: should we copy documents or reference them
+ copy.Set(key!, field)
+ else if (field)
+ copy.Set(key!, field.Copy())
+ })
+ }
+ });
+ return copy;
}
- ToJson(): { type: Types, data: [string, string][], _id: string } {
- let fields: [string, string][] = []
+ ToJson(): { type: Types; data: [string, string][]; _id: string } {
+ let fields: [string, string][] = [];
this._proxies.forEach((field, key) => {
if (field) {
- fields.push([key, field as string])
+ fields.push([key, field as string]);
}
});
@@ -364,6 +433,6 @@ export class Document extends Field {
type: Types.Document,
data: fields,
_id: this.Id
- }
+ };
}
-} \ No newline at end of file
+}
diff --git a/src/fields/KeyStore.ts b/src/fields/KeyStore.ts
index 1f039e592..42dc34c51 100644
--- a/src/fields/KeyStore.ts
+++ b/src/fields/KeyStore.ts
@@ -1,4 +1,5 @@
import { Key } from "./Key";
+import { KeyTransfer } from "../server/Message";
export namespace KeyStore {
export const Prototype = new Key("Prototype");
@@ -43,6 +44,22 @@ export namespace KeyStore {
export const Cursors = new Key("Cursors");
export const OptionalRightCollection = new Key("OptionalRightCollection");
export const Archives = new Key("Archives");
- export const Updated = new Key("Updated");
export const Workspaces = new Key("Workspaces");
+ export const Minimized = new Key("Minimized");
+ export const CopyDraggedItems = new Key("CopyDraggedItems");
+
+ export const KeyList: Key[] = [Prototype, X, Y, Page, Title, Author, PanX, PanY, Scale, NativeWidth, NativeHeight,
+ Width, Height, ZIndex, Data, Annotations, ViewType, Layout, BackgroundColor, BackgroundLayout, OverlayLayout, LayoutKeys,
+ LayoutFields, ColumnsKey, SchemaSplitPercentage, Caption, ActiveWorkspace, DocumentText, BrushingDocs, LinkedToDocs, LinkedFromDocs,
+ LinkDescription, LinkTags, Thumbnail, ThumbnailPage, CurPage, AnnotationOn, NumPages, Ink, Cursors, OptionalRightCollection,
+ Archives, Workspaces, Minimized, CopyDraggedItems
+ ];
+ export function KeyLookup(keyid: string) {
+ for (let i = 0; i < KeyList.length; i++) {
+ let keylistid = KeyList[i].Id;
+ if (keylistid === keyid)
+ return KeyList[i];
+ }
+ return null;
+ }
}
diff --git a/src/fields/NumberField.ts b/src/fields/NumberField.ts
index 47dfc74cb..e0c8648de 100644
--- a/src/fields/NumberField.ts
+++ b/src/fields/NumberField.ts
@@ -8,7 +8,7 @@ export class NumberField extends BasicField<number> {
}
ToScriptString(): string {
- return "new NumberField(this.Data)";
+ return `new NumberField(${this.Data})`;
}
Copy() {
diff --git a/src/server/Message.ts b/src/server/Message.ts
index 05ae0f19a..0274609bb 100644
--- a/src/server/Message.ts
+++ b/src/server/Message.ts
@@ -9,12 +9,12 @@ export class Message<T> {
}
get Message(): string {
- return this.guid
+ return this.guid;
}
constructor(name: string) {
this.name = name;
- this.guid = Utils.GenerateDeterministicGuid(name)
+ this.guid = Utils.GenerateDeterministicGuid(name);
}
GetValue() {
@@ -31,8 +31,8 @@ export class SetFieldArgs {
value: any;
constructor(f: string, v: any) {
- this.field = f
- this.value = v
+ this.field = f;
+ this.value = v;
}
}
@@ -40,37 +40,55 @@ export class GetFieldArgs {
field: string;
constructor(f: string) {
- this.field = f
+ this.field = f;
}
}
export enum Types {
- Number, List, Key, Image, Web, Document, Text, RichText, DocumentReference, Html, Video, Audio, Ink, PDF, Tuple, HistogramOp
+ Number,
+ List,
+ Key,
+ Image,
+ Web,
+ Document,
+ Text,
+ RichText,
+ DocumentReference,
+ Html,
+ Video,
+ Audio,
+ Ink,
+ PDF,
+ Tuple,
+ HistogramOp,
+ Boolean
}
export class DocumentTransfer implements Transferable {
- readonly type = Types.Document
- _id: string
+ readonly type = Types.Document;
+ _id: string;
- constructor(readonly obj: { type: Types, data: [string, string][], _id: string }) {
- this._id = obj._id
+ constructor(
+ readonly obj: { type: Types; data: [string, string][]; _id: string }
+ ) {
+ this._id = obj._id;
}
}
export class ImageTransfer implements Transferable {
- readonly type = Types.Image
+ readonly type = Types.Image;
constructor(readonly _id: string) { }
}
export class KeyTransfer implements Transferable {
- name: string
- readonly _id: string
- readonly type = Types.Key
+ name: string;
+ readonly _id: string;
+ readonly type = Types.Key;
constructor(i: string, n: string) {
- this.name = n
- this._id = i
+ this.name = n;
+ this._id = i;
}
}
@@ -81,45 +99,47 @@ export class ListTransfer implements Transferable {
}
export class NumberTransfer implements Transferable {
- readonly type = Types.Number
+ readonly type = Types.Number;
constructor(readonly value: number, readonly _id: string) { }
}
export class TextTransfer implements Transferable {
- value: string
- readonly _id: string
- readonly type = Types.Text
+ value: string;
+ readonly _id: string;
+ readonly type = Types.Text;
constructor(t: string, i: string) {
- this.value = t
- this._id = i
+ this.value = t;
+ this._id = i;
}
}
export class RichTextTransfer implements Transferable {
- value: string
- readonly _id: string
- readonly type = Types.Text
+ value: string;
+ readonly _id: string;
+ readonly type = Types.Text;
constructor(t: string, i: string) {
- this.value = t
- this._id = i
+ this.value = t;
+ this._id = i;
}
}
export interface Transferable {
- readonly _id: string
- readonly type: Types
+ readonly _id: string;
+ readonly type: Types;
}
export namespace MessageStore {
export const Foo = new Message<string>("Foo");
export const Bar = new Message<string>("Bar");
export const AddDocument = new Message<DocumentTransfer>("Add Document");
- export const SetField = new Message<{ _id: string, data: any, type: Types }>("Set Field")
- export const GetField = new Message<string>("Get Field")
- export const GetFields = new Message<string[]>("Get Fields")
+ export const SetField = new Message<{ _id: string; data: any; type: Types }>(
+ "Set Field"
+ );
+ export const GetField = new Message<string>("Get Field");
+ export const GetFields = new Message<string[]>("Get Fields");
export const GetDocument = new Message<string>("Get Document");
export const DeleteAll = new Message<any>("Delete All");
-} \ No newline at end of file
+}
diff --git a/src/server/ServerUtil.ts b/src/server/ServerUtil.ts
index 98a7a1451..2c2bfd0c9 100644
--- a/src/server/ServerUtil.ts
+++ b/src/server/ServerUtil.ts
@@ -1,78 +1,84 @@
-
-import { Field } from './../fields/Field';
-import { TextField } from './../fields/TextField';
-import { NumberField } from './../fields/NumberField';
-import { RichTextField } from './../fields/RichTextField';
-import { Key } from './../fields/Key';
-import { ImageField } from './../fields/ImageField';
-import { ListField } from './../fields/ListField';
-import { Document } from './../fields/Document';
-import { Server } from './../client/Server';
-import { Types } from './Message';
-import { Utils } from '../Utils';
-import { HtmlField } from '../fields/HtmlField';
-import { WebField } from '../fields/WebField';
-import { AudioField } from '../fields/AudioField';
-import { VideoField } from '../fields/VideoField';
-import { InkField } from '../fields/InkField';
-import { PDFField } from '../fields/PDFField';
-import { TupleField } from '../fields/TupleField';
-import { HistogramField } from '../client/northstar/dash-fields/HistogramField';
-
-
+import { Field } from "./../fields/Field";
+import { TextField } from "./../fields/TextField";
+import { NumberField } from "./../fields/NumberField";
+import { RichTextField } from "./../fields/RichTextField";
+import { Key } from "./../fields/Key";
+import { ImageField } from "./../fields/ImageField";
+import { ListField } from "./../fields/ListField";
+import { Document } from "./../fields/Document";
+import { Server } from "./../client/Server";
+import { Types } from "./Message";
+import { Utils } from "../Utils";
+import { HtmlField } from "../fields/HtmlField";
+import { WebField } from "../fields/WebField";
+import { AudioField } from "../fields/AudioField";
+import { VideoField } from "../fields/VideoField";
+import { InkField } from "../fields/InkField";
+import { PDFField } from "../fields/PDFField";
+import { TupleField } from "../fields/TupleField";
+import { BooleanField } from "../fields/BooleanField";
+import { HistogramField } from "../client/northstar/dash-fields/HistogramField";
export class ServerUtils {
- public static prepend(extension: string): string { return window.location.origin + extension; }
+ public static prepend(extension: string): string {
+ return window.location.origin + extension;
+ }
public static FromJson(json: any): Field {
- let obj = json
- let data: any = obj.data
- let id: string = obj._id
- let type: Types = obj.type
+ let obj = json;
+ let data: any = obj.data;
+ let id: string = obj._id;
+ let type: Types = obj.type;
if (!(data !== undefined && id && type !== undefined)) {
- console.log("how did you manage to get an object that doesn't have a data or an id?")
+ console.log(
+ "how did you manage to get an object that doesn't have a data or an id?"
+ );
return new TextField("Something to fill the space", Utils.GenerateGuid());
}
switch (type) {
+ case Types.Boolean:
+ return new BooleanField(data, id, false);
case Types.Number:
- return new NumberField(data, id, false)
+ return new NumberField(data, id, false);
case Types.Text:
- return new TextField(data, id, false)
+ return new TextField(data, id, false);
case Types.Html:
- return new HtmlField(data, id, false)
+ return new HtmlField(data, id, false);
case Types.Web:
- return new WebField(new URL(data), id, false)
+ return new WebField(new URL(data), id, false);
case Types.RichText:
- return new RichTextField(data, id, false)
+ return new RichTextField(data, id, false);
case Types.Key:
- return new Key(data, id, false)
+ return new Key(data, id, false);
case Types.Image:
- return new ImageField(new URL(data), id, false)
+ return new ImageField(new URL(data), id, false);
case Types.HistogramOp:
return HistogramField.FromJson(id, data);
case Types.PDF:
- return new PDFField(new URL(data), id, false)
+ return new PDFField(new URL(data), id, false);
case Types.List:
- return ListField.FromJson(id, data)
+ return ListField.FromJson(id, data);
case Types.Audio:
- return new AudioField(new URL(data), id, false)
+ return new AudioField(new URL(data), id, false);
case Types.Video:
- return new VideoField(new URL(data), id, false)
+ return new VideoField(new URL(data), id, false);
case Types.Tuple:
return new TupleField(data, id, false);
case Types.Ink:
return InkField.FromJson(id, data);
case Types.Document:
- let doc: Document = new Document(id, false)
- let fields: [string, string][] = data as [string, string][]
+ let doc: Document = new Document(id, false);
+ let fields: [string, string][] = data as [string, string][];
fields.forEach(element => {
doc._proxies.set(element[0], element[1]);
});
- return doc
+ return doc;
default:
- throw Error("Error, unrecognized field type received from server. If you just created a new field type, be sure to add it here");
+ throw Error(
+ "Error, unrecognized field type received from server. If you just created a new field type, be sure to add it here"
+ );
}
}
-} \ No newline at end of file
+}
diff --git a/src/server/database.ts b/src/server/database.ts
index 87a0b3c70..415acc09a 100644
--- a/src/server/database.ts
+++ b/src/server/database.ts
@@ -1,8 +1,4 @@
-import { action, configure } from 'mobx';
import * as mongodb from 'mongodb';
-import { ObjectID } from 'mongodb';
-import { Transferable } from './Message';
-import { Utils } from '../Utils';
export class Database {
public static Instance = new Database()
@@ -16,21 +12,39 @@ export class Database {
})
}
+ private currentWrites: { [_id: string]: Promise<void> } = {};
+
public update(id: string, value: any, callback: () => void) {
if (this.db) {
let collection = this.db.collection('documents');
- collection.updateOne({ _id: id }, { $set: value }, {
- upsert: true
- }, (err, res) => {
- if (err) {
- console.log(err.message);
- console.log(err.errmsg);
- }
- if (res) {
- // console.log(JSON.stringify(res.result));
- }
- callback()
- });
+ const prom = this.currentWrites[id];
+ const run = (promise: Promise<void>, resolve?: () => void) => {
+ collection.updateOne({ _id: id }, { $set: value }, {
+ upsert: true
+ }, (err, res) => {
+ if (err) {
+ console.log(err.message);
+ console.log(err.errmsg);
+ }
+ // if (res) {
+ // console.log(JSON.stringify(res.result));
+ // }
+ if (this.currentWrites[id] === promise) {
+ delete this.currentWrites[id]
+ }
+ if (resolve) {
+ resolve();
+ }
+ callback();
+ });
+ }
+ if (prom) {
+ const newProm: Promise<void> = prom.then(() => run(newProm));
+ this.currentWrites[id] = newProm;
+ } else {
+ const newProm: Promise<void> = new Promise<void>(res => run(newProm, res))
+ this.currentWrites[id] = newProm;
+ }
}
}
diff --git a/src/server/public/files/.gitignore b/src/server/public/files/.gitignore
new file mode 100644
index 000000000..f59ec20aa
--- /dev/null
+++ b/src/server/public/files/.gitignore
@@ -0,0 +1 @@
+* \ No newline at end of file
diff --git a/src/server/public/files/upload_a6a70d84ebb65febf7900e29f52cc86d.pdf b/src/server/public/files/upload_a6a70d84ebb65febf7900e29f52cc86d.pdf
new file mode 100644
index 000000000..dfd6ab339
--- /dev/null
+++ b/src/server/public/files/upload_a6a70d84ebb65febf7900e29f52cc86d.pdf
Binary files differ
diff --git a/src/server/public/files/upload_e72669595eae4384a2a32196496f4f05.pdf b/src/server/public/files/upload_e72669595eae4384a2a32196496f4f05.pdf
deleted file mode 100644
index 8e58bfddd..000000000
--- a/src/server/public/files/upload_e72669595eae4384a2a32196496f4f05.pdf
+++ /dev/null
Binary files differ
diff --git a/tsconfig.json b/tsconfig.json
index 2b2c69fe1..41db1d0a7 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -10,7 +10,7 @@
"lib": [
"dom",
"es2015"
- ]
+ ],
},
// "exclude": [
// "node_modules",