aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/collections
diff options
context:
space:
mode:
authorlaurawilsonri <laura_wilson@brown.edu>2019-03-18 18:14:27 -0400
committerlaurawilsonri <laura_wilson@brown.edu>2019-03-18 18:14:27 -0400
commit9d939c1190ef86e456cf26e9f5cb84743279f7a6 (patch)
tree24d72d918f2becf6762ea30b92da7339957eedbd /src/client/views/collections
parent67170e521366f8178645cc85aaf377e53b1a6f21 (diff)
parentf70ad315167b714f11f7d68f35a46abe9e525a4d (diff)
Merge branch 'master' of https://github.com/browngraphicslab/Dash-Web into editableSchema
Diffstat (limited to 'src/client/views/collections')
-rw-r--r--src/client/views/collections/CollectionDockingView.tsx8
-rw-r--r--src/client/views/collections/CollectionFreeFormView.scss108
-rw-r--r--src/client/views/collections/CollectionFreeFormView.tsx367
-rw-r--r--src/client/views/collections/CollectionPDFView.scss27
-rw-r--r--src/client/views/collections/CollectionPDFView.tsx28
-rw-r--r--src/client/views/collections/CollectionSchemaView.scss108
-rw-r--r--src/client/views/collections/CollectionSchemaView.tsx230
-rw-r--r--src/client/views/collections/CollectionTreeView.scss32
-rw-r--r--src/client/views/collections/CollectionTreeView.tsx30
-rw-r--r--src/client/views/collections/CollectionVideoView.scss40
-rw-r--r--src/client/views/collections/CollectionVideoView.tsx118
-rw-r--r--src/client/views/collections/CollectionView.tsx18
-rw-r--r--src/client/views/collections/CollectionViewBase.tsx78
-rw-r--r--src/client/views/collections/MarqueeView.scss8
-rw-r--r--src/client/views/collections/MarqueeView.tsx175
-rw-r--r--src/client/views/collections/PreviewCursor.scss18
-rw-r--r--src/client/views/collections/PreviewCursor.tsx73
17 files changed, 1106 insertions, 360 deletions
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 94005a4c0..19788447e 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -144,7 +144,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
if (this._containerRef.current) {
reaction(
() => this.props.Document.GetText(KeyStore.Data, ""),
- () => this.setupGoldenLayout(), { fireImmediately: true });
+ () => setTimeout(() => this.setupGoldenLayout(), 1), { fireImmediately: true });
window.addEventListener('resize', this.onResize); // bcz: would rather add this event to the parent node, but resize events only come from Window
}
@@ -212,6 +212,12 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
stack.remove();
//}
}));
+ stack.header.controlsContainer.find('.lm_popout') //get the close icon
+ .off('click') //unbind the current click handler
+ .click(action(function () {
+ var url = "http://localhost:1050/doc/" + stack.contentItems[0].tab.contentItem.config.props.documentId;
+ let win = window.open(url, stack.contentItems[0].tab.title, "width=300,height=400");
+ }));
}
render() {
diff --git a/src/client/views/collections/CollectionFreeFormView.scss b/src/client/views/collections/CollectionFreeFormView.scss
index d487cd7ce..11addc5a1 100644
--- a/src/client/views/collections/CollectionFreeFormView.scss
+++ b/src/client/views/collections/CollectionFreeFormView.scss
@@ -1,36 +1,56 @@
+@import "../global_variables";
+
.collectionfreeformview-container {
+ .collectionfreeformview > .jsx-parser {
+ position: absolute;
+ height: 100%;
+ width: 100%;
+ }
- .collectionfreeformview > .jsx-parser{
- position:absolute;
- height: 100%;
- width: 100%;
- }
-
- border-style: solid;
- box-sizing: border-box;
- position: relative;
+ //nested freeform views
+ // .collectionfreeformview-container {
+ // background-image: linear-gradient(to right, $light-color-secondary 1px, transparent 1px),
+ // linear-gradient(to bottom, $light-color-secondary 1px, transparent 1px);
+ // background-size: 30px 30px;
+ // }
+
+ border: 0px solid $light-color-secondary;
+ border-radius: $border-radius;
+ box-sizing: border-box;
+ position: relative;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ overflow: hidden;
+ box-shadow: $intermediate-color 0.2vw 0.2vw 0.8vw;
+ .collectionfreeformview {
+ position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
- overflow: hidden;
- .collectionfreeformview {
- position: absolute;
- top: 0;
- left: 0;
- width:100%;
- height: 100%;
- }
-}
-.collectionfreeformview-marquee{
- border-style: dashed;
- box-sizing: border-box;
- position: absolute;
- border-width: 1px;
- border-color: black;
+ }
}
.collectionfreeformview-overlay {
+ .collectionfreeformview > .jsx-parser {
+ position: absolute;
+ height: 100%;
+ }
+ .formattedTextBox-cont {
+ background: $light-color-secondary;
+ }
+ position:absolute;
+ border: 0px solid transparent;
+ border-radius: $border-radius;
+ overflow: hidden;
+ box-sizing: border-box;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ .collectionfreeformview {
.collectionfreeformview > .jsx-parser{
position:absolute;
height: 100%;
@@ -39,37 +59,39 @@
background:yellow;
}
- border-style: solid;
- box-sizing: border-box;
+ // overflow: hidden;
+ // border-style: solid;
+ // box-sizing: border-box;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
- overflow: hidden;
- .collectionfreeformview {
- position: absolute;
- top: 0;
- left: 0;
- width:100%;
- height: 100%;
- }
+ }
}
+// selection border...?
.border {
- border-style: solid;
- box-sizing: border-box;
- width: 100%;
- height: 100%;
+ border-style: solid;
+ box-sizing: border-box;
+ width: 98%;
+ height: 98%;
+ border-radius: $border-radius;
}
//this is an animation for the blinking cursor!
@keyframes blink {
- 0% {opacity: 0}
- 49%{opacity: 0}
- 50% {opacity: 1}
+ 0% {
+ opacity: 0;
+ }
+ 49% {
+ opacity: 0;
+ }
+ 50% {
+ opacity: 1;
+ }
}
#prevCursor {
- animation: blink 1s infinite;
-} \ No newline at end of file
+ animation: blink 1s infinite;
+}
diff --git a/src/client/views/collections/CollectionFreeFormView.tsx b/src/client/views/collections/CollectionFreeFormView.tsx
index ab41d1378..8bf4a7539 100644
--- a/src/client/views/collections/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/CollectionFreeFormView.tsx
@@ -5,46 +5,63 @@ import { FieldWaiting } from "../../../fields/Field";
import { KeyStore } from "../../../fields/KeyStore";
import { ListField } from "../../../fields/ListField";
import { TextField } from "../../../fields/TextField";
-import { Documents } from "../../documents/Documents";
import { DragManager } from "../../util/DragManager";
import { Transform } from "../../util/Transform";
import { undoBatch } from "../../util/UndoManager";
-import { CollectionDockingView } from "../collections/CollectionDockingView";
-import { CollectionPDFView } from "../collections/CollectionPDFView";
-import { CollectionSchemaView } from "../collections/CollectionSchemaView";
-import { CollectionView } from "../collections/CollectionView";
import { InkingCanvas } from "../InkingCanvas";
import { CollectionFreeFormDocumentView } from "../nodes/CollectionFreeFormDocumentView";
-import { DocumentView } from "../nodes/DocumentView";
-import { FormattedTextBox } from "../nodes/FormattedTextBox";
-import { ImageBox } from "../nodes/ImageBox";
-import { KeyValueBox } from "../nodes/KeyValueBox";
-import { PDFBox } from "../nodes/PDFBox";
-import { WebBox } from "../nodes/WebBox";
+import { DocumentContentsView } from "../nodes/DocumentContentsView";
+import { DocumentViewProps } from "../nodes/DocumentView";
import "./CollectionFreeFormView.scss";
import { COLLECTION_BORDER_WIDTH } from "./CollectionView";
import { CollectionViewBase } from "./CollectionViewBase";
+import { MarqueeView } from "./MarqueeView";
+import { PreviewCursor } from "./PreviewCursor";
import React = require("react");
-import { SelectionManager } from "../../util/SelectionManager";
-const JsxParser = require('react-jsx-parser').default;//TODO Why does this need to be imported like this?
+import v5 = require("uuid/v5");
@observer
export class CollectionFreeFormView extends CollectionViewBase {
- private _canvasRef = React.createRef<HTMLDivElement>();
- @observable
- private _lastX: number = 0;
- @observable
- private _lastY: number = 0;
+ public _canvasRef = React.createRef<HTMLDivElement>();
private _selectOnLoaded: string = ""; // id of document that should be selected once it's loaded (used for click-to-type)
- @observable
- private _downX: number = 0;
- @observable
- private _downY: number = 0;
+ public addLiveTextBox = (newBox: Document) => {
+ // mark this collection so that when the text box is created we can send it the SelectOnLoad prop to focus itself
+ this._selectOnLoaded = newBox.Id;
+ //set text to be the typed key and get focus on text box
+ this.props.addDocument(newBox, false);
+ //remove cursor from screen
+ this.PreviewCursorVisible = false;
+ }
+
+ public selectDocuments = (docs: Document[]) => {
+ this.props.CollectionView.SelectedDocs.length = 0;
+ docs.map(d => this.props.CollectionView.SelectedDocs.push(d.Id));
+ }
+
+ public getActiveDocuments = () => {
+ var curPage = this.props.Document.GetNumber(KeyStore.CurPage, -1);
+ const lvalue = this.props.Document.GetT<ListField<Document>>(this.props.fieldKey, ListField);
+ let active: Document[] = [];
+ if (lvalue && lvalue != FieldWaiting) {
+ lvalue.Data.map(doc => {
+ var page = doc.GetNumber(KeyStore.Page, -1);
+ if (page == curPage || page == -1) {
+ active.push(doc);
+ }
+ })
+ }
+
+ return active;
+ }
//determines whether the blinking cursor for indicating whether a text will be made on key down is visible
- @observable
- private _previewCursorVisible: boolean = false;
+ @observable public PreviewCursorVisible: boolean = false;
+ @observable public MarqueeVisible = false;
+ @observable public DownX: number = 0;
+ @observable public DownY: number = 0;
+ @observable private _lastX: number = 0;
+ @observable private _lastY: number = 0;
@computed get panX(): number { return this.props.Document.GetNumber(KeyStore.PanX, 0) }
@computed get panY(): number { return this.props.Document.GetNumber(KeyStore.PanY, 0) }
@@ -60,52 +77,44 @@ export class CollectionFreeFormView extends CollectionViewBase {
@action
drop = (e: Event, de: DragManager.DropEvent) => {
super.drop(e, de);
- const docView: DocumentView = de.data["documentView"];
- let doc: Document = docView ? docView.props.Document : de.data["document"];
- if (doc) {
- let screenX = de.x - (de.data["xOffset"] as number || 0);
- let screenY = de.y - (de.data["yOffset"] as number || 0);
+ if (de.data instanceof DragManager.DocumentDragData) {
+ let screenX = de.x - (de.data.xOffset as number || 0);
+ let screenY = de.y - (de.data.yOffset as number || 0);
const [x, y] = this.getTransform().transformPoint(screenX, screenY);
- doc.SetNumber(KeyStore.X, x);
- doc.SetNumber(KeyStore.Y, y);
- this.bringToFront(doc);
+ de.data.droppedDocument.SetNumber(KeyStore.X, x);
+ de.data.droppedDocument.SetNumber(KeyStore.Y, y);
+ this.bringToFront(de.data.droppedDocument);
}
}
- @observable
- _marquee = false;
+
+ @action
+ cleanupInteractions = () => {
+ document.removeEventListener("pointermove", this.onPointerMove);
+ document.removeEventListener("pointerup", this.onPointerUp);
+ this.MarqueeVisible = false;
+ }
@action
onPointerDown = (e: React.PointerEvent): void => {
+ this.PreviewCursorVisible = false;
if ((e.button === 2 && this.props.active() && (!this.isAnnotationOverlay || this.zoomScaling != 1)) || e.button == 0) {
document.removeEventListener("pointermove", this.onPointerMove);
document.addEventListener("pointermove", this.onPointerMove);
document.removeEventListener("pointerup", this.onPointerUp);
document.addEventListener("pointerup", this.onPointerUp);
- this._lastX = e.pageX;
- this._lastY = e.pageY;
- this._downX = e.pageX;
- this._downY = e.pageY;
+ this._lastX = this.DownX = e.pageX;
+ this._lastY = this.DownY = e.pageY;
}
}
@action
onPointerUp = (e: PointerEvent): void => {
- if (this._marquee) {
- document.removeEventListener("keydown", this.marqueeCommand);
- }
e.stopPropagation();
- if (this._marquee) {
- if (!e.shiftKey) {
- SelectionManager.DeselectAll();
- }
- var selectedDocs = this.marqueeSelect();
- selectedDocs.map(s => this.props.CollectionView.SelectedDocs.push(s.Id));
- }
- else if (!this._marquee && Math.abs(this._downX - e.clientX) < 3 && Math.abs(this._downY - e.clientY) < 3) {
+ if (Math.abs(this.DownX - e.clientX) < 4 && Math.abs(this.DownY - e.clientY) < 4) {
//show preview text cursor on tap
- this._previewCursorVisible = true;
+ this.PreviewCursorVisible = true;
//select is not already selected
if (!this.props.isSelected()) {
this.props.select(false);
@@ -115,98 +124,26 @@ export class CollectionFreeFormView extends CollectionViewBase {
}
@action
- cleanupInteractions = () => {
- document.removeEventListener("keydown", this.marqueeCommand);
- document.removeEventListener("pointermove", this.onPointerMove);
- document.removeEventListener("pointerup", this.onPointerUp);
- this._marquee = false;
- }
-
- intersectRect(r1: { left: number, right: number, top: number, bottom: number },
- r2: { left: number, right: number, top: number, bottom: number }) {
- return !(r2.left > r1.right ||
- r2.right < r1.left ||
- r2.top > r1.bottom ||
- r2.bottom < r1.top);
- }
-
- marqueeSelect() {
- this.props.CollectionView.SelectedDocs.length = 0;
- var curPage = this.props.Document.GetNumber(KeyStore.CurPage, 1);
- let p = this.getTransform().transformPoint(this._downX < this._lastX ? this._downX : this._lastX, this._downY < this._lastY ? this._downY : this._lastY);
- let v = this.getTransform().transformDirection(this._lastX - this._downX, this._lastY - this._downY);
- let selRect = { left: p[0], top: p[1], right: p[0] + Math.abs(v[0]), bottom: p[1] + Math.abs(v[1]) }
-
- var curPage = this.props.Document.GetNumber(KeyStore.CurPage, 1);
- const lvalue = this.props.Document.GetT<ListField<Document>>(this.props.fieldKey, ListField);
- let selection: Document[] = [];
- if (lvalue && lvalue != FieldWaiting) {
- lvalue.Data.map(doc => {
- var page = doc.GetNumber(KeyStore.Page, 0);
- if (page == curPage || page == 0) {
- var x = doc.GetNumber(KeyStore.X, 0);
- var y = doc.GetNumber(KeyStore.Y, 0);
- var w = doc.GetNumber(KeyStore.Width, 0);
- var h = doc.GetNumber(KeyStore.Height, 0);
- if (this.intersectRect({ left: x, top: y, right: x + w, bottom: y + h }, selRect))
- selection.push(doc)
- }
- })
- }
- return selection;
- }
-
- @action
onPointerMove = (e: PointerEvent): void => {
if (!e.cancelBubble && this.props.active()) {
- let wasMarquee = this._marquee;
- this._marquee = e.buttons != 2 && !e.altKey && !e.metaKey;
- if (this._marquee && !wasMarquee) {
- this._previewCursorVisible = false;
- document.addEventListener("keydown", this.marqueeCommand);
+ if (e.buttons == 1 && !e.altKey && !e.metaKey) {
+ this.MarqueeVisible = true;
}
- if (this._marquee) {
+ if (this.MarqueeVisible) {
e.stopPropagation();
e.preventDefault();
}
-
- if (!this._marquee && (!this.isAnnotationOverlay || this.zoomScaling != 1) && !e.shiftKey) {
+ else if ((!this.isAnnotationOverlay || this.zoomScaling != 1) && !e.shiftKey) {
let x = this.props.Document.GetNumber(KeyStore.PanX, 0);
let y = this.props.Document.GetNumber(KeyStore.PanY, 0);
let [dx, dy] = this.getTransform().transformDirection(e.clientX - this._lastX, e.clientY - this._lastY);
- this._previewCursorVisible = false;
this.SetPan(x - dx, y - dy);
+ this._lastX = e.pageX;
+ this._lastY = e.pageY;
e.stopPropagation();
e.preventDefault();
}
}
- this._lastX = e.pageX;
- this._lastY = e.pageY;
- }
-
- @action
- marqueeCommand = (e: KeyboardEvent) => {
- if (e.key == "Backspace") {
- this.marqueeSelect().map(d => this.props.removeDocument(d));
- this.cleanupInteractions();
- }
- if (e.key == "c") {
- let p = this.getTransform().transformPoint(this._downX < this._lastX ? this._downX : this._lastX, this._downY < this._lastY ? this._downY : this._lastY);
- let v = this.getTransform().transformDirection(this._lastX - this._downX, this._lastY - this._downY);
-
- let selected = this.marqueeSelect().map(m => m);
- this.marqueeSelect().map(d => this.props.removeDocument(d));
- //setTimeout(() => {
- this.props.CollectionView.addDocument(Documents.FreeformDocument(selected.map(d => {
- d.SetNumber(KeyStore.X, d.GetNumber(KeyStore.X, 0) - p[0] - v[0] / 2);
- d.SetNumber(KeyStore.Y, d.GetNumber(KeyStore.Y, 0) - p[1] - v[1] / 2);
- d.SetNumber(KeyStore.Page, this.props.Document.GetNumber(KeyStore.Page, 0));
- d.SetText(KeyStore.Title, "" + d.GetNumber(KeyStore.Width, 0) + " " + d.GetNumber(KeyStore.Height, 0));
- return d;
- }), { x: p[0], y: p[1], panx: 0, pany: 0, width: v[0], height: v[1], title: "a nested collection" }));
- // }, 100);
- this.cleanupInteractions();
- }
}
@action
@@ -247,7 +184,6 @@ export class CollectionFreeFormView extends CollectionViewBase {
@action
private SetPan(panX: number, panY: number) {
var x1 = this.getLocalTransform().inverse().Scale;
- var x2 = this.getTransform().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));
this.props.Document.SetNumber(KeyStore.PanX, this.isAnnotationOverlay ? newPanX : panX);
@@ -264,24 +200,6 @@ export class CollectionFreeFormView extends CollectionViewBase {
}
@action
- onKeyDown = (e: React.KeyboardEvent<Element>) => {
- //if not these keys, make a textbox if preview cursor is active!
- if (!e.ctrlKey && !e.altKey) {
- if (this._previewCursorVisible) {
- //make textbox and add it to this collection
- let [x, y] = this.getTransform().transformPoint(this._downX, this._downY); (this._downX, this._downY);
- let newBox = Documents.TextDocument({ width: 200, height: 100, x: x, y: y, title: "new" });
- // mark this collection so that when the text box is created we can send it the SelectOnLoad prop to focus itself
- this._selectOnLoaded = newBox.Id;
- //set text to be the typed key and get focus on text box
- this.props.CollectionView.addDocument(newBox);
- //remove cursor from screen
- this._previewCursorVisible = false;
- }
- }
- }
-
- @action
bringToFront(doc: Document) {
const { fieldKey: fieldKey, Document: Document } = this.props;
@@ -319,27 +237,31 @@ export class CollectionFreeFormView extends CollectionViewBase {
this.props.focus(this.props.Document);
}
+ getDocumentViewProps(document: Document): DocumentViewProps {
+ return {
+ Document: document,
+ AddDocument: this.props.addDocument,
+ RemoveDocument: this.props.removeDocument,
+ ScreenToLocalTransform: this.getTransform,
+ isTopMost: false,
+ SelectOnLoad: document.Id == this._selectOnLoaded,
+ PanelWidth: document.Width,
+ PanelHeight: document.Height,
+ ContentScaling: this.noScaling,
+ ContainingCollectionView: this.props.CollectionView,
+ focus: this.focusDocument
+ }
+ }
@computed
get views() {
- var curPage = this.props.Document.GetNumber(KeyStore.CurPage, 1);
+ var curPage = this.props.Document.GetNumber(KeyStore.CurPage, -1);
const lvalue = this.props.Document.GetT<ListField<Document>>(this.props.fieldKey, ListField);
if (lvalue && lvalue != FieldWaiting) {
return lvalue.Data.map(doc => {
var page = doc.GetNumber(KeyStore.Page, 0);
return (page != curPage && page != 0) ? (null) :
- (<CollectionFreeFormDocumentView key={doc.Id} Document={doc}
- AddDocument={this.props.addDocument}
- RemoveDocument={this.props.removeDocument}
- ScreenToLocalTransform={this.getTransform}
- isTopMost={false}
- SelectOnLoad={doc.Id === this._selectOnLoaded}
- ContentScaling={this.noScaling}
- PanelWidth={doc.Width}
- PanelHeight={doc.Height}
- ContainingCollectionView={this.props.CollectionView}
- focus={this.focusDocument}
- />);
+ (<CollectionFreeFormDocumentView key={doc.Id} {...this.getDocumentViewProps(doc)} />);
})
}
return null;
@@ -348,63 +270,79 @@ export class CollectionFreeFormView extends CollectionViewBase {
@computed
get backgroundView() {
return !this.backgroundLayout ? (null) :
- (<JsxParser
- components={{ FormattedTextBox, ImageBox, CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, CollectionPDFView, WebBox, KeyValueBox, PDFBox }}
- bindings={this.props.bindings}
- jsx={this.backgroundLayout}
- showWarnings={true}
- onError={(test: any) => console.log(test)}
- />);
+ (<DocumentContentsView {...this.getDocumentViewProps(this.props.Document)}
+ layoutKey={KeyStore.BackgroundLayout} isSelected={() => false} select={() => { }} />);
}
@computed
get overlayView() {
return !this.overlayLayout ? (null) :
- (<JsxParser
- components={{ FormattedTextBox, ImageBox, CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, CollectionPDFView, WebBox, KeyValueBox, PDFBox }}
- bindings={this.props.bindings}
- jsx={this.overlayLayout}
- showWarnings={true}
- onError={(test: any) => console.log(test)}
- />);
+ (<DocumentContentsView {...this.getDocumentViewProps(this.props.Document)}
+ layoutKey={KeyStore.OverlayLayout} isSelected={() => false} select={() => { }} />);
}
getTransform = (): Transform => this.props.ScreenToLocalTransform().translate(-COLLECTION_BORDER_WIDTH, -COLLECTION_BORDER_WIDTH).translate(-this.centeringShiftX, -this.centeringShiftY).transform(this.getLocalTransform())
+ getMarqueeTransform = (): Transform => this.props.ScreenToLocalTransform().translate(-COLLECTION_BORDER_WIDTH, -COLLECTION_BORDER_WIDTH)
getLocalTransform = (): Transform => Transform.Identity.scale(1 / this.scale).translate(this.panX, this.panY);
noScaling = () => 1;
//when focus is lost, this will remove the preview cursor
@action
- onBlur = (e: React.FocusEvent<HTMLDivElement>): void => {
- this._previewCursorVisible = false;
+ onBlur = (): void => {
+ this.PreviewCursorVisible = false;
}
- render() {
- //determines whether preview text cursor should be visible (ie when user taps this collection it should)
- let cursor = null;
- if (this._previewCursorVisible) {
- //get local position and place cursor there!
- let [x, y] = this.getTransform().transformPoint(this._downX, this._downY);
- cursor = <div id="prevCursor" onKeyPress={this.onKeyDown} style={{ color: "black", position: "absolute", transformOrigin: "left top", transform: `translate(${x}px, ${y}px)` }}>I</div>
- }
+ private crosshairs?: HTMLCanvasElement;
+ drawCrosshairs = (backgroundColor: string) => {
+ if (this.crosshairs) {
+ let c = this.crosshairs;
+ let ctx = c.getContext('2d');
+ if (ctx) {
+ ctx.fillStyle = backgroundColor;
+ ctx.fillRect(0, 0, 20, 20);
+
+ ctx.fillStyle = "black";
+ ctx.lineWidth = 0.5;
+
+ ctx.beginPath();
+
+ ctx.moveTo(10, 0);
+ ctx.lineTo(10, 8);
+
+ ctx.moveTo(10, 20);
+ ctx.lineTo(10, 12);
- let p = this.getTransform().transformPoint(this._downX < this._lastX ? this._downX : this._lastX, this._downY < this._lastY ? this._downY : this._lastY);
- let v = this.getTransform().transformDirection(this._lastX - this._downX, this._lastY - this._downY);
- var marquee = this._marquee ? <div className="collectionfreeformview-marquee" style={{ transform: `translate(${p[0]}px, ${p[1]}px)`, width: `${Math.abs(v[0])}`, height: `${Math.abs(v[1])}` }}></div> : (null);
+ ctx.moveTo(0, 10);
+ ctx.lineTo(8, 10);
+ ctx.moveTo(20, 10);
+ ctx.lineTo(12, 10);
+
+ ctx.stroke();
+
+ // ctx.font = "10px Arial";
+ // ctx.fillText(CurrentUserUtils.email[0].toUpperCase(), 10, 10);
+ }
+ }
+ }
+
+ render() {
let [dx, dy] = [this.centeringShiftX, this.centeringShiftY];
const panx: number = -this.props.Document.GetNumber(KeyStore.PanX, 0);
const pany: number = -this.props.Document.GetNumber(KeyStore.PanY, 0);
+ // const panx: number = this.props.Document.GetNumber(KeyStore.PanX, 0) + this.centeringShiftX;
+ // const pany: number = this.props.Document.GetNumber(KeyStore.PanY, 0) + this.centeringShiftY;
+ // console.log("center:", this.getLocalTransform().transformPoint(this.centeringShiftX, this.centeringShiftY));
return (
<div className={`collectionfreeformview${this.isAnnotationOverlay ? "-overlay" : "-container"}`}
onPointerDown={this.onPointerDown}
- onKeyPress={this.onKeyDown}
+ onPointerMove={(e) => super.setCursorPosition(this.getTransform().transformPoint(e.clientX, e.clientY))}
onWheel={this.onPointerWheel}
onDrop={this.onDrop.bind(this)}
onDragOver={this.onDragOver}
onBlur={this.onBlur}
- style={{ borderWidth: `${COLLECTION_BORDER_WIDTH}px`, }}
+ style={{ borderWidth: `${COLLECTION_BORDER_WIDTH}px` }}// , zIndex: !this.props.isTopMost ? -1 : undefined }}
tabIndex={0}
ref={this.createDropTarget}>
<div className="collectionfreeformview"
@@ -412,12 +350,57 @@ export class CollectionFreeFormView extends CollectionViewBase {
ref={this._canvasRef}>
{this.backgroundView}
<InkingCanvas getScreenTransform={this.getTransform} Document={this.props.Document} />
- {cursor}
+ <PreviewCursor container={this} addLiveTextDocument={this.addLiveTextBox} getTransform={this.getTransform} />
{this.views}
- {marquee}
+ {super.getCursors().map(entry => {
+ if (entry.Data.length > 0) {
+ let id = entry.Data[0][0];
+ let email = entry.Data[0][1];
+ let point = entry.Data[1];
+ this.drawCrosshairs("#" + v5(id, v5.URL).substring(0, 6).toUpperCase() + "22")
+ return (
+ <div
+ key={id}
+ style={{
+ position: "absolute",
+ transform: `translate(${point[0] - 10}px, ${point[1] - 10}px)`,
+ zIndex: 10000,
+ transformOrigin: 'center center',
+ }}
+ >
+ <canvas
+ ref={(el) => { if (el) this.crosshairs = el }}
+ width={20}
+ height={20}
+ style={{
+ position: 'absolute',
+ width: "20px",
+ height: "20px",
+ opacity: 0.5,
+ borderRadius: "50%",
+ border: "2px solid black"
+ }}
+ />
+ <p
+ style={{
+ fontSize: 14,
+ color: "black",
+ // fontStyle: "italic",
+ marginLeft: -12,
+ marginTop: 4
+ }}
+ >{email[0].toUpperCase()}</p>
+ </div>
+ );
+ }
+ })}
</div>
+ <MarqueeView container={this} activeDocuments={this.getActiveDocuments} selectDocuments={this.selectDocuments}
+ addDocument={this.props.addDocument} removeDocument={this.props.removeDocument}
+ getMarqueeTransform={this.getMarqueeTransform} getTransform={this.getTransform} />
{this.overlayView}
+
</div>
);
}
-}
+} \ No newline at end of file
diff --git a/src/client/views/collections/CollectionPDFView.scss b/src/client/views/collections/CollectionPDFView.scss
new file mode 100644
index 000000000..0144625c1
--- /dev/null
+++ b/src/client/views/collections/CollectionPDFView.scss
@@ -0,0 +1,27 @@
+.collectionPdfView-buttonTray {
+ top : 25px;
+ left : 20px;
+ position: relative;
+ transform-origin: left top;
+ position: absolute;
+}
+.collectionPdfView-cont{
+ width: 100%;
+ height: 100%;
+ position: absolute;
+
+}
+.collectionPdfView-backward {
+ color : white;
+ top :0px;
+ left : 0px;
+ position: absolute;
+ background-color: rgba(50, 50, 50, 0.2);
+}
+.collectionPdfView-forward {
+ color : white;
+ top :0px;
+ left : 35px;
+ position: absolute;
+ background-color: rgba(50, 50, 50, 0.2);
+} \ No newline at end of file
diff --git a/src/client/views/collections/CollectionPDFView.tsx b/src/client/views/collections/CollectionPDFView.tsx
index f22c07060..e64b4c945 100644
--- a/src/client/views/collections/CollectionPDFView.tsx
+++ b/src/client/views/collections/CollectionPDFView.tsx
@@ -1,10 +1,11 @@
-import { action, computed } from "mobx";
+import { action, computed, observable } from "mobx";
import { observer } from "mobx-react";
import { Document } from "../../../fields/Document";
import { KeyStore } from "../../../fields/KeyStore";
import { ContextMenu } from "../ContextMenu";
import { CollectionView, CollectionViewType } from "./CollectionView";
import { CollectionViewProps } from "./CollectionViewBase";
+import "./CollectionPDFView.scss"
import React = require("react");
import { FieldId } from "../../../fields/Field";
@@ -18,25 +19,26 @@ export class CollectionPDFView extends React.Component<CollectionViewProps> {
isTopMost={isTopMost} SelectOnLoad={selectOnLoad} BackgroundView={BackgroundView} focus={focus}/>`;
}
- public SelectedDocs: FieldId[] = []
- @action onPageBack = () => this.curPage > 1 ? this.props.Document.SetNumber(KeyStore.CurPage, this.curPage - 1) : 0;
- @action onPageForward = () => this.curPage < this.numPages ? this.props.Document.SetNumber(KeyStore.CurPage, this.curPage + 1) : 0;
+ private get curPage() { return this.props.Document.GetNumber(KeyStore.CurPage, -1); }
+ private get numPages() { return this.props.Document.GetNumber(KeyStore.NumPages, 0); }
+ @action onPageBack = () => this.curPage > 1 ? this.props.Document.SetNumber(KeyStore.CurPage, this.curPage - 1) : -1;
+ @action onPageForward = () => this.curPage < this.numPages ? this.props.Document.SetNumber(KeyStore.CurPage, this.curPage + 1) : -1;
- @computed private get curPage() { return this.props.Document.GetNumber(KeyStore.CurPage, 0); }
- @computed private get numPages() { return this.props.Document.GetNumber(KeyStore.NumPages, 0); }
- @computed private get uIButtons() {
+ private get uIButtons() {
+ let scaling = Math.min(1.8, this.props.ScreenToLocalTransform().transformDirection(1, 1)[0]);
return (
- <div className="pdfBox-buttonTray" key="tray">
- <button className="pdfButton" onClick={this.onPageBack}>{"<"}</button>
- <button className="pdfButton" onClick={this.onPageForward}>{">"}</button>
+ <div className="collectionPdfView-buttonTray" key="tray" style={{ transform: `scale(${scaling}, ${scaling})` }}>
+ <button className="collectionPdfView-backward" onClick={this.onPageBack}>{"<"}</button>
+ <button className="collectionPdfView-forward" onClick={this.onPageForward}>{">"}</button>
</div>);
}
// "inherited" CollectionView API starts here...
-
+ @observable
+ public SelectedDocs: FieldId[] = []
public active: () => boolean = () => CollectionView.Active(this);
- addDocument = (doc: Document): void => { CollectionView.AddDocument(this.props, doc); }
+ addDocument = (doc: Document, allowDuplicates: boolean): void => { CollectionView.AddDocument(this.props, doc, allowDuplicates); }
removeDocument = (doc: Document): boolean => { return CollectionView.RemoveDocument(this.props, doc); }
specificContextMenu = (e: React.MouseEvent): void => {
@@ -49,7 +51,7 @@ export class CollectionPDFView extends React.Component<CollectionViewProps> {
get subView(): any { return CollectionView.SubView(this); }
render() {
- return (<div className="collectionView-cont" onContextMenu={this.specificContextMenu}>
+ return (<div className="collectionPdfView-cont" onContextMenu={this.specificContextMenu}>
{this.subView}
{this.props.isSelected() ? this.uIButtons : (null)}
</div>)
diff --git a/src/client/views/collections/CollectionSchemaView.scss b/src/client/views/collections/CollectionSchemaView.scss
index ab906cf4b..d16f0edf9 100644
--- a/src/client/views/collections/CollectionSchemaView.scss
+++ b/src/client/views/collections/CollectionSchemaView.scss
@@ -60,16 +60,25 @@
padding:0px;
}
+@import "../global_variables";
.collectionSchemaView-container {
- border-style: solid;
+ border: 1px solid $intermediate-color;
+ border-radius: $border-radius;
box-sizing: border-box;
position: absolute;
width: 100%;
height: 100%;
+ overflow: hidden;
+ .collectionSchemaView-content {
+ position: absolute;
+ height: 100%;
+ width: 100%;
+ overflow: auto;
+ }
.collectionSchemaView-previewRegion {
- position: relative;
- background: black;
- float: left;
+ position: relative;
+ background: $light-color;
+ float: left;
height: 100%;
}
.collectionSchemaView-previewHandle {
@@ -85,30 +94,57 @@
position: relative;
background: black;
float: left;
+ height: 37px;
+ width: 20px;
+ z-index: 20;
+ right: 0;
+ top: 0;
+ background: $main-accent;
+ }
+ .collectionSchemaView-columnsHandle {
+ position: absolute;
+ height: 37px;
+ width: 20px;
+ z-index: 20;
+ left: 0;
+ bottom: 0;
+ background: $main-accent;
+ }
+ .collectionSchemaView-colDividerDragger {
+ position: relative;
+ box-sizing: border-box;
+ border-top: 1px solid $intermediate-color;
+ border-bottom: 1px solid $intermediate-color;
+ float: top;
+ width: 100%;
+ }
+ .collectionSchemaView-dividerDragger {
+ position: relative;
+ box-sizing: border-box;
+ border-left: 1px solid $intermediate-color;
+ border-right: 1px solid $intermediate-color;
+ float: left;
height: 100%;
}
.collectionSchemaView-tableContainer {
position: relative;
float: left;
- height: 100%;
+ height: 100%;
}
-
.ReactTable {
- position: absolute;
- // display: inline-block;
- // overflow: auto;
+ // position: absolute; // display: inline-block;
+ // overflow: auto;
width: 100%;
height: 100%;
- background: white;
+ background: $light-color;
box-sizing: border-box;
+ border: none !important;
.rt-table {
overflow-y: auto;
overflow-x: auto;
height: 100%;
-
display: -webkit-inline-box;
- direction: ltr;
- // direction:rtl;
+ direction: ltr; // direction:rtl;
// display:block;
}
.rt-tbody {
@@ -120,37 +156,47 @@
max-height: 44px;
}
.rt-td {
- border-width: 1;
- border-right-color: #aaa;
+ border-width: 1px;
+ border-right-color: $intermediate-color;
.imageBox-cont {
- position:relative;
- max-height:100%;
+ position: relative;
+ max-height: 100%;
}
.imageBox-cont img {
object-fit: contain;
max-width: 100%;
- height: 100%
+ height: 100%;
+ }
+ .videobox-cont {
+ object-fit: contain;
+ width: auto;
+ height: 100%;
}
- }
- .rt-tr-group {
- border-width: 1;
- border-bottom-color: #aaa
}
}
.ReactTable .rt-thead.-header {
- background:grey;
- }
- .ReactTable .rt-th, .ReactTable .rt-td {
+ background: $intermediate-color;
+ color: $light-color;
+ text-transform: uppercase;
+ letter-spacing: 2px;
+ font-size: 12px;
+ height: 30px;
+ padding-top: 4px;
+ }
+ .ReactTable .rt-th,
+ .ReactTable .rt-td {
max-height: 44;
padding: 3px 7px;
+ font-size: 13px;
+ text-align: center;
}
.ReactTable .rt-tbody .rt-tr-group:last-child {
- border-bottom: grey;
+ border-bottom: $intermediate-color;
border-bottom-style: solid;
border-bottom-width: 1;
}
.documentView-node:first-child {
- background: grey;
+ background: $light-color;
.imageBox-cont img {
object-fit: contain;
}
@@ -264,4 +310,12 @@
-webkit-flex: 1;
-ms-flex: 1;
flex: 1;
+}
+
+.-even {
+ background: $light-color !important;
+}
+
+.-odd {
+ background: $light-color-secondary !important;
} \ No newline at end of file
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 7c87ef744..2598dd3a9 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -1,11 +1,16 @@
+<<<<<<< HEAD
import React = require("react");
import { action, observable } from "mobx";
+=======
+import React = require("react")
+import { action, observable, ObservableMap, computed } from "mobx";
+>>>>>>> f70ad315167b714f11f7d68f35a46abe9e525a4d
import { observer } from "mobx-react";
import Measure from "react-measure";
import ReactTable, { CellInfo, ComponentPropsGetterR, ReactTableDefaults } from "react-table";
import "react-table/react-table.css";
import { Document } from "../../../fields/Document";
-import { Field } from "../../../fields/Field";
+import { Field, Opt } from "../../../fields/Field";
import { KeyStore } from "../../../fields/KeyStore";
import { CompileScript, ToField } from "../../util/Scripting";
import { Transform } from "../../util/Transform";
@@ -14,9 +19,10 @@ import { EditableView } from "../EditableView";
import { DocumentView } from "../nodes/DocumentView";
import { FieldView, FieldViewProps } from "../nodes/FieldView";
import "./CollectionSchemaView.scss";
-import { COLLECTION_BORDER_WIDTH } from "./CollectionView";
+import { COLLECTION_BORDER_WIDTH, CollectionView } from "./CollectionView";
import { CollectionViewBase } from "./CollectionViewBase";
import { setupDrag } from "../../util/DragManager";
+<<<<<<< HEAD
import '../DocumentDecorations.scss';
import { Flyout, anchorPoints } from "../DocumentDecorations";
import { DropdownButton, Dropdown } from 'react-bootstrap';
@@ -27,21 +33,53 @@ import { Key } from "../../../fields/Key";
import { Server } from "../../Server";
//import { MenuItem } from 'react-bootstrap';
+=======
+import { Key } from "./../../../fields/Key";
+import { Server } from "../../Server";
+import { ListField } from "../../../fields/ListField";
+
+>>>>>>> f70ad315167b714f11f7d68f35a46abe9e525a4d
// bcz: need to add drag and drop of rows and columns. This seems like it might work for rows: https://codesandbox.io/s/l94mn1q657
@observer
+class KeyToggle extends React.Component<{ keyId: string, checked: boolean, toggle: (key: Key) => void }> {
+ @observable
+ key: Key | undefined;
+
+ componentWillReceiveProps() {
+ Server.GetField(this.props.keyId, action((field: Opt<Field>) => {
+ if (field instanceof Key) {
+ this.key = field;
+ }
+ }))
+ }
+
+ render() {
+ if (this.key) {
+ return (<div key={this.key.Id}>
+ <input type="checkbox" checked={this.props.checked} onChange={() => this.key && this.props.toggle(this.key)} />{this.key.Name}
+ </div>)
+ } else {
+ return <div></div>
+ }
+ }
+}
+
+@observer
export class CollectionSchemaView extends CollectionViewBase {
private _mainCont = React.createRef<HTMLDivElement>();
- private DIVIDER_WIDTH = 5;
+ private DIVIDER_WIDTH = 4;
+ @observable _columns: Array<Key> = [KeyStore.Title, KeyStore.Data, KeyStore.Author];
@observable _contentScaling = 1; // used to transfer the dimensions of the content pane in the DOM to the ContentScaling prop of the DocumentView
@observable _dividerX = 0;
@observable _panelWidth = 0;
@observable _panelHeight = 0;
@observable _selectedIndex = 0;
- @observable _splitPercentage: number = 50;
+ @observable _columnsPercentage = 0;
+ @computed get splitPercentage() { return this.props.Document.GetNumber(KeyStore.SchemaSplitPercentage, 0); }
renderCell = (rowProps: CellInfo) => {
let props: FieldViewProps = {
@@ -57,10 +95,12 @@ export class CollectionSchemaView extends CollectionViewBase {
<FieldView {...props} />
)
let reference = React.createRef<HTMLDivElement>();
- let onItemDown = setupDrag(reference, () => props.doc);
+ let onItemDown = setupDrag(reference, () => props.doc, (containingCollection: CollectionView) => this.props.removeDocument(props.doc));
return (
- <div onPointerDown={onItemDown} key={props.doc.Id} ref={reference}>
- <EditableView contents={contents}
+ <div className="collectionSchemaView-cellContents" onPointerDown={onItemDown} style={{ height: "36px" }} key={props.doc.Id} ref={reference}>
+ <EditableView
+ display={"inline"}
+ contents={contents}
height={36} GetValue={() => {
let field = props.doc.Get(props.fieldKey);
if (field && field instanceof Field) {
@@ -69,7 +109,7 @@ export class CollectionSchemaView extends CollectionViewBase {
return field || "";
}}
SetValue={(value: string) => {
- let script = CompileScript(value, undefined, true);
+ let script = CompileScript(value);
if (!script.compiled) {
return false;
}
@@ -99,7 +139,8 @@ export class CollectionSchemaView extends CollectionViewBase {
return {
onClick: action((e: React.MouseEvent, handleOriginal: Function) => {
that._selectedIndex = rowInfo.index;
- this._splitPercentage += 0.05; // bcz - ugh - needed to force Measure to do its thing and call onResize
+ // bcz - ugh - needed to force Measure to do its thing and call onResize
+ this.props.Document.SetNumber(KeyStore.SchemaSplitPercentage, this.splitPercentage - 0.05)
if (handleOriginal) {
handleOriginal()
@@ -112,27 +153,83 @@ export class CollectionSchemaView extends CollectionViewBase {
};
}
+ get columns() {
+ return this.props.Document.GetList<Key>(KeyStore.ColumnsKey, []);
+ }
+
+ @action
+ toggleKey = (key: Key) => {
+ this.props.Document.GetOrCreateAsync<ListField<Key>>(KeyStore.ColumnsKey, ListField,
+ (field) => {
+ const index = field.Data.indexOf(key);
+ if (index === -1) {
+ this.columns.push(key);
+ } else {
+ this.columns.splice(index, 1);
+ }
+
+ })
+ }
+
+ @observable keys: Key[] = [];
+
+ findAllDocumentKeys = (): { [id: string]: boolean } => {
+ const docs = this.props.Document.GetList<Document>(this.props.fieldKey, []);
+ let keys: { [id: string]: boolean } = {}
+ docs.forEach(doc => {
+ let protos = doc.GetAllPrototypes();
+ for (const proto of protos) {
+ proto._proxies.forEach((val: any, key: string) => {
+ keys[key] = false
+ })
+ }
+ })
+ this.columns.forEach(key => {
+ keys[key.Id] = true;
+ })
+ return keys;
+ }
+
_startSplitPercent = 0;
@action
onDividerMove = (e: PointerEvent): void => {
let nativeWidth = this._mainCont.current!.getBoundingClientRect();
- this._splitPercentage = Math.round((e.clientX - nativeWidth.left) / nativeWidth.width * 100);
+ this.props.Document.SetNumber(KeyStore.SchemaSplitPercentage, 100 - Math.round((e.clientX - nativeWidth.left) / nativeWidth.width * 100));
}
@action
onDividerUp = (e: PointerEvent): void => {
document.removeEventListener("pointermove", this.onDividerMove);
document.removeEventListener('pointerup', this.onDividerUp);
- if (this._startSplitPercent == this._splitPercentage) {
- this._splitPercentage = this._splitPercentage == 1 ? 66 : 100;
+ if (this._startSplitPercent == this.splitPercentage) {
+ this.props.Document.SetNumber(KeyStore.SchemaSplitPercentage, this.splitPercentage == 0 ? 33 : 0);
}
}
onDividerDown = (e: React.PointerEvent) => {
- this._startSplitPercent = this._splitPercentage;
+ this._startSplitPercent = this.splitPercentage;
e.stopPropagation();
e.preventDefault();
document.addEventListener("pointermove", this.onDividerMove);
document.addEventListener('pointerup', this.onDividerUp);
}
+
+
+ @action
+ onColDividerMove = (e: PointerEvent): void => {
+ let nativeWidth = this._mainCont.current!.getBoundingClientRect();
+ this._columnsPercentage = 100 - (e.clientY - nativeWidth.top) / nativeWidth.height * 100;
+ }
+ @action
+ onColDividerUp = (e: PointerEvent): void => {
+ document.removeEventListener("pointermove", this.onColDividerMove);
+ document.removeEventListener('pointerup', this.onColDividerUp);
+ }
+ onColDividerDown = (e: React.PointerEvent) => {
+ e.stopPropagation();
+ e.preventDefault();
+ document.addEventListener("pointermove", this.onColDividerMove);
+ document.addEventListener('pointerup', this.onColDividerUp);
+ }
+
@action
onExpanderMove = (e: PointerEvent): void => {
e.stopPropagation();
@@ -144,12 +241,12 @@ export class CollectionSchemaView extends CollectionViewBase {
e.preventDefault();
document.removeEventListener("pointermove", this.onExpanderMove);
document.removeEventListener('pointerup', this.onExpanderUp);
- if (this._startSplitPercent == this._splitPercentage) {
- this._splitPercentage = this._splitPercentage == 100 ? 66 : 100;
+ if (this._startSplitPercent == this.splitPercentage) {
+ this.props.Document.SetNumber(KeyStore.SchemaSplitPercentage, this.splitPercentage == 0 ? 33 : 0);
}
}
onExpanderDown = (e: React.PointerEvent) => {
- this._startSplitPercent = this._splitPercentage;
+ this._startSplitPercent = this.splitPercentage;
e.stopPropagation();
e.preventDefault();
document.addEventListener("pointermove", this.onExpanderMove);
@@ -162,15 +259,36 @@ export class CollectionSchemaView extends CollectionViewBase {
// e.preventDefault();
// } else
{
- if (e.buttons === 1) {
- if (this.props.isSelected()) {
- e.stopPropagation();
- }
- }
+ // if (e.buttons === 1) {
+ // if (this.props.isSelected()) {
+ // e.stopPropagation();
+ // }
+ // }
}
}
@action
+ onColumnsMove = (e: PointerEvent): void => {
+ e.stopPropagation();
+ e.preventDefault();
+ }
+ @action
+ onColumnsUp = (e: PointerEvent): void => {
+ e.stopPropagation();
+ e.preventDefault();
+ document.removeEventListener("pointermove", this.onColumnsMove);
+ document.removeEventListener('pointerup', this.onColumnsUp);
+ this._columnsPercentage = this._columnsPercentage ? 0 : 50;
+ }
+ onColumnsDown = (e: React.PointerEvent) => {
+ this._startSplitPercent = this.splitPercentage;
+ e.stopPropagation();
+ e.preventDefault();
+ document.addEventListener("pointermove", this.onColumnsMove);
+ document.addEventListener('pointerup', this.onColumnsUp);
+ }
+
+ @action
setScaling = (r: any) => {
const children = this.props.Document.GetList<Document>(this.props.fieldKey, []);
const selected = children.length > this._selectedIndex ? children[this._selectedIndex] : undefined;
@@ -202,9 +320,10 @@ export class CollectionSchemaView extends CollectionViewBase {
}
render() {
- const columns = this.props.Document.GetList(KeyStore.ColumnsKey, [KeyStore.Title, KeyStore.Data, KeyStore.Author])
+ const columns = this.columns;
const children = this.props.Document.GetList<Document>(this.props.fieldKey, []);
const selected = children.length > this._selectedIndex ? children[this._selectedIndex] : undefined;
+ const allKeys = this.findAllDocumentKeys();
let content = this._selectedIndex == -1 || !selected ? (null) : (
<Measure onResize={this.setScaling}>
{({ measureRef }) =>
@@ -226,8 +345,11 @@ export class CollectionSchemaView extends CollectionViewBase {
)
let previewHandle = !this.props.active() ? (null) : (
<div className="collectionSchemaView-previewHandle" onPointerDown={this.onExpanderDown} />);
- let dividerDragger = this._splitPercentage == 100 ? (null) :
+ let columnsHandle = !this.props.active() ? (null) : (
+ <div className="collectionSchemaView-columnsHandle" onPointerDown={this.onColumnsDown} />);
+ let dividerDragger = this.splitPercentage == 0 ? (null) :
<div className="collectionSchemaView-dividerDragger" onPointerDown={this.onDividerDown} style={{ width: `${this.DIVIDER_WIDTH}px` }} />
+<<<<<<< HEAD
//get the union of all childrens' keys
let addFields: { id: string, name: string }[] = [];
@@ -268,6 +390,10 @@ export class CollectionSchemaView extends CollectionViewBase {
}
+=======
+ let colDividerDragger = this._columnsPercentage == 0 ? (null) :
+ <div className="collectionSchemaView-colDividerDragger" onPointerDown={this.onColDividerDown} style={{ height: `${this.DIVIDER_WIDTH}px` }} />
+>>>>>>> f70ad315167b714f11f7d68f35a46abe9e525a4d
return (
<div className="collectionSchemaView-container" onPointerDown={this.onPointerDown} ref={this._mainCont} style={{ borderWidth: `${COLLECTION_BORDER_WIDTH}px` }} >
@@ -277,33 +403,53 @@ export class CollectionSchemaView extends CollectionViewBase {
this._panelHeight = r.entry.height;
})}>
{({ measureRef }) =>
- <div ref={measureRef} className="collectionSchemaView-tableContainer" style={{ width: `${this._splitPercentage}%` }}>
- <ReactTable
- data={children}
- pageSize={children.length}
- page={0}
- showPagination={false}
- columns={columns.map(col => ({
- Header: col.Name,
- accessor: (doc: Document) => [doc, col],
- id: col.Id
- }))}
- column={{
- ...ReactTableDefaults.column,
- Cell: this.renderCell,
-
- }}
- getTrProps={this.getTrProps}
- />
+ <div ref={measureRef} className="collectionSchemaView-tableContainer"
+ style={{ width: `calc(100% - ${this.splitPercentage}%)` }}>
+ <div className="collectionSchemaView-reactContainer" style={{ height: `calc(100% - ${this._columnsPercentage}%)` }}>
+ <ReactTable
+ data={children}
+ pageSize={children.length}
+ page={0}
+ showPagination={false}
+ columns={columns.map(col => ({
+ Header: col.Name,
+ accessor: (doc: Document) => [doc, col],
+ id: col.Id
+ }))}
+ column={{
+ ...ReactTableDefaults.column,
+ Cell: this.renderCell,
+
+ }}
+ getTrProps={this.getTrProps}
+ />
+ </div>
+ {colDividerDragger}
+ <div className="collectionSchemaView-addColumn" style={{ height: `${this._columnsPercentage}%` }} >
+ {/* <input type="checkbox" id="addColumn-toggle" />
+ <label htmlFor="addColumn-toggle" title="Add Column"><p>+</p></label> */}
+
+ <div className="addColumn-options">
+ <ul style={{ overflow: "scroll" }}>
+ {Array.from(Object.keys(allKeys)).map(item => {
+ return (<KeyToggle checked={allKeys[item]} key={item} keyId={item} toggle={this.toggleKey} />)
+ })}
+ </ul>
+ </div>
+ </div>
</div>
}
</Measure>
{dividerDragger}
- <div className="collectionSchemaView-previewRegion" style={{ width: `calc(${100 - this._splitPercentage}% - ${this.DIVIDER_WIDTH}px)` }}>
+ <div className="collectionSchemaView-previewRegion" style={{ width: `calc(${this.props.Document.GetNumber(KeyStore.SchemaSplitPercentage, 0)}% - ${this.DIVIDER_WIDTH}px)` }}>
{content}
</div>
{previewHandle}
+<<<<<<< HEAD
{optionsMenu}
+=======
+ {columnsHandle}
+>>>>>>> f70ad315167b714f11f7d68f35a46abe9e525a4d
</div>
</div >
diff --git a/src/client/views/collections/CollectionTreeView.scss b/src/client/views/collections/CollectionTreeView.scss
index f8d580a7b..fa0f1c761 100644
--- a/src/client/views/collections/CollectionTreeView.scss
+++ b/src/client/views/collections/CollectionTreeView.scss
@@ -1,16 +1,25 @@
+@import "../global_variables";
#body {
padding: 20px;
- background: #bbbbbb;
+ background: $light-color-secondary;
+ font-size: 13px;
+ overflow: scroll;
}
ul {
list-style: none;
+ padding-left: 20px;
}
li {
margin: 5px 0;
}
+.collection-child {
+ margin-top: 10px;
+ margin-bottom: 10px;
+}
+
.no-indent {
padding-left: 0;
}
@@ -18,10 +27,17 @@ li {
.bullet {
width: 1.5em;
display: inline-block;
+ color: $intermediate-color;
+}
+
+.coll-title {
+ font-size: 24px;
+ margin-bottom: 20px;
}
.collectionTreeView-dropTarget {
- border-style: solid;
+ border: 0px solid transparent;
+ border-radius: $border-radius;
box-sizing: border-box;
height: 100%;
}
@@ -30,8 +46,16 @@ li {
display: inline-table;
}
+.docContainer:hover {
+ .delete-button {
+ display: inline;
+ }
+}
+
.delete-button {
- color: #999999;
+ color: $intermediate-color;
float: right;
- margin-left: 1em;
+ margin-left: 15px;
+ margin-top: 3px;
+ display: none;
} \ No newline at end of file
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 8b06d9ac4..f9da759fd 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -12,6 +12,10 @@ import { setupDrag } from "../../util/DragManager";
import { FieldWaiting } from "../../../fields/Field";
import { COLLECTION_BORDER_WIDTH } from "./CollectionView";
+import { library } from '@fortawesome/fontawesome-svg-core';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { faTrashAlt, faCaretRight, faCaretDown } from '@fortawesome/free-solid-svg-icons';
+
export interface TreeViewProps {
document: Document;
deleteDoc: (doc: Document) => void;
@@ -23,6 +27,10 @@ export enum BulletType {
List
}
+library.add(faTrashAlt);
+library.add(faCaretDown);
+library.add(faCaretRight);
+
@observer
/**
* Component that takes in a document prop and a boolean whether it's collapsed or not.
@@ -50,11 +58,11 @@ class TreeView extends React.Component<TreeViewProps> {
switch (type) {
case BulletType.Collapsed:
- return <div className="bullet" onClick={onClicked}>&#9654;</div>
+ return <div className="bullet" onClick={onClicked}><FontAwesomeIcon icon="caret-right" /></div>
case BulletType.Collapsible:
- return <div className="bullet" onClick={onClicked}>&#9660;</div>
+ return <div className="bullet" onClick={onClicked}><FontAwesomeIcon icon="caret-down" /></div>
case BulletType.List:
- return <div className="bullet">&mdash;</div>
+ return <div className="bullet"></div>
}
}
@@ -69,7 +77,9 @@ class TreeView extends React.Component<TreeViewProps> {
return <div key={this.props.document.Id}></div>;
}
- return <div className="docContainer"> <EditableView contents={title.Data}
+ return <div className="docContainer"> <EditableView
+ display={"inline"}
+ contents={title.Data}
height={36} GetValue={() => {
let title = this.props.document.GetT<TextField>(KeyStore.Title, TextField);
if (title && title !== "<Waiting>")
@@ -79,7 +89,7 @@ class TreeView extends React.Component<TreeViewProps> {
this.props.document.SetData(KeyStore.Title, value, TextField);
return true;
}} />
- <div className="delete-button" onClick={this.delete}>x</div>
+ <div className="delete-button" onClick={this.delete}><FontAwesomeIcon icon="trash-alt" size="xs" /></div>
</div >
}
@@ -101,7 +111,7 @@ class TreeView extends React.Component<TreeViewProps> {
<TreeView document={value} deleteDoc={this.remove} />)
)
subView =
- <li key={this.props.document.Id} >
+ <li className="collection-child" key={this.props.document.Id} >
{this.renderBullet(BulletType.Collapsible)}
{titleElement}
<ul key={this.props.document.Id}>
@@ -109,7 +119,7 @@ class TreeView extends React.Component<TreeViewProps> {
</ul>
</li>
} else {
- subView = <li key={this.props.document.Id}>
+ subView = <li className="collection-child" key={this.props.document.Id}>
{this.renderBullet(BulletType.Collapsed)}
{titleElement}
</li>
@@ -157,15 +167,17 @@ export class CollectionTreeView extends CollectionViewBase {
return (
<div id="body" className="collectionTreeView-dropTarget" onDrop={(e: React.DragEvent) => this.onDrop(e, {})} ref={this.createDropTarget} style={{ borderWidth: `${COLLECTION_BORDER_WIDTH}px` }}>
- <h3>
+ <div className="coll-title">
<EditableView contents={titleStr}
+ display={"inline"}
height={72} GetValue={() => {
return this.props.Document.Title;
}} SetValue={(value: string) => {
this.props.Document.SetData(KeyStore.Title, value, TextField);
return true;
}} />
- </h3>
+ </div>
+ <hr />
<ul className="no-indent">
{childrenElement}
</ul>
diff --git a/src/client/views/collections/CollectionVideoView.scss b/src/client/views/collections/CollectionVideoView.scss
new file mode 100644
index 000000000..cbb981b13
--- /dev/null
+++ b/src/client/views/collections/CollectionVideoView.scss
@@ -0,0 +1,40 @@
+
+.collectionVideoView-cont{
+ width: 100%;
+ height: 100%;
+ position: absolute;
+
+}
+.collectionVideoView-time{
+ color : white;
+ top :25px;
+ left : 25px;
+ position: absolute;
+ background-color: rgba(50, 50, 50, 0.2);
+ transform-origin: left top;
+}
+.collectionVideoView-play {
+ width: 25px;
+ height: 20px;
+ bottom: 25px;
+ left : 25px;
+ position: absolute;
+ color : white;
+ background-color: rgba(50, 50, 50, 0.2);
+ border-radius: 4px;
+ text-align: center;
+ transform-origin: left bottom;
+}
+.collectionVideoView-full {
+ width: 25px;
+ height: 20px;
+ bottom: 25px;
+ right : 25px;
+ position: absolute;
+ color : white;
+ background-color: rgba(50, 50, 50, 0.2);
+ border-radius: 4px;
+ text-align: center;
+ transform-origin: right bottom;
+
+} \ No newline at end of file
diff --git a/src/client/views/collections/CollectionVideoView.tsx b/src/client/views/collections/CollectionVideoView.tsx
new file mode 100644
index 000000000..05f759967
--- /dev/null
+++ b/src/client/views/collections/CollectionVideoView.tsx
@@ -0,0 +1,118 @@
+import { action, computed, observable } from "mobx";
+import { observer } from "mobx-react";
+import { Document } from "../../../fields/Document";
+import { KeyStore } from "../../../fields/KeyStore";
+import { ContextMenu } from "../ContextMenu";
+import { CollectionView, CollectionViewType } from "./CollectionView";
+import { CollectionViewProps } from "./CollectionViewBase";
+import React = require("react");
+import { FieldId } from "../../../fields/Field";
+import "./CollectionVideoView.scss"
+
+
+@observer
+export class CollectionVideoView extends React.Component<CollectionViewProps> {
+
+ public static LayoutString(fieldKey: string = "DataKey") {
+ return `<${CollectionVideoView.name} Document={Document}
+ ScreenToLocalTransform={ScreenToLocalTransform} fieldKey={${fieldKey}} panelWidth={PanelWidth} panelHeight={PanelHeight} isSelected={isSelected} select={select} bindings={bindings}
+ isTopMost={isTopMost} SelectOnLoad={selectOnLoad} BackgroundView={BackgroundView} focus={focus}/>`;
+ }
+
+ private _mainCont = React.createRef<HTMLDivElement>();
+
+ private get uIButtons() {
+ let scaling = Math.min(1.8, this.props.ScreenToLocalTransform().transformDirection(1, 1)[0]);
+ return ([
+ <div className="collectionVideoView-time" key="time" onPointerDown={this.onResetDown} style={{ transform: `scale(${scaling}, ${scaling})` }}>
+ <span>{"" + Math.round(this.ctime)}</span>
+ <span style={{ fontSize: 8 }}>{" " + Math.round((this.ctime - Math.trunc(this.ctime)) * 100)}</span>
+ </div>,
+ <div className="collectionVideoView-play" key="play" onPointerDown={this.onPlayDown} style={{ transform: `scale(${scaling}, ${scaling})` }}>
+ {this.playing ? "\"" : ">"}
+ </div>,
+ <div className="collectionVideoView-full" key="full" onPointerDown={this.onFullDown} style={{ transform: `scale(${scaling}, ${scaling})` }}>
+ F
+ </div>
+ ]);
+ }
+
+
+ // "inherited" CollectionView API starts here...
+
+ @observable
+ public SelectedDocs: FieldId[] = []
+ public active: () => boolean = () => CollectionView.Active(this);
+
+ addDocument = (doc: Document, allowDuplicates: boolean): void => { CollectionView.AddDocument(this.props, doc, allowDuplicates); }
+ removeDocument = (doc: Document): boolean => { return CollectionView.RemoveDocument(this.props, doc); }
+
+ specificContextMenu = (e: React.MouseEvent): void => {
+ if (!e.isPropagationStopped() && this.props.Document.Id != "mainDoc") { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7
+ ContextMenu.Instance.addItem({ description: "VideoOptions", event: () => { } });
+ }
+ }
+
+ get collectionViewType(): CollectionViewType { return CollectionViewType.Freeform; }
+ get subView(): any { return CollectionView.SubView(this); }
+
+ componentDidMount() {
+ this.updateTimecode();
+ }
+
+ get player(): HTMLVideoElement | undefined {
+ return this._mainCont.current ? this._mainCont.current.getElementsByTagName("video")[0] : undefined;
+ }
+
+ @action
+ updateTimecode = () => {
+ if (this.player) {
+ this.ctime = this.player.currentTime;
+ this.props.Document.SetNumber(KeyStore.CurPage, Math.round(this.ctime));
+ }
+ setTimeout(() => this.updateTimecode(), 100)
+ }
+
+
+ @observable
+ ctime: number = 0
+ @observable
+ playing: boolean = false;
+
+ @action
+ onPlayDown = () => {
+ if (this.player) {
+ if (this.player.paused) {
+ this.player.play();
+ this.playing = true;
+ } else {
+ this.player.pause();
+ this.playing = false;
+ }
+ }
+ }
+ @action
+ onFullDown = (e: React.PointerEvent) => {
+ if (this.player) {
+ this.player.requestFullscreen();
+ e.stopPropagation();
+ e.preventDefault();
+ }
+ }
+
+ @action
+ onResetDown = () => {
+ if (this.player) {
+ this.player.pause();
+ this.player.currentTime = 0;
+ }
+
+ }
+
+ render() {
+ return (<div className="collectionVideoView-cont" ref={this._mainCont} onContextMenu={this.specificContextMenu}>
+ {this.subView}
+ {this.props.isSelected() ? this.uIButtons : (null)}
+ </div>)
+ }
+} \ No newline at end of file
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 17a0fbd23..303099c16 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -22,22 +22,21 @@ export enum CollectionViewType {
Tree
}
-export const COLLECTION_BORDER_WIDTH = 2;
+export const COLLECTION_BORDER_WIDTH = 1;
@observer
export class CollectionView extends React.Component<CollectionViewProps> {
- @observable
- public SelectedDocs: FieldId[] = [];
-
public static LayoutString(fieldKey: string = "DataKey") {
return `<${CollectionView.name} Document={Document}
ScreenToLocalTransform={ScreenToLocalTransform} fieldKey={${fieldKey}} panelWidth={PanelWidth} panelHeight={PanelHeight} isSelected={isSelected} select={select} bindings={bindings}
isTopMost={isTopMost} SelectOnLoad={selectOnLoad} BackgroundView={BackgroundView} focus={focus}/>`;
}
+ @observable
+ public SelectedDocs: FieldId[] = [];
public active: () => boolean = () => CollectionView.Active(this);
- addDocument = (doc: Document): void => { CollectionView.AddDocument(this.props, doc); }
+ addDocument = (doc: Document, allowDuplicates: boolean): void => { CollectionView.AddDocument(this.props, doc, allowDuplicates); }
removeDocument = (doc: Document): boolean => { return CollectionView.RemoveDocument(this.props, doc); }
get subView() { return CollectionView.SubView(this); }
@@ -49,14 +48,15 @@ export class CollectionView extends React.Component<CollectionViewProps> {
}
@action
- public static AddDocument(props: CollectionViewProps, doc: Document) {
- doc.SetNumber(KeyStore.Page, props.Document.GetNumber(KeyStore.CurPage, 0));
+ public static AddDocument(props: CollectionViewProps, doc: Document, allowDuplicates: boolean) {
+ doc.SetNumber(KeyStore.Page, props.Document.GetNumber(KeyStore.CurPage, -1));
if (props.Document.Get(props.fieldKey) instanceof Field) {
//TODO This won't create the field if it doesn't already exist
const value = props.Document.GetData(props.fieldKey, ListField, new Array<Document>())
- value.push(doc);
+ if (!value.some(v => v.Id == doc.Id) || allowDuplicates)
+ value.push(doc);
} else {
- props.Document.SetData(props.fieldKey, [doc], ListField);
+ props.Document.SetOnPrototype(props.fieldKey, new ListField([doc]));
}
}
diff --git a/src/client/views/collections/CollectionViewBase.tsx b/src/client/views/collections/CollectionViewBase.tsx
index b581d3060..7175e2846 100644
--- a/src/client/views/collections/CollectionViewBase.tsx
+++ b/src/client/views/collections/CollectionViewBase.tsx
@@ -3,14 +3,18 @@ import { Document } from "../../../fields/Document";
import { ListField } from "../../../fields/ListField";
import React = require("react");
import { KeyStore } from "../../../fields/KeyStore";
-import { FieldWaiting } from "../../../fields/Field";
+import { FieldWaiting, Opt } from "../../../fields/Field";
import { undoBatch } from "../../util/UndoManager";
import { DragManager } from "../../util/DragManager";
-import { DocumentView } from "../nodes/DocumentView";
import { Documents, DocumentOptions } from "../../documents/Documents";
import { Key } from "../../../fields/Key";
import { Transform } from "../../util/Transform";
import { CollectionView } from "./CollectionView";
+import { RouteStore } from "../../../server/RouteStore";
+import { TupleField } from "../../../fields/TupleField";
+import { CurrentUserUtils } from "../../../server/authentication/models/current_user_utils";
+import { NumberField } from "../../../fields/NumberField";
+import { DocumentManager } from "../../util/DocumentManager";
export interface CollectionViewProps {
fieldKey: Key;
@@ -24,13 +28,16 @@ export interface CollectionViewProps {
panelHeight: () => number;
focus: (doc: Document) => void;
}
+
export interface SubCollectionViewProps extends CollectionViewProps {
active: () => boolean;
- addDocument: (doc: Document) => void;
+ addDocument: (doc: Document, allowDuplicates: boolean) => void;
removeDocument: (doc: Document) => boolean;
CollectionView: CollectionView;
}
+export type CursorEntry = TupleField<[string, string], [number, number]>;
+
export class CollectionViewBase extends React.Component<SubCollectionViewProps> {
private dropDisposer?: DragManager.DragDropDisposer;
protected createDropTarget = (ele: HTMLDivElement) => {
@@ -42,52 +49,83 @@ export class CollectionViewBase extends React.Component<SubCollectionViewProps>
}
}
+ @action
+ protected setCursorPosition(position: [number, number]) {
+ let ind;
+ let doc = this.props.Document;
+ let id = CurrentUserUtils.id;
+ let email = CurrentUserUtils.email;
+ if (id && email) {
+ let textInfo: [string, string] = [id, email];
+ doc.GetOrCreateAsync<ListField<CursorEntry>>(KeyStore.Cursors, ListField, field => {
+ let cursors = field.Data;
+ if (cursors.length > 0 && (ind = cursors.findIndex(entry => entry.Data[0][0] === id)) > -1) {
+ cursors[ind].Data[1] = position;
+ } else {
+ let entry = new TupleField<[string, string], [number, number]>([textInfo, position]);
+ cursors.push(entry);
+ }
+ })
+
+
+ }
+ }
+
+ protected getCursors(): CursorEntry[] {
+ let doc = this.props.Document;
+ let id = CurrentUserUtils.id;
+ let cursors = doc.GetList<CursorEntry>(KeyStore.Cursors, []);
+ let notMe = cursors.filter(entry => entry.Data[0][0] !== id);
+ return id ? notMe : [];
+ }
+
@undoBatch
@action
protected drop(e: Event, de: DragManager.DropEvent) {
- const docView: DocumentView = de.data["documentView"];
- const doc: Document = de.data["document"];
- if (docView && (!docView.props.ContainingCollectionView || docView.props.ContainingCollectionView !== this.props.CollectionView)) {
- if (docView.props.RemoveDocument) {
- docView.props.RemoveDocument(docView.props.Document);
+ if (de.data instanceof DragManager.DocumentDragData) {
+ if (de.data.aliasOnDrop) {
+ [KeyStore.Width, KeyStore.Height, KeyStore.CurPage].map(key =>
+ de.data.draggedDocument.GetTAsync(key, NumberField, (f: Opt<NumberField>) => f ? de.data.droppedDocument.SetNumber(key, f.Data) : null));
+ } else if (de.data.removeDocument) {
+ de.data.removeDocument(this.props.CollectionView);
}
- this.props.addDocument(docView.props.Document);
- e.stopPropagation();
- } else if (doc) {
- this.props.removeDocument(doc);
- this.props.addDocument(doc);
+ this.props.addDocument(de.data.droppedDocument, false);
e.stopPropagation();
}
}
@action
protected onDrop(e: React.DragEvent, options: DocumentOptions): void {
- e.stopPropagation()
- e.preventDefault()
let that = this;
let html = e.dataTransfer.getData("text/html");
let text = e.dataTransfer.getData("text/plain");
+
+ if (text && text.startsWith("<div")) {
+ return;
+ }
+ e.stopPropagation()
+ e.preventDefault()
+
if (html && html.indexOf("<img") != 0) {
console.log("not good");
let htmlDoc = Documents.HtmlDocument(html, { ...options, width: 300, height: 300 });
htmlDoc.SetText(KeyStore.DocumentText, text);
- this.props.addDocument(htmlDoc);
+ this.props.addDocument(htmlDoc, false);
return;
}
console.log(e.dataTransfer.items.length);
for (let i = 0; i < e.dataTransfer.items.length; i++) {
- const upload = window.location.origin + "/upload";
+ const upload = window.location.origin + RouteStore.upload;
let item = e.dataTransfer.items[i];
if (item.kind === "string" && item.type.indexOf("uri") != -1) {
- e.dataTransfer.items[i].getAsString(action((s: string) => this.props.addDocument(Documents.WebDocument(s, options))))
+ e.dataTransfer.items[i].getAsString(action((s: string) => this.props.addDocument(Documents.WebDocument(s, options), false)))
}
let type = item.type
console.log(type)
if (item.kind == "file") {
- let fReader = new FileReader()
let file = item.getAsFile();
let formData = new FormData()
@@ -109,7 +147,7 @@ export class CollectionViewBase extends React.Component<SubCollectionViewProps>
var doc: any;
if (type.indexOf("image") !== -1) {
- doc = Documents.ImageDocument(path, { ...options, nativeWidth: 300, width: 300, })
+ doc = Documents.ImageDocument(path, { ...options, nativeWidth: 200, width: 200, })
}
if (type.indexOf("video") !== -1) {
doc = Documents.VideoDocument(path, { ...options, nativeWidth: 300, width: 300, })
diff --git a/src/client/views/collections/MarqueeView.scss b/src/client/views/collections/MarqueeView.scss
new file mode 100644
index 000000000..6d9a79344
--- /dev/null
+++ b/src/client/views/collections/MarqueeView.scss
@@ -0,0 +1,8 @@
+
+.marqueeView {
+ border-style: dashed;
+ box-sizing: border-box;
+ position: absolute;
+ border-width: 1px;
+ border-color: black;
+} \ No newline at end of file
diff --git a/src/client/views/collections/MarqueeView.tsx b/src/client/views/collections/MarqueeView.tsx
new file mode 100644
index 000000000..8c2f3443c
--- /dev/null
+++ b/src/client/views/collections/MarqueeView.tsx
@@ -0,0 +1,175 @@
+import { action, IReactionDisposer, observable, reaction } from "mobx";
+import { observer } from "mobx-react";
+import { Document } from "../../../fields/Document";
+import { FieldWaiting, Opt } from "../../../fields/Field";
+import { KeyStore } from "../../../fields/KeyStore";
+import { Documents } from "../../documents/Documents";
+import { SelectionManager } from "../../util/SelectionManager";
+import { Transform } from "../../util/Transform";
+import { CollectionFreeFormView } from "./CollectionFreeFormView";
+import "./MarqueeView.scss";
+import React = require("react");
+import { InkField, StrokeData } from "../../../fields/InkField";
+import { Utils } from "../../../Utils";
+import { InkingCanvas } from "../InkingCanvas";
+
+interface MarqueeViewProps {
+ getMarqueeTransform: () => Transform;
+ getTransform: () => Transform;
+ container: CollectionFreeFormView;
+ addDocument: (doc: Document, allowDuplicates: false) => void;
+ activeDocuments: () => Document[];
+ selectDocuments: (docs: Document[]) => void;
+ removeDocument: (doc: Document) => boolean;
+}
+
+@observer
+export class MarqueeView extends React.Component<MarqueeViewProps>
+{
+ private _reactionDisposer: Opt<IReactionDisposer>;
+
+ @observable _lastX: number = 0;
+ @observable _lastY: number = 0;
+ @observable _downX: number = 0;
+ @observable _downY: number = 0;
+
+ componentDidMount() {
+ this._reactionDisposer = reaction(
+ () => this.props.container.MarqueeVisible,
+ (visible: boolean) => this.onPointerDown(visible, this.props.container.DownX, this.props.container.DownY))
+ }
+ componentWillUnmount() {
+ if (this._reactionDisposer) {
+ this._reactionDisposer();
+ }
+ this.cleanupInteractions();
+ }
+
+ @action
+ cleanupInteractions = () => {
+ document.removeEventListener("pointermove", this.onPointerMove, true)
+ document.removeEventListener("pointerup", this.onPointerUp, true);
+ document.removeEventListener("keydown", this.marqueeCommand, true);
+ }
+
+ @action
+ onPointerDown = (visible: boolean, downX: number, downY: number): void => {
+ if (visible) {
+ this._downX = this._lastX = downX;
+ this._downY = this._lastY = downY;
+ document.addEventListener("pointermove", this.onPointerMove, true)
+ document.addEventListener("pointerup", this.onPointerUp, true);
+ document.addEventListener("keydown", this.marqueeCommand, true);
+ }
+ }
+
+ @action
+ onPointerMove = (e: PointerEvent): void => {
+ this._lastX = e.pageX;
+ this._lastY = e.pageY;
+ }
+
+ @action
+ onPointerUp = (e: PointerEvent): void => {
+ this.cleanupInteractions();
+ if (!e.shiftKey) {
+ SelectionManager.DeselectAll();
+ }
+ this.props.selectDocuments(this.marqueeSelect());
+ }
+
+ intersectRect(r1: { left: number, top: number, width: number, height: number },
+ r2: { left: number, top: number, width: number, height: number }) {
+ return !(r2.left > r1.left + r1.width || r2.left + r2.width < r1.left || r2.top > r1.top + r1.height || r2.top + r2.height < r1.top);
+ }
+
+ get Bounds() {
+ let left = this._downX < this._lastX ? this._downX : this._lastX;
+ let top = this._downY < this._lastY ? this._downY : this._lastY;
+ let topLeft = this.props.getTransform().transformPoint(left, top);
+ let size = this.props.getTransform().transformDirection(this._lastX - this._downX, this._lastY - this._downY);
+ return { left: topLeft[0], top: topLeft[1], width: Math.abs(size[0]), height: Math.abs(size[1]) }
+ }
+
+ @action
+ marqueeCommand = (e: KeyboardEvent) => {
+ if (e.key == "Backspace" || e.key == "Delete") {
+ this.marqueeSelect().map(d => this.props.removeDocument(d));
+ this.props.container.props.Document.SetData(KeyStore.Ink, this.marqueeInkSelect(false), InkField);
+ this.cleanupInteractions();
+ }
+ if (e.key == "c") {
+ let bounds = this.Bounds;
+ let selected = this.marqueeSelect().map(d => {
+ this.props.removeDocument(d);
+ d.SetNumber(KeyStore.X, d.GetNumber(KeyStore.X, 0) - bounds.left - bounds.width / 2);
+ d.SetNumber(KeyStore.Y, d.GetNumber(KeyStore.Y, 0) - bounds.top - bounds.height / 2);
+ d.SetNumber(KeyStore.Page, 0);
+ d.SetText(KeyStore.Title, "" + d.GetNumber(KeyStore.Width, 0) + " " + d.GetNumber(KeyStore.Height, 0));
+ return d;
+ });
+ let liftedInk = this.marqueeInkSelect(true);
+ this.props.container.props.Document.SetData(KeyStore.Ink, this.marqueeInkSelect(false), InkField);
+ //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: liftedInk,
+ title: "a nested collection"
+ });
+ this.props.addDocument(newCollection, false);
+ // }, 100);
+ this.cleanupInteractions();
+ }
+ }
+ marqueeInkSelect(select: boolean) {
+ let selRect = this.Bounds;
+ let centerShiftX = 0 - (selRect.left + selRect.width / 2); // moves each point by the offset that shifts the selection's center to the origin.
+ let centerShiftY = 0 - (selRect.top + selRect.height / 2);
+ let ink = this.props.container.props.Document.GetT(KeyStore.Ink, InkField);
+ if (ink && ink != FieldWaiting && ink.Data) {
+ let idata = new Map();
+ ink.Data.forEach((value: StrokeData, key: string, map: any) => {
+ let inside = InkingCanvas.IntersectStrokeRect(value, selRect);
+ if (inside && select) {
+ idata.set(key,
+ {
+ pathData: value.pathData.map(val => { return { x: val.x + centerShiftX, y: val.y + centerShiftY } }),
+ color: value.color,
+ width: value.width,
+ tool: value.tool,
+ page: -1
+ });
+ } else if (!inside && !select) {
+ idata.set(key, value);
+ }
+ })
+ return idata;
+ }
+ }
+
+ marqueeSelect() {
+ let selRect = this.Bounds;
+ let selection: Document[] = [];
+ this.props.activeDocuments().map(doc => {
+ var x = doc.GetNumber(KeyStore.X, 0);
+ var y = doc.GetNumber(KeyStore.Y, 0);
+ var w = doc.GetNumber(KeyStore.Width, 0);
+ var h = doc.GetNumber(KeyStore.Height, 0);
+ if (this.intersectRect({ left: x, top: y, width: w, height: h }, selRect))
+ selection.push(doc)
+ })
+ return selection;
+ }
+
+ render() {
+ let p = this.props.getMarqueeTransform().transformPoint(this._downX < this._lastX ? this._downX : this._lastX, this._downY < this._lastY ? this._downY : this._lastY);
+ let v = this.props.getMarqueeTransform().transformDirection(this._lastX - this._downX, this._lastY - this._downY);
+ return (!this.props.container.MarqueeVisible ? (null) : <div className="marqueeView" style={{ transform: `translate(${p[0]}px, ${p[1]}px)`, width: `${Math.abs(v[0])}`, height: `${Math.abs(v[1])}` }} />);
+ }
+} \ No newline at end of file
diff --git a/src/client/views/collections/PreviewCursor.scss b/src/client/views/collections/PreviewCursor.scss
new file mode 100644
index 000000000..a797411f6
--- /dev/null
+++ b/src/client/views/collections/PreviewCursor.scss
@@ -0,0 +1,18 @@
+
+.previewCursor {
+ color: black;
+ position: absolute;
+ transform-origin: left top;
+ pointer-events: none;
+}
+
+//this is an animation for the blinking cursor!
+@keyframes blink {
+ 0% {opacity: 0}
+ 49%{opacity: 0}
+ 50% {opacity: 1}
+}
+
+#previewCursor {
+ animation: blink 1s infinite;
+} \ No newline at end of file
diff --git a/src/client/views/collections/PreviewCursor.tsx b/src/client/views/collections/PreviewCursor.tsx
new file mode 100644
index 000000000..cbf36cf9e
--- /dev/null
+++ b/src/client/views/collections/PreviewCursor.tsx
@@ -0,0 +1,73 @@
+import { action, IReactionDisposer, observable, reaction } from "mobx";
+import { observer } from "mobx-react";
+import { Document } from "../../../fields/Document";
+import { Opt } from "../../../fields/Field";
+import { Documents } from "../../documents/Documents";
+import { Transform } from "../../util/Transform";
+import { CollectionFreeFormView } from "./CollectionFreeFormView";
+import "./PreviewCursor.scss";
+import React = require("react");
+
+
+export interface PreviewCursorProps {
+ getTransform: () => Transform;
+ container: CollectionFreeFormView;
+ addLiveTextDocument: (doc: Document) => void;
+}
+
+@observer
+export class PreviewCursor extends React.Component<PreviewCursorProps> {
+ private _reactionDisposer: Opt<IReactionDisposer>;
+
+ @observable _lastX: number = 0;
+ @observable _lastY: number = 0;
+
+ componentDidMount() {
+ this._reactionDisposer = reaction(
+ () => this.props.container.PreviewCursorVisible,
+ (visible: boolean) => this.onCursorPlaced(visible, this.props.container.DownX, this.props.container.DownY))
+ }
+ componentWillUnmount() {
+ if (this._reactionDisposer) {
+ this._reactionDisposer();
+ }
+ this.cleanupInteractions();
+ }
+
+
+ @action
+ cleanupInteractions = () => {
+ document.removeEventListener("keypress", this.onKeyPress, true);
+ }
+
+ @action
+ onCursorPlaced = (visible: boolean, downX: number, downY: number): void => {
+ if (visible) {
+ document.addEventListener("keypress", this.onKeyPress, true);
+ this._lastX = downX;
+ this._lastY = downY;
+ } else
+ this.cleanupInteractions();
+ }
+
+ @action
+ onKeyPress = (e: KeyboardEvent) => {
+ //if not these keys, make a textbox if preview cursor is active!
+ if (!e.ctrlKey && !e.altKey && !e.defaultPrevented) {
+ //make textbox and add it to this collection
+ let [x, y] = this.props.getTransform().transformPoint(this._lastX, this._lastY);
+ let newBox = Documents.TextDocument({ width: 200, height: 100, x: x, y: y, title: "new" });
+ this.props.addLiveTextDocument(newBox);
+ e.stopPropagation();
+ }
+ }
+
+ render() {
+ //get local position and place cursor there!
+ let [x, y] = this.props.getTransform().transformPoint(this._lastX, this._lastY);
+ return (
+ !this.props.container.PreviewCursorVisible ? (null) :
+ <div className="previewCursor" id="previewCursor" style={{ transform: `translate(${x}px, ${y}px)` }}>I</div>)
+
+ }
+} \ No newline at end of file