aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/nodes
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views/nodes')
-rw-r--r--src/client/views/nodes/CollectionFreeFormDocumentView.tsx173
-rw-r--r--src/client/views/nodes/DocumentView.tsx235
-rw-r--r--src/client/views/nodes/FieldView.tsx7
-rw-r--r--src/client/views/nodes/FormattedTextBox.scss4
-rw-r--r--src/client/views/nodes/FormattedTextBox.tsx53
-rw-r--r--src/client/views/nodes/ImageBox.scss4
-rw-r--r--src/client/views/nodes/ImageBox.tsx42
-rw-r--r--src/client/views/nodes/VideoBox.tsx3
-rw-r--r--src/client/views/nodes/WebBox.tsx19
9 files changed, 270 insertions, 270 deletions
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 7b7b7e65e..376af0b36 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -1,13 +1,16 @@
-import { computed, trace } from "mobx";
+import { computed, trace, action } from "mobx";
import { observer } from "mobx-react";
import { Transform } from "../../util/Transform";
import { DocumentView, DocumentViewProps, positionSchema } from "./DocumentView";
import "./DocumentView.scss";
import React = require("react");
-import { OmitKeys } from "../../../Utils";
import { DocComponent } from "../DocComponent";
-import { createSchema, makeInterface } from "../../../new_fields/Schema";
-import { FieldValue } from "../../../new_fields/Types";
+import { createSchema, makeInterface, listSpec } from "../../../new_fields/Schema";
+import { FieldValue, Cast, NumCast } from "../../../new_fields/Types";
+import { OmitKeys, Utils } from "../../../Utils";
+import { SelectionManager } from "../../util/SelectionManager";
+import { matchedData } from "express-validator/filter";
+import { Doc } from "../../../new_fields/Doc";
export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps {
}
@@ -23,18 +26,21 @@ const FreeformDocument = makeInterface(schema, positionSchema);
@observer
export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeFormDocumentViewProps, FreeformDocument>(FreeformDocument) {
private _mainCont = React.createRef<HTMLDivElement>();
+ private _downX: number = 0;
+ private _downY: number = 0;
- @computed
- get transform(): string {
+ @computed get transform() {
return `scale(${this.props.ContentScaling()}, ${this.props.ContentScaling()}) translate(${this.X}px, ${this.Y}px) scale(${this.zoom}, ${this.zoom}) `;
}
+ @computed get X() { return FieldValue(this.Document.x, 0); }
+ @computed get Y() { return FieldValue(this.Document.y, 0); }
@computed get zoom(): number { return 1 / FieldValue(this.Document.zoom, 1); }
- @computed get zIndex(): number { return FieldValue(this.Document.zIndex, 0); }
- @computed get width(): number { return FieldValue(this.Document.width, 0); }
- @computed get height(): number { return FieldValue(this.Document.height, 0); }
@computed get nativeWidth(): number { return FieldValue(this.Document.nativeWidth, 0); }
@computed get nativeHeight(): number { return FieldValue(this.Document.nativeHeight, 0); }
+ @computed get width(): number { return FieldValue(this.Document.width, 0); }
+ @computed get height(): number { return FieldValue(this.Document.height, 0); }
+ @computed get zIndex(): number { return FieldValue(this.Document.zIndex, 0); }
set width(w: number) {
this.Document.width = w;
@@ -42,36 +48,28 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
this.Document.height = this.nativeHeight / this.nativeWidth * w;
}
}
-
set height(h: number) {
this.Document.height = h;
if (this.nativeWidth && this.nativeHeight) {
this.Document.width = this.nativeWidth / this.nativeHeight * h;
}
}
-
set zIndex(h: number) {
this.Document.zIndex = h;
}
- get X() {
- return FieldValue(this.Document.x, 0);
- }
- get Y() {
- return FieldValue(this.Document.y, 0);
- }
- getTransform = (): Transform =>
- this.props.ScreenToLocalTransform()
- .translate(-this.X, -this.Y)
- .scale(1 / this.contentScaling()).scale(1 / this.zoom)
-
contentScaling = () => this.nativeWidth > 0 ? this.width / this.nativeWidth : 1;
panelWidth = () => this.props.PanelWidth();
panelHeight = () => this.props.PanelHeight();
+ toggleMinimized = () => this.toggleIcon();
+ getTransform = (): Transform => this.props.ScreenToLocalTransform()
+ .translate(-this.X, -this.Y)
+ .scale(1 / this.contentScaling()).scale(1 / this.zoom)
@computed
get docView() {
return <DocumentView {...OmitKeys(this.props, ['zoomFade']).omit}
+ toggleMinimized={this.toggleMinimized}
ContentScaling={this.contentScaling}
ScreenToLocalTransform={this.getTransform}
PanelWidth={this.panelWidth}
@@ -79,30 +77,121 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
/>;
}
+ animateBetweenIcon(first: boolean, icon: number[], targ: number[], width: number, height: number, stime: number, target: Doc, maximizing: boolean) {
+ setTimeout(() => {
+ let now = Date.now();
+ let progress = Math.min(1, (now - stime) / 200);
+ let pval = maximizing ?
+ [icon[0] + (targ[0] - icon[0]) * progress, icon[1] + (targ[1] - icon[1]) * progress] :
+ [targ[0] + (icon[0] - targ[0]) * progress, targ[1] + (icon[1] - targ[1]) * progress];
+ target.width = maximizing ? 25 + (width - 25) * progress : width + (25 - width) * progress;
+ target.height = maximizing ? 25 + (height - 25) * progress : height + (25 - height) * progress;
+ target.x = pval[0];
+ target.y = pval[1];
+ if (first) {
+ target.isMinimized = false;
+ }
+ if (now < stime + 200) {
+ this.animateBetweenIcon(false, icon, targ, width, height, stime, target, maximizing);
+ }
+ else {
+ if (!maximizing) {
+ target.isMinimized = true;
+ target.x = targ[0];
+ target.y = targ[1];
+ target.width = width;
+ target.height = height;
+ }
+ (target as any).isIconAnimating = false;
+ }
+ },
+ 2);
+ }
+ @action
+ public toggleIcon = async (): Promise<void> => {
+ SelectionManager.DeselectAll();
+ let isMinimized: boolean | undefined;
+ let minimizedDocSet = Cast(this.props.Document.linkTags, listSpec(Doc));
+ if (!minimizedDocSet) return;
+ minimizedDocSet.map(async minimizedDoc => {
+ if (minimizedDoc instanceof Document) {
+ this.props.addDocument && this.props.addDocument(minimizedDoc, false);
+ let maximizedDoc = await Cast(minimizedDoc.maximizedDoc, Doc);
+ if (maximizedDoc && !(maximizedDoc as any).isIconAnimating) {
+ (maximizedDoc as any).isIconAnimating = true;
+ if (isMinimized === undefined) {
+ let maximizedDocMinimizedState = Cast(maximizedDoc.isMinimized, "boolean");
+ isMinimized = (maximizedDocMinimizedState) ? true : false;
+ }
+ let minx = NumCast(minimizedDoc.x, undefined);
+ let miny = NumCast(minimizedDoc.y, undefined);
+ let maxx = NumCast(maximizedDoc.x, undefined);
+ let maxy = NumCast(maximizedDoc.y, undefined);
+ let maxw = NumCast(maximizedDoc.width, undefined);
+ let maxh = NumCast(maximizedDoc.height, undefined);
+ if (minx !== undefined && miny !== undefined && maxx !== undefined && maxy !== undefined &&
+ maxw !== undefined && maxh !== undefined) {
+ this.animateBetweenIcon(true, [minx, miny], [maxx, maxy], maxw, maxh, Date.now(), maximizedDoc, isMinimized);
+ }
+ }
+
+ }
+ })
+ }
+ onPointerDown = (e: React.PointerEvent): void => {
+ this._downX = e.clientX;
+ this._downY = e.clientY;
+ }
+ onClick = async (e: React.MouseEvent) => {
+ e.stopPropagation();
+ if (Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD &&
+ Math.abs(e.clientY - this._downY) < Utils.DRAG_THRESHOLD) {
+ const maxDoc = await Cast(this.props.Document.maximizedDoc, Doc);
+ if (maxDoc) { // bcz: need a better way to associate behaviors with click events on widget-documents
+ this.props.addDocument && this.props.addDocument(maxDoc, false);
+ this.toggleIcon();
+ }
+ }
+ }
+
+ borderRounding = () => {
+ let br = NumCast(this.props.Document.borderRounding);
+ return br >= 0 ? br :
+ NumCast(this.props.Document.nativeWidth) === 0 ?
+ Math.min(this.props.PanelWidth(), this.props.PanelHeight())
+ : Math.min(this.Document.nativeWidth || 0, this.Document.nativeHeight || 0);
+ }
+
render() {
+ let maximizedDoc = FieldValue(Cast(this.props.Document.maximizedDoc, Doc));
let zoomFade = 1;
- //var zoom = doc.GetNumber(KeyStore.Zoom, 1);
- // let transform = this.getTransform().scale(this.contentScaling()).inverse();
- // var [sptX, sptY] = transform.transformPoint(0, 0);
- // let [bptX, bptY] = transform.transformPoint(this.props.PanelWidth(), this.props.PanelHeight());
- // let w = bptX - sptX;
- // //zoomFade = area < 100 || area > 800 ? Math.max(0, Math.min(1, 2 - 5 * (zoom < this.scale ? this.scale / zoom : zoom / this.scale))) : 1;
- // let fadeUp = .75 * 1800;
- // let fadeDown = .075 * 1800;
- // zoomFade = w < fadeDown /* || w > fadeUp */ ? Math.max(0, Math.min(1, 2 - (w < fadeDown ? fadeDown / w : w / fadeUp))) : 1;
+ //var zoom = doc.GetNumber(KeyStore.ZoomBasis, 1);
+ let transform = this.getTransform().scale(this.contentScaling()).inverse();
+ var [sptX, sptY] = transform.transformPoint(0, 0);
+ let [bptX, bptY] = transform.transformPoint(this.props.PanelWidth(), this.props.PanelHeight());
+ let w = bptX - sptX;
+ //zoomFade = area < 100 || area > 800 ? Math.max(0, Math.min(1, 2 - 5 * (zoom < this.scale ? this.scale / zoom : zoom / this.scale))) : 1;
+ const screenWidth = 1800;
+ let fadeUp = .75 * screenWidth;
+ let fadeDown = (maximizedDoc ? .0075 : .075) * screenWidth;
+ zoomFade = w < fadeDown /* || w > fadeUp */ ? Math.max(0, Math.min(1, 2 - (w < fadeDown ? fadeDown / w : w / fadeUp))) : 1;
return (
- <div className="collectionFreeFormDocumentView-container" ref={this._mainCont} style={{
- opacity: zoomFade,
- transformOrigin: "left top",
- transform: this.transform,
- pointerEvents: (zoomFade < 0.09 ? "none" : "all"),
- width: this.width,
- height: this.height,
- position: "absolute",
- zIndex: this.zIndex,
- backgroundColor: "transparent"
- }} >
+ <div className="collectionFreeFormDocumentView-container" ref={this._mainCont}
+ onPointerDown={this.onPointerDown}
+ onClick={this.onClick}
+ style={{
+ opacity: zoomFade,
+ borderRadius: `${this.borderRounding()}px`,
+ transformOrigin: "left top",
+ transform: this.transform,
+ pointerEvents: (zoomFade < 0.09 ? "none" : "all"),
+ width: this.width,
+ height: this.height,
+ position: "absolute",
+ zIndex: this.zIndex,
+ backgroundColor: "transparent"
+ }} >
{this.docView}
</div>
);
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index ce338e9a7..aabc1633e 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -1,8 +1,7 @@
import { action, computed, runInAction } from "mobx";
import { observer } from "mobx-react";
-import { ServerUtils } from "../../../server/ServerUtil";
import { emptyFunction, Utils } from "../../../Utils";
-import { Documents } from "../../documents/Documents";
+import { Docs } from "../../documents/Documents";
import { DocumentManager } from "../../util/DocumentManager";
import { DragManager } from "../../util/DragManager";
import { SelectionManager } from "../../util/SelectionManager";
@@ -21,6 +20,10 @@ import { DocComponent } from "../DocComponent";
import { createSchema, makeInterface, listSpec } from "../../../new_fields/Schema";
import { FieldValue, Cast, PromiseValue } from "../../../new_fields/Types";
import { List } from "../../../new_fields/List";
+import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView";
+import { CurrentUserUtils } from "../../../server/authentication/models/current_user_utils";
+import { MarqueeView } from "../collections/collectionFreeForm/MarqueeView";
+import { DocServer } from "../../DocServer";
const linkSchema = createSchema({
title: "string",
@@ -48,6 +51,7 @@ export interface DocumentViewProps {
selectOnLoad: boolean;
parentActive: () => boolean;
whenActiveChanged: (isActive: boolean) => void;
+ toggleMinimized: () => void;
}
const schema = createSchema({
@@ -83,39 +87,16 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
@computed get active(): boolean { return SelectionManager.IsSelected(this) || this.props.parentActive(); }
@computed get topMost(): boolean { return this.props.isTopMost; }
- onPointerDown = (e: React.PointerEvent): void => {
- this._downX = e.clientX;
- this._downY = e.clientY;
- if (e.button === 2 && !this.isSelected()) {
- return;
- }
- 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);
- }
- e.stopPropagation();
- } else {
- if (this.active) {
- e.stopPropagation();
- document.removeEventListener("pointermove", this.onPointerMove);
- document.addEventListener("pointermove", this.onPointerMove);
- document.removeEventListener("pointerup", this.onPointerUp);
- document.addEventListener("pointerup", this.onPointerUp);
- }
- }
- }
-
+ @action
componentDidMount() {
if (this._mainCont.current) {
this._dropDisposer = DragManager.MakeDropTarget(this._mainCont.current, {
handlers: { drop: this.drop.bind(this) }
});
}
- runInAction(() => DocumentManager.Instance.DocumentViews.push(this));
+ DocumentManager.Instance.DocumentViews.push(this);
}
-
+ @action
componentDidUpdate() {
if (this._dropDisposer) {
this._dropDisposer();
@@ -126,21 +107,26 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
});
}
}
-
+ @action
componentWillUnmount() {
if (this._dropDisposer) {
this._dropDisposer();
}
- runInAction(() => DocumentManager.Instance.DocumentViews.splice(DocumentManager.Instance.DocumentViews.indexOf(this), 1));
+ DocumentManager.Instance.DocumentViews.splice(DocumentManager.Instance.DocumentViews.indexOf(this), 1);
+ }
+
+ stopPropagation = (e: React.SyntheticEvent) => {
+ e.stopPropagation();
}
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().scale(this.props.ContentScaling()).inverse().transformPoint(0, 0);
let dragData = new DragManager.DocumentDragData([this.props.Document]);
+ const [xoff, yoff] = this.props.ScreenToLocalTransform().scale(this.props.ContentScaling()).transformDirection(x - left, y - top);
dragData.aliasOnDrop = dropAliasOfDraggedDoc;
- dragData.xOffset = x - left;
- dragData.yOffset = y - top;
+ dragData.xOffset = xoff;
+ dragData.yOffset = yoff;
dragData.moveDocument = this.props.moveDocument;
DragManager.StartDocumentDrag([this._mainCont.current], dragData, x, y, {
handlers: {
@@ -151,49 +137,58 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
}
}
- onPointerMove = (e: PointerEvent): void => {
- if (e.cancelBubble) {
+ onClick = (e: React.MouseEvent): void => {
+ if (CurrentUserUtils.MainDocId !== this.props.Document.Id &&
+ (Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD &&
+ Math.abs(e.clientY - this._downY) < Utils.DRAG_THRESHOLD)) {
+ SelectionManager.SelectDoc(this, e.ctrlKey);
+ }
+ }
+ onPointerDown = (e: React.PointerEvent): void => {
+ this._downX = e.clientX;
+ this._downY = e.clientY;
+ if (CollectionFreeFormView.RIGHT_BTN_DRAG && (e.button === 2 || (e.button === 0 && e.altKey)) && !this.isSelected()) {
return;
}
- if (Math.abs(this._downX - e.clientX) > 3 || Math.abs(this._downY - e.clientY) > 3) {
+ 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);
+ }
+ e.stopPropagation();
+ } else if (this.active) {
document.removeEventListener("pointermove", this.onPointerMove);
+ document.addEventListener("pointermove", this.onPointerMove);
document.removeEventListener("pointerup", this.onPointerUp);
- if (!e.altKey && (!this.topMost || e.buttons === 2)) {
- this.startDragging(this._downX, this._downY, e.ctrlKey || e.altKey);
+ document.addEventListener("pointerup", this.onPointerUp);
+ e.preventDefault();
+ }
+ }
+ onPointerMove = (e: PointerEvent): void => {
+ if (!e.cancelBubble) {
+ 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 (!e.altKey && !this.topMost && (!CollectionFreeFormView.RIGHT_BTN_DRAG && e.buttons === 1) || (CollectionFreeFormView.RIGHT_BTN_DRAG && e.buttons === 2)) {
+ this.startDragging(this._downX, this._downY, e.ctrlKey || e.altKey);
+ }
}
+ e.stopPropagation(); // doesn't actually stop propagation since all our listeners are listening to events on 'document' however it does mark the event as cancelBubble=true which we test for in the move event handlers
+ e.preventDefault();
}
- e.stopPropagation();
- e.preventDefault();
}
onPointerUp = (e: PointerEvent): void => {
document.removeEventListener("pointermove", this.onPointerMove);
document.removeEventListener("pointerup", this.onPointerUp);
- e.stopPropagation();
- if (!SelectionManager.IsSelected(this) && e.button !== 2) {
- if (Math.abs(e.clientX - this._downX) < 4 && Math.abs(e.clientY - this._downY) < 4) {
- PromiseValue(Cast(this.props.Document.maximizedDoc, Doc)).then(maxdoc => {
- if (maxdoc instanceof Doc) {
- this.props.addDocument && this.props.addDocument(maxdoc, false);
- this.toggleMinimize(maxdoc, this.props.Document);
- } else {
- SelectionManager.SelectDoc(this, e.ctrlKey);
- }
- });
- }
- }
- }
- stopPropagation = (e: React.SyntheticEvent) => {
- e.stopPropagation();
}
deleteClicked = (): void => {
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);
- }
+ let kvp = Docs.KVPDocument(this.props.Document, { width: 300, height: 300 });
+ CollectionDockingView.Instance.AddRightSplit(kvp);
}
fullScreenClicked = (e: React.MouseEvent): void => {
const doc = Doc.MakeDelegate(FieldValue(this.Document.proto));
@@ -204,7 +199,6 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
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();
@@ -212,118 +206,21 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15);
}
- @action createIcon = (layoutString: string): Doc => {
- let iconDoc: Doc = Documents.IconDocument(layoutString);
- iconDoc.isMinimized = false;
- iconDoc.nativeWidth = 0;
- iconDoc.nativeHeight = 0;
- iconDoc.proto = this.props.Document;
- iconDoc.maximizedDoc = this.props.Document;
- this.Document.minimizedDoc = iconDoc;
- this.props.addDocument && this.props.addDocument(iconDoc, false);
- return iconDoc;
- }
-
- animateTransition(icon: number[], targ: number[], width: number, height: number, stime: number, target: Doc, maximizing: boolean) {
- setTimeout(() => {
- let now = Date.now();
- let progress = Math.min(1, (now - stime) / 200);
- let pval = maximizing ?
- [icon[0] + (targ[0] - icon[0]) * progress, icon[1] + (targ[1] - icon[1]) * progress] :
- [targ[0] + (icon[0] - targ[0]) * progress, targ[1] + (icon[1] - targ[1]) * progress];
- target.width = maximizing ? 25 + (width - 25) * progress : width + (25 - width) * progress;
- target.height = maximizing ? 25 + (height - 25) * progress : height + (25 - height) * progress;
- target.x = pval[0];
- target.y = pval[1];
- if (now < stime + 200) {
- this.animateTransition(icon, targ, width, height, stime, target, maximizing);
- }
- else {
- if (!maximizing) {
- target.isMinimized = true;
- target.x = targ[0];
- target.y = targ[1];
- target.width = width;
- target.height = height;
- }
- this._completed = true;
- }
- },
- 2);
- }
-
- _completed = true;
-
- @action
- public toggleMinimize = (maximized: Doc, minim: Doc): void => {
- SelectionManager.DeselectAll();
- if (this._completed) {
- this._completed = false;
- let minimized = Cast(maximized.isMinimized, "boolean", false);
- maximized.isMinimized = false;
- this.animateTransition(
- [Cast(minim.x, "number", 0), Cast(minim.y, "number", 0)],
- [Cast(maximized.x, "number", 0), Cast(maximized.y, "number", 0)],
- Cast(maximized.width, "number", 0), Cast(maximized.width, "number", 0),
- Date.now(), maximized, minimized);
- }
- }
-
- @action
- public minimize = async (): Promise<void> => {
- const mindoc = await Cast(this.props.Document.minimizedDoc, Doc);
- if (mindoc === undefined) {
- const background = await Cast(this.props.Document.backgroundLayout, "string");
- if (background === undefined) {
- const layout = await Cast(this.props.Document.layout, "string");
- if (layout) {
- this.createIcon(layout);
- this.toggleMinimize(this.props.Document, this.createIcon(layout));
- }
- } else {
- this.toggleMinimize(this.props.Document, this.createIcon(background));
- }
- } else {
- this.props.addDocument && this.props.addDocument(mindoc, false);
- this.toggleMinimize(this.props.Document, mindoc);
- }
- }
-
@undoBatch
@action
drop = async (e: Event, de: DragManager.DropEvent) => {
if (de.data instanceof DragManager.LinkDragData) {
- let sourceDoc: Doc = de.data.linkSourceDocument;
- let destDoc: Doc = this.props.Document;
- let linkDoc = LinkDoc();
-
- const protoDest = await Cast(destDoc.proto, Doc);
- const protoSrc = await Cast(sourceDoc.proto, Doc);
- UndoManager.RunInBatch(() => {
- linkDoc.title = "New Link";
- linkDoc.linkDescription = "";
- linkDoc.linkTags = "Default";
+ let sourceDoc = de.data.linkSourceDocument;
+ let destDoc = this.props.Document;
- let dstTarg = protoDest ? protoDest : destDoc;
- let srcTarg = protoSrc ? protoSrc : sourceDoc;
- linkDoc.linkedTo = dstTarg;
- linkDoc.linkedFrom = srcTarg;
- let linkedFrom = Cast(dstTarg.linkedFrom, listSpec(Doc));
- if (!linkedFrom) {
- dstTarg.linkedFrom = linkedFrom = new List<Doc>();
- }
- linkedFrom.push(linkDoc);
-
- let linkedTo = Cast(srcTarg.linkedTo, listSpec(Doc));
- if (!linkedTo) {
- srcTarg.linkedTo = linkedTo = new List<Doc>();
- }
- linkedTo.push(linkDoc);
- }, "document view drop");
+ const protoDest = destDoc.proto;
+ const protoSrc = sourceDoc.proto;
+ Doc.MakeLink(protoSrc ? protoSrc : sourceDoc, protoDest ? protoDest : destDoc);
e.stopPropagation();
}
}
+ @action
onDrop = (e: React.DragEvent) => {
let text = e.dataTransfer.getData("text/plain");
if (!e.isDefaultPrevented() && text && text.startsWith("<div")) {
@@ -349,7 +246,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
ContextMenu.Instance.addItem({ description: "Fields", event: this.fieldsClicked });
ContextMenu.Instance.addItem({ description: "Center", event: () => this.props.focus(this.props.Document) });
ContextMenu.Instance.addItem({ description: "Open Right", event: () => CollectionDockingView.Instance.AddRightSplit(this.props.Document) });
- ContextMenu.Instance.addItem({ description: "Copy URL", event: () => Utils.CopyText(ServerUtils.prepend("/doc/" + this.props.Document.Id)) });
+ ContextMenu.Instance.addItem({ description: "Copy URL", event: () => Utils.CopyText(DocServer.prepend("/doc/" + this.props.Document.Id)) });
ContextMenu.Instance.addItem({ description: "Copy ID", event: () => Utils.CopyText(this.props.Document[Id]) });
//ContextMenu.Instance.addItem({ description: "Docking", event: () => this.props.Document.SetNumber(KeyStore.ViewType, CollectionViewType.Docking) })
ContextMenu.Instance.addItem({ description: "Delete", event: this.deleteClicked });
@@ -362,11 +259,10 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
isSelected = () => SelectionManager.IsSelected(this);
select = (ctrlPressed: boolean) => SelectionManager.SelectDoc(this, ctrlPressed);
- @computed get nativeWidth() { return FieldValue(this.Document.nativeWidth) || 0; }
- @computed get nativeHeight() { return FieldValue(this.Document.nativeHeight) || 0; }
+ @computed get nativeWidth() { return FieldValue(this.Document.nativeWidth, 0); }
+ @computed get nativeHeight() { return FieldValue(this.Document.nativeHeight, 0); }
@computed get contents() { return (<DocumentContentsView {...this.props} isSelected={this.isSelected} select={this.select} layoutKey={"layout"} />); }
-
render() {
var scaling = this.props.ContentScaling();
var nativeHeight = this.nativeHeight > 0 ? this.nativeHeight.toString() + "px" : "100%";
@@ -376,11 +272,12 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
<div className={`documentView-node${this.props.isTopMost ? "-topmost" : ""}`}
ref={this._mainCont}
style={{
+ borderRadius: "inherit",
background: FieldValue(this.Document.backgroundColor) || "",
width: nativeWidth, height: nativeHeight,
transform: `scale(${scaling}, ${scaling})`
}}
- onDrop={this.onDrop} onContextMenu={this.onContextMenu} onPointerDown={this.onPointerDown}
+ onDrop={this.onDrop} onContextMenu={this.onContextMenu} onPointerDown={this.onPointerDown} onClick={this.onClick}
>
{this.contents}
</div>
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index b11a87d75..c00c47fc4 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -11,11 +11,11 @@ import { returnFalse, emptyFunction } from "../../../Utils";
import { CollectionView } from "../collections/CollectionView";
import { CollectionPDFView } from "../collections/CollectionPDFView";
import { CollectionVideoView } from "../collections/CollectionVideoView";
-import { IconField } from "../../../fields/IconFIeld";
import { IconBox } from "./IconBox";
import { Opt, Doc, FieldResult } from "../../../new_fields/Doc";
import { List } from "../../../new_fields/List";
import { ImageField, VideoField, AudioField } from "../../../new_fields/URLField";
+import { IconField } from "../../../new_fields/IconField";
//
@@ -38,6 +38,8 @@ export interface FieldViewProps {
active: () => boolean;
whenActiveChanged: (isActive: boolean) => void;
focus: (doc: Doc) => void;
+ PanelWidth: () => number;
+ PanelHeight: () => number;
}
@observer
@@ -91,6 +93,7 @@ export class FieldView extends React.Component<FieldViewProps> {
layoutKey={"layout"}
ContainingCollectionView={this.props.ContainingCollectionView}
parentActive={this.props.active}
+ toggleMinimized={emptyFunction}
whenActiveChanged={this.props.whenActiveChanged} />
);
}
@@ -106,7 +109,7 @@ export class FieldView extends React.Component<FieldViewProps> {
else if (typeof field === "number") {
return <p>{field}</p>;
}
- else if (field !== FieldWaiting) {
+ else if (!(field instanceof Promise)) {
return <p>{JSON.stringify(field)}</p>;
}
else {
diff --git a/src/client/views/nodes/FormattedTextBox.scss b/src/client/views/nodes/FormattedTextBox.scss
index 5eb2bf7ce..f4f37250f 100644
--- a/src/client/views/nodes/FormattedTextBox.scss
+++ b/src/client/views/nodes/FormattedTextBox.scss
@@ -12,9 +12,9 @@
.formattedTextBox-cont-scroll, .formattedTextBox-cont-hidden {
background: $light-color-secondary;
- padding: 0.9em;
+ padding: 0;
border-width: 0px;
- border-radius: $border-radius;
+ border-radius: inherit;
border-color: $intermediate-color;
box-sizing: border-box;
border-style: solid;
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index cb082dc69..c65b1ac89 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -1,4 +1,4 @@
-import { action, IReactionDisposer, reaction } from "mobx";
+import { action, IReactionDisposer, reaction, trace, computed } from "mobx";
import { baseKeymap } from "prosemirror-commands";
import { history } from "prosemirror-history";
import { keymap } from "prosemirror-keymap";
@@ -18,6 +18,9 @@ import { SelectionManager } from "../../util/SelectionManager";
import { DocComponent } from "../DocComponent";
import { createSchema, makeInterface } from "../../../new_fields/Schema";
import { Opt, Doc } from "../../../new_fields/Doc";
+import { observer } from "mobx-react";
+import { InkingControl } from "../InkingControl";
+import { StrCast } from "../../../new_fields/Types";
const { buildMenuItems } = require("prosemirror-example-setup");
const { menuBar } = require("prosemirror-menu");
@@ -49,6 +52,7 @@ const richTextSchema = createSchema({
type RichTextDocument = makeInterface<[typeof richTextSchema]>;
const RichTextDocument = makeInterface(richTextSchema);
+@observer
export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTextBoxOverlay), RichTextDocument>(RichTextDocument) {
public static LayoutString(fieldStr: string = "DataKey") {
return FieldView.LayoutString(FormattedTextBox, fieldStr);
@@ -112,34 +116,26 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
if (this._editorView) {
this._editorView.destroy();
}
- this.setupEditor(config, MainOverlayTextBox.Instance.TextDoc); // bcz: not sure why, but the order of events is such that this.props.Document hasn't updated yet, so without forcing the editor to the MainOverlayTextBox, it will display the previously focused textbox
+ this.setupEditor(config, this.props.Document);// MainOverlayTextBox.Instance.TextDoc); // bcz: not sure why, but the order of events is such that this.props.Document hasn't updated yet, so without forcing the editor to the MainOverlayTextBox, it will display the previously focused textbox
}
);
} else {
this._proxyReactionDisposer = reaction(() => this.props.isSelected(),
- () => this.props.isSelected() && MainOverlayTextBox.Instance.SetTextDoc(this.props.Document, this.props.fieldKey, this._ref.current!, this.props.ScreenToLocalTransform()));
+ () => this.props.isSelected() && MainOverlayTextBox.Instance.SetTextDoc(this.props.Document, this.props.fieldKey, this._ref.current!, this.props.ScreenToLocalTransform));
}
+
this._reactionDisposer = reaction(
() => {
const field = this.props.Document ? this.props.Document.GetT(this.props.fieldKey, RichTextField) : undefined;
return field && field !== FieldWaiting ? field.Data : undefined;
},
- field => {
- if (field && this._editorView && !this._applyingChange) {
- this._editorView.updateState(
- EditorState.fromJSON(config, JSON.parse(field))
- );
- }
- }
+ field => field && this._editorView && !this._applyingChange &&
+ this._editorView.updateState(EditorState.fromJSON(config, JSON.parse(field)))
);
this.setupEditor(config, this.props.Document);
}
- shouldComponentUpdate() {
- return false;
- }
-
private setupEditor(config: any, doc?: Document) {
let field = doc ? doc.GetT(this.props.fieldKey, RichTextField) : undefined;
if (this._ref.current) {
@@ -190,7 +186,9 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
onFocused = (e: React.FocusEvent): void => {
if (!this.props.isOverlay) {
- MainOverlayTextBox.Instance.SetTextDoc(this.props.Document, this.props.fieldKey, this._ref.current!, this.props.ScreenToLocalTransform());
+ if (MainOverlayTextBox.Instance.TextDoc !== this.props.Document) {
+ MainOverlayTextBox.Instance.SetTextDoc(this.props.Document, this.props.fieldKey, this._ref.current!, this.props.ScreenToLocalTransform);
+ }
} else {
if (this._ref.current) {
this._ref.current.scrollTop = MainOverlayTextBox.Instance.TextScroll;
@@ -231,6 +229,10 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
}
}
+ onClick = (e: React.MouseEvent): void => {
+ this._ref.current!.focus();
+ }
+
tooltipTextMenuPlugin() {
let myprops = this.props;
return new Plugin({
@@ -249,7 +251,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
});
}
- onKeyPress(e: React.KeyboardEvent) {
+ onKeyPress = (e: React.KeyboardEvent) => {
if (e.key === "Escape") {
SelectionManager.DeselectAll();
}
@@ -257,22 +259,33 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
if (e.keyCode === 9) e.preventDefault();
// stop propagation doesn't seem to stop propagation of native keyboard events.
// so we set a flag on the native event that marks that the event's been handled.
- // (e.nativeEvent as any).DASHFormattedTextBoxHandled = true;
+ (e.nativeEvent as any).DASHFormattedTextBoxHandled = true;
+ if (StrCast(this.props.Document.title).startsWith("-") && this._editorView) {
+ let str = this._editorView.state.doc.textContent;
+ let titlestr = str.substr(0, Math.min(40, str.length));
+ this.props.Document.title = "-" + titlestr + (str.length > 40 ? "..." : "");
+ }
}
render() {
- let style = this.props.isSelected() || this.props.isOverlay ? "scroll" : "hidden";
+ let style = this.props.isOverlay ? "scroll" : "hidden";
+ let color = StrCast(this.props.Document.backgroundColor);
+ let interactive = InkingControl.Instance.selectedTool ? "" : "interactive";
return (
<div className={`formattedTextBox-cont-${style}`}
+ style={{
+ pointerEvents: interactive ? "all" : "none",
+ background: color,
+ }}
onKeyDown={this.onKeyPress}
onKeyPress={this.onKeyPress}
onFocus={this.onFocused}
+ onClick={this.onClick}
onPointerUp={this.onPointerUp}
onPointerDown={this.onPointerDown}
onContextMenu={this.specificContextMenu}
// tfs: do we need this event handler
onWheel={this.onPointerWheel}
- ref={this._ref}
- />
+ ref={this._ref} />
);
}
}
diff --git a/src/client/views/nodes/ImageBox.scss b/src/client/views/nodes/ImageBox.scss
index f4b3837ff..9fe211df0 100644
--- a/src/client/views/nodes/ImageBox.scss
+++ b/src/client/views/nodes/ImageBox.scss
@@ -6,6 +6,10 @@
height: auto;
max-width: 100%;
max-height: 100%;
+ pointer-events: none;
+}
+.imageBox-cont-interactive {
+ pointer-events: all;
}
.imageBox-dot {
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index cd003ba71..0e9e904a8 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -10,12 +10,14 @@ import { ContextMenu } from "../../views/ContextMenu";
import { FieldView, FieldViewProps } from './FieldView';
import "./ImageBox.scss";
import React = require("react");
-import { createSchema, makeInterface } from '../../../new_fields/Schema';
+import { createSchema, makeInterface, listSpec } from '../../../new_fields/Schema';
import { DocComponent } from '../DocComponent';
import { positionSchema } from './DocumentView';
-import { FieldValue, Cast } from '../../../new_fields/Types';
+import { FieldValue, Cast, StrCast } from '../../../new_fields/Types';
import { ImageField } from '../../../new_fields/URLField';
import { List } from '../../../new_fields/List';
+import { InkingControl } from '../InkingControl';
+import { Doc } from '../../../new_fields/Doc';
export const pageSchema = createSchema({
curPage: "number"
@@ -28,7 +30,7 @@ const ImageDocument = makeInterface(pageSchema, positionSchema);
export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageDocument) {
public static LayoutString() { return FieldView.LayoutString(ImageBox); }
- private _imgRef: React.RefObject<HTMLImageElement>;
+ private _imgRef: React.RefObject<HTMLImageElement> = React.createRef();
private _downX: number = 0;
private _downY: number = 0;
private _lastTap: number = 0;
@@ -36,12 +38,6 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
@observable private _isOpen: boolean = false;
private dropDisposer?: DragManager.DragDropDisposer;
- constructor(props: FieldViewProps) {
- super(props);
-
- this._imgRef = React.createRef();
- }
-
@action
onLoad = (target: any) => {
var h = this._imgRef.current!.naturalHeight;
@@ -71,19 +67,18 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
@undoBatch
drop = (e: Event, de: DragManager.DropEvent) => {
if (de.data instanceof DragManager.DocumentDragData) {
- de.data.droppedDocuments.forEach(action((drop: Document) => {
- let layout = drop.GetText(KeyStore.BackgroundLayout, "");
+ de.data.droppedDocuments.forEach(action((drop: Doc) => {
+ let layout = StrCast(drop.backgroundLayout);
if (layout.indexOf(ImageBox.name) !== -1) {
- let imgData = this.props.Document.Get(KeyStore.Data);
- if (imgData instanceof ImageField && imgData) {
- this.props.Document.SetOnPrototype(KeyStore.Data, new ListField([imgData]));
+ let imgData = this.props.Document[this.props.fieldKey];
+ if (imgData instanceof ImageField) {
+ Doc.SetOnPrototype(this.props.Document, "data", new List([imgData]));
}
- let imgList = this.props.Document.GetList(KeyStore.Data, [] as any[]);
+ let imgList = Cast(this.props.Document[this.props.fieldKey], listSpec(ImageField), [] as any[]);
if (imgList) {
- let field = drop.Get(KeyStore.Data);
- if (field === FieldWaiting) { }
- else if (field instanceof ImageField) imgList.push(field);
- else if (field instanceof ListField) imgList.push(field.Data);
+ let field = drop.data;
+ if (field instanceof ImageField) imgList.push(field);
+ else if (field instanceof List) imgList.concat(field);
}
e.stopPropagation();
}
@@ -95,7 +90,6 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
onPointerDown = (e: React.PointerEvent): void => {
if (Date.now() - this._lastTap < 300) {
if (e.buttons === 1) {
- e.stopPropagation();
this._downX = e.clientX;
this._downY = e.clientY;
document.removeEventListener("pointerup", this.onPointerUp);
@@ -135,7 +129,7 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
specificContextMenu = (e: React.MouseEvent): void => {
let field = Cast(this.Document[this.props.fieldKey], ImageField);
- if (field && field !== FieldWaiting) {
+ if (field) {
let url = field.url.href;
ContextMenu.Instance.addItem({
description: "Copy path", event: () => {
@@ -165,12 +159,12 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
render() {
let field = this.Document[this.props.fieldKey];
let paths: string[] = ["http://www.cs.brown.edu/~bcz/face.gif"];
- if (field === FieldWaiting) paths = ["https://image.flaticon.com/icons/svg/66/66163.svg"];
- else if (field instanceof ImageField) paths = [field.url.href];
+ if (field instanceof ImageField) paths = [field.url.href];
else if (field instanceof List) paths = field.filter(val => val instanceof ImageField).map(p => (p as ImageField).url.href);
let nativeWidth = FieldValue(this.Document.nativeWidth, 1);
+ let interactive = InkingControl.Instance.selectedTool ? "" : "-interactive";
return (
- <div className="imageBox-cont" onPointerDown={this.onPointerDown} onDrop={this.onDrop} ref={this.createDropTarget} onContextMenu={this.specificContextMenu}>
+ <div className={`imageBox-cont${interactive}`} onPointerDown={this.onPointerDown} onDrop={this.onDrop} ref={this.createDropTarget} onContextMenu={this.specificContextMenu}>
<img src={paths[Math.min(paths.length, this._photoIndex)]} style={{ objectFit: (this._photoIndex === 0 ? undefined : "contain") }} width={nativeWidth} alt="Image not found" ref={this._imgRef} onLoad={this.onLoad} />
{paths.length > 1 ? this.dots(paths) : (null)}
{this.lightbox(paths)}
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index 7b922b620..86276660a 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -2,7 +2,6 @@ import React = require("react");
import { observer } from "mobx-react";
import { FieldView, FieldViewProps } from './FieldView';
import "./VideoBox.scss";
-import Measure from "react-measure";
import { action, computed } from "mobx";
import { DocComponent } from "../DocComponent";
import { positionSchema } from "./DocumentView";
@@ -10,6 +9,8 @@ import { makeInterface } from "../../../new_fields/Schema";
import { pageSchema } from "./ImageBox";
import { Cast, FieldValue } from "../../../new_fields/Types";
import { VideoField } from "../../../new_fields/URLField";
+import Measure from "react-measure";
+import "./VideoBox.scss";
type VideoDocument = makeInterface<[typeof positionSchema, typeof pageSchema]>;
const VideoDocument = makeInterface(positionSchema, pageSchema);
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 63778fa50..c12fd5655 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -1,20 +1,17 @@
import "./WebBox.scss";
import React = require("react");
import { FieldViewProps, FieldView } from './FieldView';
-import { observer } from "mobx-react";
-import { computed } from 'mobx';
import { HtmlField } from "../../../new_fields/HtmlField";
import { WebField } from "../../../new_fields/URLField";
+import { observer } from "mobx-react";
+import { computed, reaction, IReactionDisposer } from 'mobx';
+import { DocumentDecorations } from "../DocumentDecorations";
@observer
export class WebBox extends React.Component<FieldViewProps> {
public static LayoutString() { return FieldView.LayoutString(WebBox); }
- constructor(props: FieldViewProps) {
- super(props);
- }
-
_ignore = 0;
onPreWheel = (e: React.WheelEvent) => {
this._ignore = e.timeStamp;
@@ -36,23 +33,25 @@ export class WebBox extends React.Component<FieldViewProps> {
let field = this.props.Document[this.props.fieldKey];
let view;
if (field instanceof HtmlField) {
- view = <span id="webBox-htmlSpan" dangerouslySetInnerHTML={{ __html: field.html }} />
+ view = <span id="webBox-htmlSpan" dangerouslySetInnerHTML={{ __html: field.html }} />;
} else if (field instanceof WebField) {
- view = <iframe src={field.url.href} style={{ position: "absolute", width: "100%", height: "100%" }} />
+ view = <iframe src={field.url.href} style={{ position: "absolute", width: "100%", height: "100%" }} />;
} else {
- view = <iframe src={"https://crossorigin.me/https://cs.brown.edu"} style={{ position: "absolute", width: "100%", height: "100%" }} />
+ view = <iframe src={"https://crossorigin.me/https://cs.brown.edu"} style={{ position: "absolute", width: "100%", height: "100%" }} />;
}
let content =
<div style={{ width: "100%", height: "100%", position: "absolute" }} onWheel={this.onPostWheel} onPointerDown={this.onPostPointer} onPointerMove={this.onPostPointer} onPointerUp={this.onPostPointer}>
{view}
</div>;
+ let frozen = !this.props.isSelected() || DocumentDecorations.Instance.Interacting;
+
return (
<>
<div className="webBox-cont" >
{content}
</div>
- {this.props.isSelected() ? (null) : <div onWheel={this.onPreWheel} onPointerDown={this.onPrePointer} onPointerMove={this.onPrePointer} onPointerUp={this.onPrePointer} style={{ width: "100%", height: "100%", position: "absolute" }} />}
+ {!frozen ? (null) : <div onWheel={this.onPreWheel} onPointerDown={this.onPrePointer} onPointerMove={this.onPrePointer} onPointerUp={this.onPrePointer} style={{ width: "100%", height: "100%", position: "absolute" }} />}
</>);
}
} \ No newline at end of file