aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/collections
diff options
context:
space:
mode:
authorTyler Schicke <tyler_schicke@brown.edu>2019-03-02 23:10:01 -0500
committerTyler Schicke <tyler_schicke@brown.edu>2019-03-02 23:10:01 -0500
commitfd77567ca4cb2afbaa9c2df1a71f8ae728369bb1 (patch)
tree123b826150a9714b4431a2a55ba948e13608099f /src/client/views/collections
parent6d456bbe4a2de50abe1a4b338d1f446d3123b321 (diff)
parentf1be2fc29ea59c05a3ece851df3ed72adb07a0c2 (diff)
Merge branch 'master' of github-tsch-brown:browngraphicslab/Dash-Web into contextMenu
Diffstat (limited to 'src/client/views/collections')
-rw-r--r--src/client/views/collections/CollectionDockingView.tsx55
-rw-r--r--src/client/views/collections/CollectionFreeFormView.scss27
-rw-r--r--src/client/views/collections/CollectionFreeFormView.tsx110
-rw-r--r--src/client/views/collections/CollectionSchemaView.scss31
-rw-r--r--src/client/views/collections/CollectionSchemaView.tsx131
-rw-r--r--src/client/views/collections/CollectionTreeView.scss29
-rw-r--r--src/client/views/collections/CollectionTreeView.tsx165
-rw-r--r--src/client/views/collections/CollectionView.tsx6
-rw-r--r--src/client/views/collections/CollectionViewBase.tsx11
9 files changed, 349 insertions, 216 deletions
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 5fb632469..c51fba908 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -1,16 +1,15 @@
import * as GoldenLayout from "golden-layout";
import 'golden-layout/src/css/goldenlayout-base.css';
import 'golden-layout/src/css/goldenlayout-dark-theme.css';
-import { action, computed, observable, reaction } from "mobx";
+import { action, observable, reaction } from "mobx";
import { observer } from "mobx-react";
import * as ReactDOM from 'react-dom';
-import Measure from "react-measure";
import { Document } from "../../../fields/Document";
-import { FieldId, Opt, Field } from "../../../fields/Field";
import { KeyStore } from "../../../fields/KeyStore";
+import Measure from "react-measure";
+import { FieldId, Opt, Field } from "../../../fields/Field";
import { Utils } from "../../../Utils";
import { Server } from "../../Server";
-import { DragManager } from "../../util/DragManager";
import { undoBatch } from "../../util/UndoManager";
import { DocumentView } from "../nodes/DocumentView";
import "./CollectionDockingView.scss";
@@ -34,10 +33,6 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
}
private _goldenLayout: any = null;
- private _dragDiv: any = null;
- private _dragParent: HTMLElement | null = null;
- private _dragElement: HTMLElement | undefined;
- private _dragFakeElement: HTMLElement | undefined;
private _containerRef = React.createRef<HTMLDivElement>();
private _fullScreen: any = null;
@@ -47,28 +42,8 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
(window as any).React = React;
(window as any).ReactDOM = ReactDOM;
}
-
- public StartOtherDrag(dragElement: HTMLElement, dragDoc: Document) {
- this._dragElement = dragElement;
- this._dragParent = dragElement.parentElement;
- // bcz: we want to copy this document into the header, not move it there.
- // However, GoldenLayout is setup to move things, so we have to do some kludgy stuff:
-
- // - create a temporary invisible div and register that as a DragSource with GoldenLayout
- this._dragDiv = document.createElement("div");
- this._dragDiv.style.opacity = 0;
- DragManager.Root().appendChild(this._dragDiv);
- this._goldenLayout.createDragSource(this._dragDiv, CollectionDockingView.makeDocumentConfig(dragDoc));
-
- // - add our document to that div so that GoldenLayout will get the move events its listening for
- this._dragDiv.appendChild(this._dragElement);
-
- // - add a duplicate of our document to the original document's container
- // (GoldenLayout will be removing our original one)
- this._dragFakeElement = dragElement.cloneNode(true) as HTMLElement;
- this._dragParent!.appendChild(this._dragFakeElement);
-
- // all of this must be undone when the document has been dropped (see tabCreated)
+ public StartOtherDrag(dragDoc: Document, e: any) {
+ this.AddRightSplit(dragDoc, true).contentItems[0].tab._dragListener.onMouseDown({ pageX: e.pageX, pageY: e.pageY, preventDefault: () => { }, button: e.button })
}
@action
@@ -98,7 +73,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
// Creates a vertical split on the right side of the docking view, and then adds the Document to that split
//
@action
- public AddRightSplit(document: Document) {
+ public AddRightSplit(document: Document, minimize: boolean = false) {
this._goldenLayout.emit('stateChanged');
let newItemStackConfig = {
type: 'stack',
@@ -121,10 +96,15 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
collayout.config["width"] = 50;
newContentItem.config["width"] = 50;
}
+ if (minimize) {
+ newContentItem.config["width"] = 10;
+ newContentItem.config["height"] = 10;
+ }
newContentItem.callDownwards('_$init');
this._goldenLayout.root.callDownwards('setSize', [this._goldenLayout.width, this._goldenLayout.height]);
this._goldenLayout.emit('stateChanged');
this.stateChanged();
+ return newContentItem;
}
setupGoldenLayout() {
@@ -218,13 +198,6 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
this.stateChanged();
}
tabCreated = (tab: any) => {
- if (this._dragDiv) {
- this._dragDiv.removeChild(this._dragElement);
- this._dragParent!.removeChild(this._dragFakeElement!);
- this._dragParent!.appendChild(this._dragElement!);
- DragManager.Root().removeChild(this._dragDiv);
- this._dragDiv = null;
- }
tab.closeElement.off('click') //unbind the current click handler
.click(function () {
tab.contentItem.remove();
@@ -245,7 +218,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
render() {
return (
<div className="collectiondockingview-container" id="menuContainer"
- onPointerDown={this.onPointerDown} onPointerUp={this.onPointerUp} onContextMenu={(e) => e.preventDefault()} ref={this._containerRef}
+ onPointerDown={this.onPointerDown} onPointerUp={this.onPointerUp} ref={this._containerRef}
style={{
width: "100%",
height: "100%",
@@ -263,7 +236,7 @@ interface DockedFrameProps {
@observer
export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
- @observable private _mainCont = React.createRef<HTMLDivElement>();
+ private _mainCont = React.createRef<HTMLDivElement>();
@observable private _panelWidth = 0;
@observable private _panelHeight = 0;
@observable private _document: Opt<Document>;
@@ -295,6 +268,8 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
PanelHeight={this._nativeHeight}
ScreenToLocalTransform={this.ScreenToLocalTransform}
isTopMost={true}
+ SelectOnLoad={false}
+ focus={(doc: Document) => { }}
ContainingCollectionView={undefined} />
</div>
diff --git a/src/client/views/collections/CollectionFreeFormView.scss b/src/client/views/collections/CollectionFreeFormView.scss
index d583a8218..f432e8cc3 100644
--- a/src/client/views/collections/CollectionFreeFormView.scss
+++ b/src/client/views/collections/CollectionFreeFormView.scss
@@ -1,14 +1,5 @@
.collectionfreeformview-container {
- ::-webkit-scrollbar {
- -webkit-appearance: none;
- width: 10px;
- }
- ::-webkit-scrollbar-thumb {
- border-radius: 5px;
- background-color: rgba(0,0,0,.5);
- }
-
.collectionfreeformview > .jsx-parser{
position:absolute;
height: 100%;
@@ -29,4 +20,22 @@
width:100%;
height: 100%
}
+}
+
+.border {
+ border-style: solid;
+ box-sizing: border-box;
+ width: 100%;
+ height: 100%;
+}
+
+//this is an animation for the blinking cursor!
+@keyframes blink {
+ 0% {opacity: 0}
+ 49%{opacity: 0}
+ 50% {opacity: 1}
+}
+
+#prevCursor {
+ animation: blink 1s infinite;
} \ No newline at end of file
diff --git a/src/client/views/collections/CollectionFreeFormView.tsx b/src/client/views/collections/CollectionFreeFormView.tsx
index 12909c151..f71f2791c 100644
--- a/src/client/views/collections/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/CollectionFreeFormView.tsx
@@ -1,4 +1,4 @@
-import { action, computed } from "mobx";
+import { observable, action, computed } from "mobx";
import { observer } from "mobx-react";
import { Document } from "../../../fields/Document";
import { FieldWaiting } from "../../../fields/Field";
@@ -10,16 +10,17 @@ import { Transform } from "../../util/Transform";
import { undoBatch } from "../../util/UndoManager";
import { CollectionDockingView } from "../collections/CollectionDockingView";
import { CollectionSchemaView } from "../collections/CollectionSchemaView";
-import { CollectionTreeView } from "../collections/CollectionTreeView";
import { CollectionView } from "../collections/CollectionView";
import { CollectionFreeFormDocumentView } from "../nodes/CollectionFreeFormDocumentView";
import { DocumentView } from "../nodes/DocumentView";
-import { WebView } from "../nodes/WebView";
import { FormattedTextBox } from "../nodes/FormattedTextBox";
import { ImageBox } from "../nodes/ImageBox";
+import { WebBox } from "../nodes/WebBox";
+import { KeyValueBox } from "../nodes/KeyValueBox"
import "./CollectionFreeFormView.scss";
import { COLLECTION_BORDER_WIDTH } from "./CollectionView";
import { CollectionViewBase } from "./CollectionViewBase";
+import { Documents } from "../../documents/Documents";
import React = require("react");
const JsxParser = require('react-jsx-parser').default;//TODO Why does this need to be imported like this?
@@ -28,9 +29,17 @@ export class CollectionFreeFormView extends CollectionViewBase {
private _canvasRef = React.createRef<HTMLDivElement>();
private _lastX: number = 0;
private _lastY: number = 0;
+ 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;
+ //determines whether the blinking cursor for indicating whether a text will be made on key down is visible
+ @observable
+ private _previewCursorVisible: boolean = false;
+
@computed get panX(): number { return this.props.Document.GetNumber(KeyStore.PanX, 0) }
@computed get panY(): number { return this.props.Document.GetNumber(KeyStore.PanY, 0) }
@computed get scale(): number { return this.props.Document.GetNumber(KeyStore.Scale, 1); }
@@ -63,8 +72,10 @@ export class CollectionFreeFormView extends CollectionViewBase {
document.addEventListener("pointermove", this.onPointerMove);
document.removeEventListener("pointerup", this.onPointerUp);
document.addEventListener("pointerup", this.onPointerUp);
- this._downX = this._lastX = e.pageX;
- this._downY = this._lastY = e.pageY;
+ this._lastX = e.pageX;
+ this._lastY = e.pageY;
+ this._downX = e.pageX;
+ this._downY = e.pageY;
}
}
@@ -74,22 +85,25 @@ export class CollectionFreeFormView extends CollectionViewBase {
document.removeEventListener("pointerup", this.onPointerUp);
e.stopPropagation();
if (Math.abs(this._downX - e.clientX) < 3 && Math.abs(this._downY - e.clientY) < 3) {
+ //show preview text cursor on tap
+ this._previewCursorVisible = true;
+ //select is not already selected
if (!this.props.isSelected()) {
this.props.select(false);
}
}
+
}
@action
onPointerMove = (e: PointerEvent): void => {
if (!e.cancelBubble && this.props.active()) {
- e.preventDefault();
e.stopPropagation();
let x = this.props.Document.GetNumber(KeyStore.PanX, 0);
let y = this.props.Document.GetNumber(KeyStore.PanY, 0);
- let [dx, dy] = this.props.ScreenToLocalTransform().transformDirection(e.clientX - this._lastX, e.clientY - this._lastY);
-
- this.SetPan(x + dx, y + dy);
+ 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;
@@ -120,11 +134,12 @@ export class CollectionFreeFormView extends CollectionViewBase {
deltaScale = 1 / this.zoomScaling;
let [x, y] = transform.transformPoint(e.clientX, e.clientY);
- let localTransform = this.getLocalTransform();
+ let localTransform = this.getLocalTransform()
localTransform = localTransform.inverse().scaleAbout(deltaScale, x, y)
+ console.log(localTransform)
this.props.Document.SetNumber(KeyStore.Scale, localTransform.Scale);
- this.SetPan(localTransform.TranslateX, localTransform.TranslateY);
+ this.SetPan(-localTransform.TranslateX / localTransform.Scale, -localTransform.TranslateY / localTransform.Scale);
}
}
@@ -146,6 +161,24 @@ 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;
@@ -163,7 +196,6 @@ export class CollectionFreeFormView extends CollectionViewBase {
});
}
-
@computed get backgroundLayout(): string | undefined {
let field = this.props.Document.GetT(KeyStore.BackgroundLayout, TextField);
if (field && field !== "<Waiting>") {
@@ -176,10 +208,18 @@ export class CollectionFreeFormView extends CollectionViewBase {
return field.Data;
}
}
+
+ focusDocument = (doc: Document) => {
+ let x = doc.GetNumber(KeyStore.X, 0) + doc.GetNumber(KeyStore.Width, 0) / 2;
+ let y = doc.GetNumber(KeyStore.Y, 0) + doc.GetNumber(KeyStore.Height, 0) / 2;
+ this.SetPan(x, y);
+ this.props.focus(this.props.Document);
+ }
+
+
@computed
get views() {
- const { fieldKey, Document } = this.props;
- const lvalue = Document.GetT<ListField<Document>>(fieldKey, ListField);
+ const lvalue = this.props.Document.GetT<ListField<Document>>(this.props.fieldKey, ListField);
if (lvalue && lvalue != FieldWaiting) {
return lvalue.Data.map(doc => {
return (<CollectionFreeFormDocumentView key={doc.Id} Document={doc}
@@ -187,10 +227,13 @@ export class CollectionFreeFormView extends CollectionViewBase {
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} />);
+ ContainingCollectionView={this.props.CollectionView}
+ focus={this.focusDocument}
+ />);
})
}
return null;
@@ -200,7 +243,7 @@ export class CollectionFreeFormView extends CollectionViewBase {
get backgroundView() {
return !this.backgroundLayout ? (null) :
(<JsxParser
- components={{ FormattedTextBox, ImageBox, CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, WebView }}
+ components={{ FormattedTextBox, ImageBox, CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, WebBox, KeyValueBox }}
bindings={this.props.bindings}
jsx={this.backgroundLayout}
showWarnings={true}
@@ -211,7 +254,7 @@ export class CollectionFreeFormView extends CollectionViewBase {
get overlayView() {
return !this.overlayLayout ? (null) :
(<JsxParser
- components={{ FormattedTextBox, ImageBox, CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView }}
+ components={{ FormattedTextBox, ImageBox, CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, WebBox, KeyValueBox }}
bindings={this.props.bindings}
jsx={this.overlayLayout}
showWarnings={true}
@@ -219,26 +262,47 @@ export class CollectionFreeFormView extends CollectionViewBase {
/>);
}
- getTransform = (): Transform => this.props.ScreenToLocalTransform().translate(-COLLECTION_BORDER_WIDTH - this.centeringShiftX, -COLLECTION_BORDER_WIDTH - this.centeringShiftY).transform(this.getLocalTransform())
- getLocalTransform = (): Transform => Transform.Identity.translate(-this.panX, -this.panY).scale(1 / this.scale);
+ getTransform = (): Transform => this.props.ScreenToLocalTransform().translate(-COLLECTION_BORDER_WIDTH, -COLLECTION_BORDER_WIDTH).translate(-this.centeringShiftX, -this.centeringShiftY).transform(this.getLocalTransform())
+ 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;
+ }
+
render() {
- const panx: number = this.props.Document.GetNumber(KeyStore.PanX, 0) + this.centeringShiftX;
- const pany: number = this.props.Document.GetNumber(KeyStore.PanY, 0) + this.centeringShiftY;
+
+ //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>
+ }
+
+ 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);
+
return (
<div className="collectionfreeformview-container"
onPointerDown={this.onPointerDown}
+ onKeyPress={this.onKeyDown}
onWheel={this.onPointerWheel}
- onContextMenu={(e) => e.preventDefault()}
onDrop={this.onDrop.bind(this)}
onDragOver={this.onDragOver}
+ onBlur={this.onBlur}
style={{ borderWidth: `${COLLECTION_BORDER_WIDTH}px`, }}
+ tabIndex={0}
ref={this.createDropTarget}>
<div className="collectionfreeformview"
- style={{ transformOrigin: "left top", transform: ` translate(${panx}px, ${pany}px) scale(${this.zoomScaling}, ${this.zoomScaling})` }}
+ style={{ transformOrigin: "left top", transform: `translate(${dx}px, ${dy}px) scale(${this.zoomScaling}, ${this.zoomScaling}) translate(${panx}px, ${pany}px)` }}
ref={this._canvasRef}>
{this.backgroundView}
+ {cursor}
{this.views}
</div>
{this.overlayView}
diff --git a/src/client/views/collections/CollectionSchemaView.scss b/src/client/views/collections/CollectionSchemaView.scss
index 0bd5a2ed3..d40e6d314 100644
--- a/src/client/views/collections/CollectionSchemaView.scss
+++ b/src/client/views/collections/CollectionSchemaView.scss
@@ -1,17 +1,36 @@
+
.collectionSchemaView-container {
border-style: solid;
box-sizing: border-box;
position: absolute;
width: 100%;
height: 100%;
- ::-webkit-scrollbar {
- -webkit-appearance: none;
- width: 10px;
+ .collectionSchemaView-previewRegion {
+ position: relative;
+ background: black;
+ float: left;
+ height: 100%;
+ }
+ .collectionSchemaView-previewHandle {
+ position: absolute;
+ height: 37px;
+ width: 20px;
+ z-index: 20;
+ right: 0;
+ top: 0;
+ background: Black ;
+ }
+ .collectionSchemaView-dividerDragger{
+ position: relative;
+ background: black;
+ float: left;
+ height: 100%;
}
- ::-webkit-scrollbar-thumb {
- border-radius: 5px;
- background-color: rgba(0,0,0,.5);
+ .collectionSchemaView-tableContainer {
+ position: relative;
+ float: left;
+ height: 100%;
}
.ReactTable {
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 4beb0aea1..49f95c014 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -1,11 +1,11 @@
import React = require("react")
-import { action, observable, trace } from "mobx";
+import { action, observable } from "mobx";
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, FieldWaiting } from "../../../fields/Field";
+import { Field } from "../../../fields/Field";
import { KeyStore } from "../../../fields/KeyStore";
import { CompileScript, ToField } from "../../util/Scripting";
import { Transform } from "../../util/Transform";
@@ -16,11 +16,11 @@ import { FieldView, FieldViewProps } from "../nodes/FieldView";
import "./CollectionSchemaView.scss";
import { COLLECTION_BORDER_WIDTH } from "./CollectionView";
import { CollectionViewBase } from "./CollectionViewBase";
-import { DragManager } from "../../util/DragManager";
-import { CollectionDockingView } from "./CollectionDockingView";
+import { setupDrag } from "../../util/DragManager";
// 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
export class CollectionSchemaView extends CollectionViewBase {
private _mainCont = React.createRef<HTMLDivElement>();
@@ -33,9 +33,6 @@ export class CollectionSchemaView extends CollectionViewBase {
@observable _selectedIndex = 0;
@observable _splitPercentage: number = 50;
-
-
-
renderCell = (rowProps: CellInfo) => {
let props: FieldViewProps = {
doc: rowProps.value[0],
@@ -43,37 +40,16 @@ export class CollectionSchemaView extends CollectionViewBase {
isSelected: () => false,
select: () => { },
isTopMost: false,
- bindings: {}
+ bindings: {},
+ selectOnLoad: false,
}
let contents = (
<FieldView {...props} />
)
let reference = React.createRef<HTMLDivElement>();
- let onRowMove = action((e: PointerEvent): void => {
- e.stopPropagation();
- e.preventDefault();
-
- document.removeEventListener("pointermove", onRowMove);
- document.removeEventListener('pointerup', onRowUp);
- DragManager.StartDrag(reference.current!, { document: props.doc });
- });
- let onRowUp = action((e: PointerEvent): void => {
- document.removeEventListener("pointermove", onRowMove);
- document.removeEventListener('pointerup', onRowUp);
- });
- let onRowDown = (e: React.PointerEvent) => {
- if (this.props.isSelected() || this.props.isTopMost) {
- if (e.shiftKey) {
- CollectionDockingView.Instance.StartOtherDrag(reference.current!, props.doc);
- e.stopPropagation();
- } else {
- document.addEventListener("pointermove", onRowMove);
- document.addEventListener('pointerup', onRowUp);
- }
- }
- }
+ let onItemDown = setupDrag(reference, () => props.doc);
return (
- <div onPointerDown={onRowDown} ref={reference}>
+ <div onPointerDown={onItemDown} key={props.doc.Id} ref={reference}>
<EditableView contents={contents}
height={36} GetValue={() => {
let field = props.doc.Get(props.fieldKey);
@@ -81,8 +57,9 @@ export class CollectionSchemaView extends CollectionViewBase {
return field.ToScriptString();
}
return field || "";
- }} SetValue={(value: string) => {
- let script = CompileScript(value);
+ }}
+ SetValue={(value: string) => {
+ let script = CompileScript(value, undefined, true);
if (!script.compiled) {
return false;
}
@@ -98,7 +75,9 @@ export class CollectionSchemaView extends CollectionViewBase {
}
}
return false;
- }}></EditableView></div>
+ }}>
+ </EditableView>
+ </div>
)
}
@@ -117,8 +96,8 @@ export class CollectionSchemaView extends CollectionViewBase {
}
}),
style: {
- background: rowInfo.index == this._selectedIndex ? "#00afec" : "white",
- color: rowInfo.index == this._selectedIndex ? "white" : "black"
+ background: rowInfo.index == this._selectedIndex ? "lightGray" : "white",
+ //color: rowInfo.index == this._selectedIndex ? "white" : "black"
}
};
}
@@ -197,6 +176,8 @@ export class CollectionSchemaView extends CollectionViewBase {
return this.props.ScreenToLocalTransform().translate(- COLLECTION_BORDER_WIDTH - this.DIVIDER_WIDTH - this._dividerX, - COLLECTION_BORDER_WIDTH).scale(1 / this._contentScaling);
}
+ focusDocument = (doc: Document) => { }
+
render() {
const columns = this.props.Document.GetList(KeyStore.ColumnsKey, [KeyStore.Title, KeyStore.Data, KeyStore.Author])
const children = this.props.Document.GetList<Document>(this.props.fieldKey, []);
@@ -208,53 +189,55 @@ export class CollectionSchemaView extends CollectionViewBase {
<DocumentView Document={selected}
AddDocument={this.props.addDocument} RemoveDocument={this.props.removeDocument}
isTopMost={false}
+ SelectOnLoad={false}
ScreenToLocalTransform={this.getTransform}
ContentScaling={this.getContentScaling}
PanelWidth={this.getPanelWidth}
PanelHeight={this.getPanelHeight}
- ContainingCollectionView={this.props.CollectionView} />
+ ContainingCollectionView={this.props.CollectionView}
+ focus={this.focusDocument}
+ />
</div>
}
</Measure>
)
- let handle = !this.props.active() ? (null) : (
- <div style={{ position: "absolute", height: "37px", width: "20px", zIndex: 20, right: 0, top: 0, background: "Black" }} onPointerDown={this.onExpanderDown} />);
+ let previewHandle = !this.props.active() ? (null) : (
+ <div className="collectionSchemaView-previewHandle" onPointerDown={this.onExpanderDown} />);
return (
- <div onPointerDown={this.onPointerDown} ref={this._mainCont} className="collectionSchemaView-container" style={{ borderWidth: `${COLLECTION_BORDER_WIDTH}px` }} >
- <Measure onResize={action((r: any) => {
- this._dividerX = r.entry.width;
- this._panelHeight = r.entry.height;
- })}>
- {({ measureRef }) =>
- <div ref={measureRef} className="collectionSchemaView-tableContainer" style={{ position: "relative", float: "left", width: `${this._splitPercentage}%`, height: "100%" }}>
- <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>
- }
- </Measure>
- <div className="collectionSchemaView-dividerDragger" style={{ position: "relative", background: "black", float: "left", width: `${this.DIVIDER_WIDTH}px`, height: "100%" }} onPointerDown={this.onDividerDown} />
- <div className="collectionSchemaView-previewRegion"
- onDrop={(e: React.DragEvent) => this.onDrop(e, {})}
- ref={this.createDropTarget}
- style={{ position: "relative", float: "left", width: `calc(${100 - this._splitPercentage}% - ${this.DIVIDER_WIDTH}px)`, height: "100%" }}>
- {content}
+ <div className="collectionSchemaView-container" onPointerDown={this.onPointerDown} ref={this._mainCont} style={{ borderWidth: `${COLLECTION_BORDER_WIDTH}px` }} >
+ <div className="collectionSchemaView-dropTarget" onDrop={(e: React.DragEvent) => this.onDrop(e, {})} ref={this.createDropTarget}>
+ <Measure onResize={action((r: any) => {
+ this._dividerX = r.entry.width;
+ 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>
+ }
+ </Measure>
+ <div className="collectionSchemaView-dividerDragger" onPointerDown={this.onDividerDown} style={{ width: `${this.DIVIDER_WIDTH}px` }} />
+ <div className="collectionSchemaView-previewRegion" style={{ width: `calc(${100 - this._splitPercentage}% - ${this.DIVIDER_WIDTH}px)` }}>
+ {content}
+ </div>
+ {previewHandle}
</div>
- {handle}
</div >
)
}
diff --git a/src/client/views/collections/CollectionTreeView.scss b/src/client/views/collections/CollectionTreeView.scss
index 675fc6c53..f8d580a7b 100644
--- a/src/client/views/collections/CollectionTreeView.scss
+++ b/src/client/views/collections/CollectionTreeView.scss
@@ -1,3 +1,8 @@
+#body {
+ padding: 20px;
+ background: #bbbbbb;
+}
+
ul {
list-style: none;
}
@@ -10,19 +15,23 @@ li {
padding-left: 0;
}
-/* ALL THESE SPACINGS ARE SUPER HACKY RIGHT NOW HANNAH PLS HELP */
+.bullet {
+ width: 1.5em;
+ display: inline-block;
+}
-li:before {
- content: '\2014';
- margin-right: 0.7em;
+.collectionTreeView-dropTarget {
+ border-style: solid;
+ box-sizing: border-box;
+ height: 100%;
}
-.collapsed:before {
- content: '\25b6';
- margin-right: 0.65em;
+.docContainer {
+ display: inline-table;
}
-.uncollapsed:before {
- content: '\25bc';
- margin-right: 0.5em;
+.delete-button {
+ color: #999999;
+ float: right;
+ margin-left: 1em;
} \ No newline at end of file
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 52e853bf7..8b06d9ac4 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -7,9 +7,20 @@ import React = require("react")
import { TextField } from "../../../fields/TextField";
import { observable, action } from "mobx";
import "./CollectionTreeView.scss";
+import { EditableView } from "../EditableView";
+import { setupDrag } from "../../util/DragManager";
+import { FieldWaiting } from "../../../fields/Field";
+import { COLLECTION_BORDER_WIDTH } from "./CollectionView";
export interface TreeViewProps {
document: Document;
+ deleteDoc: (doc: Document) => void;
+}
+
+export enum BulletType {
+ Collapsed,
+ Collapsible,
+ List
}
@observer
@@ -21,61 +32,100 @@ class TreeView extends React.Component<TreeViewProps> {
@observable
collapsed: boolean = false;
+ delete = () => {
+ this.props.deleteDoc(this.props.document);
+ }
+
+
+ @action
+ remove = (document: Document) => {
+ var children = this.props.document.GetT<ListField<Document>>(KeyStore.Data, ListField);
+ if (children && children !== FieldWaiting) {
+ children.Data.splice(children.Data.indexOf(document), 1);
+ }
+ }
+
+ renderBullet(type: BulletType) {
+ let onClicked = action(() => this.collapsed = !this.collapsed);
+
+ switch (type) {
+ case BulletType.Collapsed:
+ return <div className="bullet" onClick={onClicked}>&#9654;</div>
+ case BulletType.Collapsible:
+ return <div className="bullet" onClick={onClicked}>&#9660;</div>
+ case BulletType.List:
+ return <div className="bullet">&mdash;</div>
+ }
+ }
+
/**
- * Renders a single child document. If this child is a collection, it will call renderTreeView again. Otherwise, it will just append a list element.
- * @param document The document to render.
+ * Renders the EditableView title element for placement into the tree.
*/
- renderChild(document: Document) {
- var children = document.GetT<ListField<Document>>(KeyStore.Data, ListField);
- let title = document.GetT<TextField>(KeyStore.Title, TextField);
+ renderTitle() {
+ let title = this.props.document.GetT<TextField>(KeyStore.Title, TextField);
// if the title hasn't loaded, immediately return the div
if (!title || title === "<Waiting>") {
- return <div key={document.Id}></div>;
+ return <div key={this.props.document.Id}></div>;
}
- // otherwise, check if it's a collection.
- else if (children && children !== "<Waiting>") {
- // if it's not collapsed, then render the full TreeView.
+ return <div className="docContainer"> <EditableView contents={title.Data}
+ height={36} GetValue={() => {
+ let title = this.props.document.GetT<TextField>(KeyStore.Title, TextField);
+ if (title && title !== "<Waiting>")
+ return title.Data;
+ return "";
+ }} SetValue={(value: string) => {
+ this.props.document.SetData(KeyStore.Title, value, TextField);
+ return true;
+ }} />
+ <div className="delete-button" onClick={this.delete}>x</div>
+ </div >
+ }
+
+ render() {
+ var children = this.props.document.GetT<ListField<Document>>(KeyStore.Data, ListField);
+
+ let reference = React.createRef<HTMLDivElement>();
+ let onItemDown = setupDrag(reference, () => this.props.document);
+ let titleElement = this.renderTitle();
+
+ // check if this document is a collection
+ if (children && children !== FieldWaiting) {
+ let subView;
+
+ // if uncollapsed, then add the children elements
if (!this.collapsed) {
- return (
- <li className="uncollapsed" key={document.Id} onClick={action(() => this.collapsed = true)} >
- {title.Data}
- <ul key={document.Id}>
- <TreeView
- document={document}
- />
+ // render all children elements
+ let childrenElement = (children.Data.map(value =>
+ <TreeView document={value} deleteDoc={this.remove} />)
+ )
+ subView =
+ <li key={this.props.document.Id} >
+ {this.renderBullet(BulletType.Collapsible)}
+ {titleElement}
+ <ul key={this.props.document.Id}>
+ {childrenElement}
</ul>
</li>
- );
} else {
- return <li className="collapsed" key={document.Id} onClick={action(() => this.collapsed = false)}>{title.Data}</li>
+ subView = <li key={this.props.document.Id}>
+ {this.renderBullet(BulletType.Collapsed)}
+ {titleElement}
+ </li>
}
- }
- // finally, if it's a normal document, then render it as such.
- else {
- return <li key={document.Id}>{title.Data}</li>;
+ return <div className="treeViewItem-container" onPointerDown={onItemDown} ref={reference}>
+ {subView}
+ </div>
}
- }
-
- render() {
- var children = this.props.document.GetT<ListField<Document>>(KeyStore.Data, ListField);
- if (children && children !== "<Waiting>") {
- return (<div>
- {children.Data.map(value => this.renderChild(value))}
- </div>)
- // let results: JSX.Element[] = [];
-
- // // append a list item for each child in the collection
- // children.Data.forEach((value) => {
- // results.push(this.renderChild(value));
- // })
-
- // return results;
- } else {
- return <div></div>;
+ // otherwise this is a normal leaf node
+ else {
+ return <li key={this.props.document.Id}>
+ {this.renderBullet(BulletType.List)}
+ {titleElement}
+ </li>;
}
}
}
@@ -84,21 +134,42 @@ class TreeView extends React.Component<TreeViewProps> {
@observer
export class CollectionTreeView extends CollectionViewBase {
+ @action
+ remove = (document: Document) => {
+ var children = this.props.Document.GetT<ListField<Document>>(KeyStore.Data, ListField);
+ if (children && children !== FieldWaiting) {
+ children.Data.splice(children.Data.indexOf(document), 1);
+ }
+ }
+
render() {
let titleStr = "";
let title = this.props.Document.GetT<TextField>(KeyStore.Title, TextField);
- if (title && title !== "<Waiting>") {
+ if (title && title !== FieldWaiting) {
titleStr = title.Data;
}
+
+ var children = this.props.Document.GetT<ListField<Document>>(KeyStore.Data, ListField);
+ let childrenElement = !children || children === FieldWaiting ? (null) :
+ (children.Data.map(value =>
+ <TreeView document={value} key={value.Id} deleteDoc={this.remove} />)
+ )
+
return (
- <div>
- <h3>{titleStr}</h3>
+ <div id="body" className="collectionTreeView-dropTarget" onDrop={(e: React.DragEvent) => this.onDrop(e, {})} ref={this.createDropTarget} style={{ borderWidth: `${COLLECTION_BORDER_WIDTH}px` }}>
+ <h3>
+ <EditableView contents={titleStr}
+ height={72} GetValue={() => {
+ return this.props.Document.Title;
+ }} SetValue={(value: string) => {
+ this.props.Document.SetData(KeyStore.Title, value, TextField);
+ return true;
+ }} />
+ </h3>
<ul className="no-indent">
- <TreeView
- document={this.props.Document}
- />
+ {childrenElement}
</ul>
- </div>
+ </div >
);
}
} \ No newline at end of file
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index a7db07a42..1dbc3c3e3 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -1,4 +1,4 @@
-import { action, computed } from "mobx";
+import { action } from "mobx";
import { observer } from "mobx-react";
import { Document } from "../../../fields/Document";
import { ListField } from "../../../fields/ListField";
@@ -30,7 +30,7 @@ export class CollectionView extends React.Component<CollectionViewProps> {
public static LayoutString(fieldKey: string = "DataKey") {
return `<CollectionView Document={Document}
ScreenToLocalTransform={ScreenToLocalTransform} fieldKey={${fieldKey}} panelWidth={PanelWidth} panelHeight={PanelHeight} isSelected={isSelected} select={select} bindings={bindings}
- isTopMost={isTopMost} BackgroundView={BackgroundView} />`;
+ isTopMost={isTopMost} SelectOnLoad={selectOnLoad} BackgroundView={BackgroundView} focus={focus}/>`;
}
public active = () => {
var isSelected = this.props.isSelected();
@@ -49,6 +49,7 @@ export class CollectionView extends React.Component<CollectionViewProps> {
}
}
+
@action
removeDocument = (doc: Document): boolean => {
//TODO This won't create the field if it doesn't already exist
@@ -60,6 +61,7 @@ export class CollectionView extends React.Component<CollectionViewProps> {
break;
}
}
+
if (index !== -1) {
value.splice(index, 1)
diff --git a/src/client/views/collections/CollectionViewBase.tsx b/src/client/views/collections/CollectionViewBase.tsx
index 217536e2b..0a3b965f2 100644
--- a/src/client/views/collections/CollectionViewBase.tsx
+++ b/src/client/views/collections/CollectionViewBase.tsx
@@ -1,16 +1,16 @@
-import { action, computed } from "mobx";
+import { action } from "mobx";
import { Document } from "../../../fields/Document";
import { ListField } from "../../../fields/ListField";
import React = require("react");
import { KeyStore } from "../../../fields/KeyStore";
-import { Opt, FieldWaiting } from "../../../fields/Field";
+import { FieldWaiting } 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";
export interface CollectionViewProps {
fieldKey: Key;
@@ -22,12 +22,13 @@ export interface CollectionViewProps {
bindings: any;
panelWidth: () => number;
panelHeight: () => number;
+ focus: (doc: Document) => void;
}
export interface SubCollectionViewProps extends CollectionViewProps {
active: () => boolean;
addDocument: (doc: Document) => void;
removeDocument: (doc: Document) => boolean;
- CollectionView: any;
+ CollectionView: CollectionView;
}
export class CollectionViewBase extends React.Component<SubCollectionViewProps> {
@@ -67,7 +68,7 @@ export class CollectionViewBase extends React.Component<SubCollectionViewProps>
let html = e.dataTransfer.getData("text/html");
let text = e.dataTransfer.getData("text/plain");
if (html && html.indexOf("<img") != 0) {
- let htmlDoc = Documents.HtmlDocument(html, { ...options });
+ let htmlDoc = Documents.HtmlDocument(html, { ...options, width: 300, height: 300 });
htmlDoc.SetText(KeyStore.DocumentText, text);
this.props.addDocument(htmlDoc);
return;