aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorlaurawilsonri <laura_wilson@brown.edu>2019-02-25 20:11:37 -0500
committerlaurawilsonri <laura_wilson@brown.edu>2019-02-25 20:11:37 -0500
commit3ef178ab23eae665a7323ae54e14114a2b6e656e (patch)
tree5f97a342f4e1e8d322035158e53eaf4bd4774305
parent171852260c04ba7aafd789b231d98cdaa2a7dc8d (diff)
parent292ff1a8d75f8b15f9388d2c577e09a13836d5dc (diff)
Merge branch 'master' of https://github.com/browngraphicslab/Dash-Web into improveText
-rw-r--r--package-lock.json29
-rw-r--r--package.json4
-rw-r--r--src/client/documents/Documents.ts10
-rw-r--r--src/client/util/DragManager.ts22
-rw-r--r--src/client/util/Transform.ts6
-rw-r--r--src/client/views/DocumentDecorations.tsx23
-rw-r--r--src/client/views/EditableView.tsx2
-rw-r--r--src/client/views/Main.tsx117
-rw-r--r--src/client/views/collections/CollectionDockingView.scss4
-rw-r--r--src/client/views/collections/CollectionDockingView.tsx21
-rw-r--r--src/client/views/collections/CollectionFreeFormView.scss6
-rw-r--r--src/client/views/collections/CollectionFreeFormView.tsx84
-rw-r--r--src/client/views/collections/CollectionSchemaView.scss77
-rw-r--r--src/client/views/collections/CollectionSchemaView.tsx121
-rw-r--r--src/client/views/collections/CollectionTreeView.scss28
-rw-r--r--src/client/views/collections/CollectionTreeView.tsx104
-rw-r--r--src/client/views/collections/CollectionView.tsx32
-rw-r--r--src/client/views/collections/CollectionViewBase.tsx20
-rw-r--r--src/client/views/nodes/CollectionFreeFormDocumentView.tsx34
-rw-r--r--src/client/views/nodes/DocumentView.tsx7
-rw-r--r--src/client/views/nodes/FormattedTextBox.scss4
-rw-r--r--src/client/views/nodes/FormattedTextBox.tsx9
-rw-r--r--src/client/views/nodes/ImageBox.scss6
-rw-r--r--src/client/views/nodes/ImageBox.tsx13
-rw-r--r--src/debug/Viewer.tsx2
-rw-r--r--src/fields/Document.ts4
-rw-r--r--src/fields/Field.ts3
-rw-r--r--src/server/index.ts6
28 files changed, 532 insertions, 266 deletions
diff --git a/package-lock.json b/package-lock.json
index 30e9a3597..761af7f92 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -77,6 +77,11 @@
"@types/node": "*"
}
},
+ "@types/caseless": {
+ "version": "0.12.1",
+ "resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.1.tgz",
+ "integrity": "sha512-FhlMa34NHp9K5MY1Uz8yb+ZvuX0pnvn3jScRSNAb75KHGB8d3rEU6hqMs3Z2vjuytcMfRg6c5CHMc3wtYyD2/A=="
+ },
"@types/chai": {
"version": "4.1.7",
"resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.1.7.tgz",
@@ -144,6 +149,14 @@
"express-validator": "*"
}
},
+ "@types/form-data": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/@types/form-data/-/form-data-2.2.1.tgz",
+ "integrity": "sha512-JAMFhOaHIciYVh8fb5/83nmuO/AHwmto+Hq7a9y8FzLDcC1KCU344XDOMEmahnrTFlHjgh4L0WJFczNIX2GxnQ==",
+ "requires": {
+ "@types/node": "*"
+ }
+ },
"@types/jquery": {
"version": "3.3.29",
"resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-3.3.29.tgz",
@@ -367,6 +380,17 @@
"@types/react": "*"
}
},
+ "@types/request": {
+ "version": "2.48.1",
+ "resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.1.tgz",
+ "integrity": "sha512-ZgEZ1TiD+KGA9LiAAPPJL68Id2UWfeSO62ijSXZjFJArVV+2pKcsVHmrcu+1oiE3q6eDGiFiSolRc4JHoerBBg==",
+ "requires": {
+ "@types/caseless": "*",
+ "@types/form-data": "*",
+ "@types/node": "*",
+ "@types/tough-cookie": "*"
+ }
+ },
"@types/serve-static": {
"version": "1.13.2",
"resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.2.tgz",
@@ -399,6 +423,11 @@
"resolved": "https://registry.npmjs.org/@types/tapable/-/tapable-1.0.4.tgz",
"integrity": "sha512-78AdXtlhpCHT0K3EytMpn4JNxaf5tbqbLcbIRoQIHzpTIyjpxLQKRoxU55ujBXAtg3Nl2h/XWvfDa9dsMOd0pQ=="
},
+ "@types/tough-cookie": {
+ "version": "2.3.5",
+ "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-2.3.5.tgz",
+ "integrity": "sha512-SCcK7mvGi3+ZNz833RRjFIxrn4gI1PPR3NtuIS+6vMkvmsGjosqTJwRt5bAEFLRz+wtJMWv8+uOnZf2hi2QXTg=="
+ },
"@types/typescript": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@types/typescript/-/typescript-2.0.0.tgz",
diff --git a/package.json b/package.json
index eea6e0c94..bd4655816 100644
--- a/package.json
+++ b/package.json
@@ -59,6 +59,7 @@
"@types/pug": "^2.0.4",
"@types/react-measure": "^2.0.4",
"@types/react-table": "^6.7.21",
+ "@types/request": "^2.48.1",
"@types/socket.io": "^2.1.2",
"@types/socket.io-client": "^1.4.32",
"@types/typescript": "^2.0.0",
@@ -77,8 +78,8 @@
"font-awesome": "^4.7.0",
"golden-layout": "^1.5.9",
"jsonwebtoken": "^8.4.0",
- "lodash": "^4.17.11",
"jsx-to-string": "^1.4.0",
+ "lodash": "^4.17.11",
"mobx": "^5.9.0",
"mobx-react": "^5.3.5",
"mobx-react-devtools": "^6.0.3",
@@ -110,6 +111,7 @@
"react-mosaic": "0.0.20",
"react-split-pane": "^0.1.85",
"react-table": "^6.9.0",
+ "request": "^2.88.0",
"socket.io": "^2.2.0",
"socket.io-client": "^2.2.0",
"url-loader": "^1.1.2",
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 4c5f26fbd..15ecfbfe6 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -112,9 +112,7 @@ export namespace Documents {
imageProto.Set(KeyStore.X, new NumberField(0));
imageProto.Set(KeyStore.Y, new NumberField(0));
imageProto.Set(KeyStore.NativeWidth, new NumberField(300));
- imageProto.Set(KeyStore.NativeHeight, new NumberField(300));
imageProto.Set(KeyStore.Width, new NumberField(300));
- imageProto.Set(KeyStore.Height, new NumberField(300));
imageProto.Set(KeyStore.Layout, new TextField(CollectionView.LayoutString("AnnotationsKey")));
imageProto.SetNumber(KeyStore.ViewType, CollectionViewType.Freeform)
imageProto.Set(KeyStore.BackgroundLayout, new TextField(ImageBox.LayoutString()));
@@ -128,8 +126,8 @@ export namespace Documents {
// example of custom display string for an image that shows a caption.
function EmbeddedCaption() {
- return `<div style="position:absolute; height:100%">
- <div style="position:relative; margin:auto; width:85%; margin:auto" >`
+ return `<div style="height:100%">
+ <div style="position:relative; margin:auto; height:85%;" >`
+ ImageBox.LayoutString() +
`</div>
<div style="position:relative; overflow:auto; height:15%; text-align:center; ">`
@@ -151,9 +149,7 @@ export namespace Documents {
doc.Set(KeyStore.BackgroundLayout, new TextField(EmbeddedCaption()));
doc.Set(KeyStore.OverlayLayout, new TextField(FixedCaption()));
doc.Set(KeyStore.LayoutKeys, new ListField([KeyStore.Data, KeyStore.Annotations, KeyStore.Caption]));
-
- let annotation = Documents.TextDocument({ title: "hello" });
- doc.Set(KeyStore.Annotations, new ListField([annotation]));
+ console.log("" + doc.GetNumber(KeyStore.Height, 311));
return doc;
}
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index eb4b3aeaa..aab23f91c 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -61,7 +61,7 @@ export namespace DragManager {
};
}
- export function StartDrag(ele: HTMLElement, dragData: { [id: string]: any }, options: DragOptions) {
+ export function StartDrag(ele: HTMLElement, dragData: { [id: string]: any }, options?: DragOptions) {
DocumentDecorations.Instance.Hidden = true;
if (!dragDiv) {
dragDiv = document.createElement("div");
@@ -75,6 +75,8 @@ export namespace DragManager {
let dragElement = ele.cloneNode(true) as HTMLElement;
dragElement.style.opacity = "0.7";
dragElement.style.position = "absolute";
+ dragElement.style.bottom = "";
+ dragElement.style.left = "";
dragElement.style.transformOrigin = "0 0";
dragElement.style.zIndex = "1000";
dragElement.style.transform = `translate(${x}px, ${y}px) scale(${scaleX}, ${scaleY})`;
@@ -87,10 +89,12 @@ export namespace DragManager {
dragDiv.appendChild(dragElement);
let hideSource = false;
- if (typeof options.hideSource === "boolean") {
- hideSource = options.hideSource;
- } else {
- hideSource = options.hideSource();
+ if (options) {
+ if (typeof options.hideSource === "boolean") {
+ hideSource = options.hideSource;
+ } else {
+ hideSource = options.hideSource();
+ }
}
const wasHidden = ele.hidden;
if (hideSource) {
@@ -107,7 +111,7 @@ export namespace DragManager {
const upHandler = (e: PointerEvent) => {
document.removeEventListener("pointermove", moveHandler, true);
document.removeEventListener("pointerup", upHandler);
- FinishDrag(dragElement, e, options, dragData);
+ FinishDrag(dragElement, e, dragData, options);
if (hideSource && !wasHidden) {
ele.hidden = false;
}
@@ -116,7 +120,7 @@ export namespace DragManager {
document.addEventListener("pointerup", upHandler);
}
- function FinishDrag(dragEle: HTMLElement, e: PointerEvent, options: DragOptions, dragData: { [index: string]: any }) {
+ function FinishDrag(dragEle: HTMLElement, e: PointerEvent, dragData: { [index: string]: any }, options?: DragOptions) {
dragDiv.removeChild(dragEle);
const target = document.elementFromPoint(e.x, e.y);
if (!target) {
@@ -130,7 +134,9 @@ export namespace DragManager {
data: dragData
}
}));
- options.handlers.dragComplete({});
+ if (options) {
+ options.handlers.dragComplete({});
+ }
DocumentDecorations.Instance.Hidden = false;
}
} \ No newline at end of file
diff --git a/src/client/util/Transform.ts b/src/client/util/Transform.ts
index 9fd4f7bef..3e1039166 100644
--- a/src/client/util/Transform.ts
+++ b/src/client/util/Transform.ts
@@ -102,6 +102,12 @@ export class Transform {
return [x * this._scale, y * this._scale];
}
+ transformBounds(x: number, y: number, width: number, height: number): { x: number, y: number, width: number, height: number } {
+ [x, y] = this.transformPoint(x, y);
+ [width, height] = this.transformDirection(width, height);
+ return { x, y, width, height };
+ }
+
inverse = () => {
return new Transform(-this._translateX / this._scale, -this._translateY / this._scale, 1 / this._scale)
}
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 2f012913d..975a125f7 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -104,25 +104,26 @@ export class DocumentDecorations extends React.Component {
const rect = element.screenRect();
if (rect.width !== 0) {
let doc = element.props.Document;
- let width = doc.GetOrCreate(KeyStore.Width, NumberField);
- let height = doc.GetOrCreate(KeyStore.Height, NumberField);
+ let width = doc.GetNumber(KeyStore.Width, 0);
+ let nwidth = doc.GetNumber(KeyStore.NativeWidth, 0);
+ let nheight = doc.GetNumber(KeyStore.NativeHeight, 0);
+ let height = doc.GetNumber(KeyStore.Height, nwidth ? nheight / nwidth * width : 0);
let x = doc.GetOrCreate(KeyStore.X, NumberField);
let y = doc.GetOrCreate(KeyStore.Y, NumberField);
- let scale = width.Data / rect.width;
- let actualdW = Math.max(width.Data + (dW * scale), 20);
- let actualdH = Math.max(height.Data + (dH * scale), 20);
- x.Data += dX * (actualdW - width.Data);
- y.Data += dY * (actualdH - height.Data);
+ let scale = width / rect.width;
+ let actualdW = Math.max(width + (dW * scale), 20);
+ let actualdH = Math.max(height + (dH * scale), 20);
+ x.Data += dX * (actualdW - width);
+ y.Data += dY * (actualdH - height);
var nativeWidth = doc.GetNumber(KeyStore.NativeWidth, 0);
var nativeHeight = doc.GetNumber(KeyStore.NativeHeight, 0);
if (nativeWidth > 0 && nativeHeight > 0) {
if (Math.abs(dW) > Math.abs(dH))
actualdH = nativeHeight / nativeWidth * actualdW;
- else
- actualdW = nativeWidth / nativeHeight * actualdH;
+ else actualdW = nativeWidth / nativeHeight * actualdH;
}
- width.Data = actualdW;
- height.Data = actualdH;
+ doc.SetNumber(KeyStore.Width, actualdW);
+ doc.SetNumber(KeyStore.Height, actualdH);
}
})
}
diff --git a/src/client/views/EditableView.tsx b/src/client/views/EditableView.tsx
index 3d1c2ebf4..8d9a47eaa 100644
--- a/src/client/views/EditableView.tsx
+++ b/src/client/views/EditableView.tsx
@@ -30,7 +30,7 @@ export class EditableView extends React.Component<EditableProps> {
style={{ width: "100%" }}></input>
} else {
return (
- <div style={{ alignItems: "center", display: "flex", height: "100%", maxHeight: `${this.props.height}` }}
+ <div className="editableView-container-editing" style={{ display: "flex", height: "100%", maxHeight: `${this.props.height}` }}
onClick={action(() => this.editing = true)}
>
{this.props.contents}
diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index 661a2ac20..17dda899d 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -17,6 +17,7 @@ import { Transform } from '../util/Transform';
import { CollectionDockingView } from './collections/CollectionDockingView';
import { FieldWaiting } from '../../fields/Field';
import { UndoManager } from '../util/UndoManager';
+import { DragManager } from '../util/DragManager';
configure({
@@ -29,6 +30,7 @@ window.addEventListener("dragover", function (e) {
e.preventDefault();
}, false)
document.addEventListener("pointerdown", action(function (e: PointerEvent) {
+ console.log(ContextMenu);
if (!ContextMenu.Instance.intersects(e.pageX, e.pageY)) {
ContextMenu.Instance.clearItems()
}
@@ -77,31 +79,47 @@ Documents.initProtos(() => {
}, 0);
}
- let addImageNode = action(() => {
- mainfreeform.GetList<Document>(KeyStore.Data, []).push(Documents.ImageDocument("https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg", {
- x: 0, y: 300, width: 200, height: 200, title: "added note"
- }));
- })
- let addTextNode = action(() => {
- mainfreeform.GetList<Document>(KeyStore.Data, []).push(Documents.TextDocument({
- x: 0, y: 300, width: 200, height: 200, title: "added note"
- }));
- })
- let addColNode = action(() => {
- mainfreeform.GetList<Document>(KeyStore.Data, []).push(Documents.FreeformDocument([], {
- x: 0, y: 300, width: 200, height: 200, title: "added note"
- }));
- })
- let addSchemaNode = action(() => {
- mainfreeform.GetList<Document>(KeyStore.Data, []).push(Documents.SchemaDocument([Documents.TextDocument()], {
- x: 0, y: 300, width: 200, height: 200, title: "added note"
- }));
- })
+ let clearDatabase = action(() => Utils.Emit(Server.Socket, MessageStore.DeleteAll, {}))
+ let addTextNode = action(() => Documents.TextDocument({ width: 200, height: 200, title: "a text note" }))
+ let addColNode = action(() => Documents.FreeformDocument([], { width: 200, height: 200, title: "a feeform collection" }));
+ let addSchemaNode = action(() => Documents.SchemaDocument([Documents.TextDocument()], { width: 200, height: 200, title: "a schema collection" }));
+ let addImageNode = action(() => Documents.ImageDocument("https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg", {
+ width: 200, height: 200, title: "an image of a cat"
+ }));
- let clearDatabase = action(() => {
- Utils.Emit(Server.Socket, MessageStore.DeleteAll, {});
- })
+ let addClick = (creator: any) => action(() => {
+ var img = creator();
+ img.SetNumber(KeyStore.X, 0);
+ img.SetNumber(KeyStore.Y, 0);
+ mainfreeform.GetList<Document>(KeyStore.Data, []).push(img);
+ });
+ let imgRef = React.createRef<HTMLDivElement>();
+ let textRef = React.createRef<HTMLDivElement>();
+ let schemaRef = React.createRef<HTMLDivElement>();
+ let colRef = React.createRef<HTMLDivElement>();
+ let curMoveListener: any = null
+ let onRowMove = (creator: any, dragRef: any) => action((e: PointerEvent): void => {
+ e.stopPropagation();
+ e.preventDefault();
+
+ document.removeEventListener("pointermove", curMoveListener);
+ document.removeEventListener('pointerup', onRowUp);
+ DragManager.StartDrag(dragRef.current!, { document: creator() });
+ });
+ let onRowUp = action((e: PointerEvent): void => {
+ document.removeEventListener("pointermove", curMoveListener);
+ document.removeEventListener('pointerup', onRowUp);
+ });
+ let onRowDown = (creator: any, dragRef: any) => (e: React.PointerEvent) => {
+ if (e.shiftKey) {
+ CollectionDockingView.Instance.StartOtherDrag(dragRef.current!, creator());
+ e.stopPropagation();
+ } else {
+ document.addEventListener("pointermove", curMoveListener = onRowMove(creator, dragRef));
+ document.addEventListener('pointerup', onRowUp);
+ }
+ }
ReactDOM.render((
<div style={{ position: "absolute", width: "100%", height: "100%" }}>
<DocumentView Document={mainContainer}
@@ -113,48 +131,17 @@ Documents.initProtos(() => {
ContainingCollectionView={undefined} />
<DocumentDecorations />
<ContextMenu />
- <button style={{
- position: 'absolute',
- bottom: '0px',
- left: '0px',
- width: '150px'
- }} onClick={addImageNode}>Add Image</button>
- <button style={{
- position: 'absolute',
- bottom: '25px',
- left: '0px',
- width: '150px'
- }} onClick={addTextNode}>Add Text</button>
- <button style={{
- position: 'absolute',
- bottom: '50px',
- left: '0px',
- width: '150px'
- }} onClick={addColNode}>Add Collection</button>
- <button style={{
- position: 'absolute',
- bottom: '100',
- left: '0px',
- width: '150px'
- }} onClick={addSchemaNode}>Add Schema</button>
- <button style={{
- position: 'absolute',
- bottom: '75px',
- left: '0px',
- width: '150px'
- }} onClick={clearDatabase}>Clear Database</button>
- <button style={{
- position: 'absolute',
- bottom: '25',
- right: '0px',
- width: '150px'
- }} onClick={() => UndoManager.Undo()}>Undo</button>
- <button style={{
- position: 'absolute',
- bottom: '0',
- right: '0px',
- width: '150px'
- }} onClick={() => UndoManager.Redo()}>Redo</button>
+ <div style={{ position: 'absolute', bottom: '0px', left: '0px', width: '150px' }} ref={imgRef} >
+ <button onPointerDown={onRowDown(addImageNode, imgRef)} onClick={addClick(addImageNode)}>Add Image</button></div>
+ <div style={{ position: 'absolute', bottom: '25px', left: '0px', width: '150px' }} ref={textRef}>
+ <button onPointerDown={onRowDown(addTextNode, textRef)} onClick={addClick(addTextNode)}>Add Text</button></div>
+ <div style={{ position: 'absolute', bottom: '50px', left: '0px', width: '150px' }} ref={colRef}>
+ <button onPointerDown={onRowDown(addColNode, colRef)} onClick={addClick(addColNode)}>Add Collection</button></div>
+ <div style={{ position: 'absolute', bottom: '75px', left: '0px', width: '150px' }} ref={schemaRef}>
+ <button onPointerDown={onRowDown(addSchemaNode, schemaRef)} onClick={addClick(addSchemaNode)}>Add Schema</button></div>
+ <button style={{ position: 'absolute', bottom: '100px', left: '0px', width: '150px' }} onClick={clearDatabase}>Clear Database</button>
+ <button style={{ position: 'absolute', bottom: '25', right: '0px', width: '150px' }} onClick={() => UndoManager.Undo()}>Undo</button>
+ <button style={{ position: 'absolute', bottom: '0', right: '0px', width: '150px' }} onClick={() => UndoManager.Redo()}>Redo</button>
</div>),
document.getElementById('root'));
})
diff --git a/src/client/views/collections/CollectionDockingView.scss b/src/client/views/collections/CollectionDockingView.scss
index 7c0b512a7..2706c3272 100644
--- a/src/client/views/collections/CollectionDockingView.scss
+++ b/src/client/views/collections/CollectionDockingView.scss
@@ -1,3 +1,7 @@
+.collectiondockingview-content {
+ height: 100%;
+}
+
.collectiondockingview-container {
position: relative;
top: 0;
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 86dc66e39..5fb632469 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -36,8 +36,8 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
private _goldenLayout: any = null;
private _dragDiv: any = null;
private _dragParent: HTMLElement | null = null;
- private _dragElement: HTMLDivElement | undefined;
- private _dragFakeElement: HTMLDivElement | undefined;
+ private _dragElement: HTMLElement | undefined;
+ private _dragFakeElement: HTMLElement | undefined;
private _containerRef = React.createRef<HTMLDivElement>();
private _fullScreen: any = null;
@@ -48,7 +48,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
(window as any).ReactDOM = ReactDOM;
}
- public StartOtherDrag(dragElement: HTMLDivElement, dragDoc: Document) {
+ 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.
@@ -65,7 +65,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
// - 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 HTMLDivElement;
+ 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)
@@ -235,11 +235,11 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
//stack.header.controlsContainer.find('.lm_popout').hide();
stack.header.controlsContainer.find('.lm_close') //get the close icon
.off('click') //unbind the current click handler
- .click(function () {
+ .click(action(function () {
//if (confirm('really close this?')) {
stack.remove();
//}
- });
+ }));
}
render() {
@@ -265,6 +265,7 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
@observable private _mainCont = React.createRef<HTMLDivElement>();
@observable private _panelWidth = 0;
+ @observable private _panelHeight = 0;
@observable private _document: Opt<Document>;
constructor(props: any) {
@@ -272,8 +273,8 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
Server.GetField(this.props.documentId, action((f: Opt<Field>) => this._document = f as Document));
}
- private _nativeWidth = () => { return this._document!.GetNumber(KeyStore.NativeWidth, 0); }
- private _nativeHeight = () => { return this._document!.GetNumber(KeyStore.NativeHeight, 0); }
+ private _nativeWidth = () => { return this._document!.GetNumber(KeyStore.NativeWidth, this._panelWidth); }
+ private _nativeHeight = () => { return this._document!.GetNumber(KeyStore.NativeHeight, this._panelHeight); }
private _contentScaling = () => { return this._panelWidth / (this._nativeWidth() ? this._nativeWidth() : this._panelWidth); }
ScreenToLocalTransform = () => {
@@ -285,7 +286,7 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
if (!this._document)
return (null);
var content =
- <div ref={this._mainCont}>
+ <div className="collectionDockingView-content" ref={this._mainCont}>
<DocumentView key={this._document.Id} Document={this._document}
AddDocument={undefined}
RemoveDocument={undefined}
@@ -297,7 +298,7 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
ContainingCollectionView={undefined} />
</div>
- return <Measure onResize={action((r: any) => this._panelWidth = r.entry.width)}>
+ return <Measure onResize={action((r: any) => { this._panelWidth = r.entry.width; this._panelHeight = r.entry.height; })}>
{({ measureRef }) => <div ref={measureRef}> {content} </div>}
</Measure>
}
diff --git a/src/client/views/collections/CollectionFreeFormView.scss b/src/client/views/collections/CollectionFreeFormView.scss
index 49953a123..df9b1030c 100644
--- a/src/client/views/collections/CollectionFreeFormView.scss
+++ b/src/client/views/collections/CollectionFreeFormView.scss
@@ -8,6 +8,12 @@
border-radius: 5px;
background-color: rgba(0,0,0,.5);
}
+
+ .collectionfreeformview > .jsx-parser{
+ position:absolute;
+ height: 100%;
+ }
+
border-style: solid;
box-sizing: border-box;
position: relative;
diff --git a/src/client/views/collections/CollectionFreeFormView.tsx b/src/client/views/collections/CollectionFreeFormView.tsx
index 834aabd99..c2d2b0f7b 100644
--- a/src/client/views/collections/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/CollectionFreeFormView.tsx
@@ -12,6 +12,7 @@ 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";
@@ -21,8 +22,8 @@ import { ImageBox } from "../nodes/ImageBox";
import "./CollectionFreeFormView.scss";
import { COLLECTION_BORDER_WIDTH } from "./CollectionView";
import { CollectionViewBase } from "./CollectionViewBase";
-import React = require("react");
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?
@observer
@@ -49,21 +50,20 @@ export class CollectionFreeFormView extends CollectionViewBase {
@computed get nativeWidth() { return this.props.Document.GetNumber(KeyStore.NativeWidth, 0); }
@computed get nativeHeight() { return this.props.Document.GetNumber(KeyStore.NativeHeight, 0); }
@computed get zoomScaling() { return this.props.Document.GetNumber(KeyStore.Scale, 1); }
+ @computed get centeringShiftX() { return !this.props.Document.GetNumber(KeyStore.NativeWidth, 0) ? this.props.panelWidth() / 2 : 0; } // shift so pan position is at center of window for non-overlay collections
+ @computed get centeringShiftY() { return !this.props.Document.GetNumber(KeyStore.NativeHeight, 0) ? this.props.panelHeight() / 2 : 0; }// shift so pan position is at center of window for non-overlay collections
@undoBatch
@action
drop = (e: Event, de: DragManager.DropEvent) => {
super.drop(e, de);
- const doc: DocumentView = de.data["document"];
- const xOffset = de.data["xOffset"] as number || 0;
- const yOffset = de.data["yOffset"] as number || 0;
- //this should be able to use translate and scale methods on an Identity transform, no?
- const transform = this.getTransform();
- const screenX = de.x - xOffset;
- const screenY = de.y - yOffset;
- const [x, y] = transform.transformPoint(screenX, screenY);
- doc.props.Document.SetNumber(KeyStore.X, x);
- doc.props.Document.SetNumber(KeyStore.Y, y);
+ const docView: DocumentView = de.data["documentView"];
+ let doc: Document = docView ? docView.props.Document : de.data["document"];
+ 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);
}
@@ -119,35 +119,45 @@ export class CollectionFreeFormView extends CollectionViewBase {
e.stopPropagation();
e.preventDefault();
let coefficient = 1000;
- // if (modes[e.deltaMode] == 'pixels') coefficient = 50;
- // else if (modes[e.deltaMode] == 'lines') coefficient = 1000; // This should correspond to line-height??
- let transform = this.getTransform();
- let deltaScale = (1 - (e.deltaY / coefficient));
- let [x, y] = transform.transformPoint(e.clientX, e.clientY);
+ if (e.ctrlKey) {
+ var nativeWidth = this.props.Document.GetNumber(KeyStore.NativeWidth, 0);
+ var nativeHeight = this.props.Document.GetNumber(KeyStore.NativeHeight, 0);
+ const coefficient = 1000;
+ let deltaScale = (1 - (e.deltaY / coefficient));
+ this.props.Document.SetNumber(KeyStore.NativeWidth, nativeWidth * deltaScale);
+ this.props.Document.SetNumber(KeyStore.NativeHeight, nativeHeight * deltaScale);
+ e.stopPropagation();
+ e.preventDefault();
+ } else {
+ // if (modes[e.deltaMode] == 'pixels') coefficient = 50;
+ // else if (modes[e.deltaMode] == 'lines') coefficient = 1000; // This should correspond to line-height??
+ let transform = this.getTransform();
+
+ let deltaScale = (1 - (e.deltaY / coefficient));
+ if (deltaScale * this.zoomScaling < 1 && this.isAnnotationOverlay)
+ deltaScale = 1 / this.zoomScaling;
+ let [x, y] = transform.transformPoint(e.clientX, e.clientY);
- let localTransform = this.getLocalTransform();
- localTransform = localTransform.inverse().scaleAbout(deltaScale, x, y)
+ let localTransform = this.getLocalTransform();
+ localTransform = localTransform.inverse().scaleAbout(deltaScale, x, y)
- this.props.Document.SetNumber(KeyStore.Scale, localTransform.Scale);
- this.SetPan(localTransform.TranslateX, localTransform.TranslateY);
+ this.props.Document.SetNumber(KeyStore.Scale, localTransform.Scale);
+ this.SetPan(localTransform.TranslateX, localTransform.TranslateY);
+ }
}
@action
private SetPan(panX: number, panY: number) {
const newPanX = Math.max((1 - this.zoomScaling) * this.nativeWidth, Math.min(0, panX));
const newPanY = Math.max((1 - this.zoomScaling) * this.nativeHeight, Math.min(0, panY));
- this.props.Document.SetNumber(KeyStore.PanX, false && this.isAnnotationOverlay ? newPanX : panX);
- this.props.Document.SetNumber(KeyStore.PanY, false && this.isAnnotationOverlay ? newPanY : panY);
+ this.props.Document.SetNumber(KeyStore.PanX, this.isAnnotationOverlay ? newPanX : panX);
+ this.props.Document.SetNumber(KeyStore.PanY, this.isAnnotationOverlay ? newPanY : panY);
}
@action
onDrop = (e: React.DragEvent): void => {
- const panx: number = this.props.Document.GetNumber(KeyStore.PanX, 0);
- const pany: number = this.props.Document.GetNumber(KeyStore.PanY, 0);
- let transform = this.getTransform();
-
- var pt = transform.transformPoint(e.pageX, e.pageY);
+ var pt = this.getTransform().transformPoint(e.pageX, e.pageY);
super.onDrop(e, { x: pt[0], y: pt[1] });
}
@@ -175,15 +185,15 @@ export class CollectionFreeFormView extends CollectionViewBase {
}
@action
- bringToFront(doc: DocumentView) {
+ bringToFront(doc: Document) {
const { fieldKey: fieldKey, Document: Document } = this.props;
const value: Document[] = Document.GetList<Document>(fieldKey, []).slice();
value.sort((doc1, doc2) => {
- if (doc1 === doc.props.Document) {
+ if (doc1 === doc) {
return 1;
}
- if (doc2 === doc.props.Document) {
+ if (doc2 === doc) {
return -1;
}
return doc1.GetNumber(KeyStore.ZIndex, 0) - doc2.GetNumber(KeyStore.ZIndex, 0);
@@ -247,7 +257,8 @@ export class CollectionFreeFormView extends CollectionViewBase {
onError={(test: any) => console.log(test)}
/>);
}
- getTransform = (): Transform => this.props.ScreenToLocalTransform().translate(-COLLECTION_BORDER_WIDTH, -COLLECTION_BORDER_WIDTH).transform(this.getLocalTransform())
+
+ 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);
noScaling = () => 1;
@@ -267,14 +278,9 @@ export class CollectionFreeFormView extends CollectionViewBase {
cursor = <div id="prevCursor" onKeyPress={this.onKeyDown} style={{ color: "black", position: "absolute", transformOrigin: "left top", transform: `translate(${x}px, ${y}px)` }}>I</div>
}
- const panx: number = this.props.Document.GetNumber(KeyStore.PanX, 0);
- const pany: number = this.props.Document.GetNumber(KeyStore.PanY, 0);
- var overlay = this.overlayView ?
- <div style={{ position: "absolute", width: "100%", height: "100%" }}>
- {this.overlayView}
- </div>
- :
- (null);
+ const panx: number = this.props.Document.GetNumber(KeyStore.PanX, 0) + this.centeringShiftX;
+ const pany: number = this.props.Document.GetNumber(KeyStore.PanY, 0) + this.centeringShiftY;
+
return (
<div className="collectionfreeformview-container"
onPointerDown={this.onPointerDown}
diff --git a/src/client/views/collections/CollectionSchemaView.scss b/src/client/views/collections/CollectionSchemaView.scss
index ba9afee62..0bd5a2ed3 100644
--- a/src/client/views/collections/CollectionSchemaView.scss
+++ b/src/client/views/collections/CollectionSchemaView.scss
@@ -14,16 +14,6 @@
background-color: rgba(0,0,0,.5);
}
- .collectionfreeformview-container {
- border-width: 0px;
- .collectionfreeformview > .jsx-parser{
- position:absolute
- }
- }
- .imageBox-cont {
- position:relative;
- max-height:100%;
- }
.ReactTable {
position: absolute;
// display: inline-block;
@@ -32,53 +22,58 @@
height: 100%;
background: white;
box-sizing: border-box;
- }
- .ReactTable .rt-table {
- overflow-y: auto;
- overflow-x: auto;
- height: 100%;
+ .rt-table {
+ overflow-y: auto;
+ overflow-x: auto;
+ height: 100%;
- display: -webkit-inline-box;
- direction: ltr;
- // direction:rtl;
- // display:block;
- }
- .ReactTable .rt-tbody {
- //direction: ltr;
- direction: rtl;
- }
- .ReactTable .rt-tr-group {
- direction: ltr;
+ display: -webkit-inline-box;
+ direction: ltr;
+ // direction:rtl;
+ // display:block;
+ }
+ .rt-tbody {
+ //direction: ltr;
+ direction: rtl;
+ }
+ .rt-tr-group {
+ direction: ltr;
+ max-height: 44px;
+ }
+ .rt-td {
+ border-width: 1;
+ border-right-color: #aaa;
+ .imageBox-cont {
+ position:relative;
+ max-height:100%;
+ }
+ .imageBox-cont img {
+ object-fit: contain;
+ max-width: 100%;
+ height: 100%
+ }
+ }
+ .rt-tr-group {
+ border-width: 1;
+ border-bottom-color: #aaa
+ }
}
.ReactTable .rt-thead.-header {
background:grey;
}
.ReactTable .rt-th, .ReactTable .rt-td {
- max-height: 75px;
+ max-height: 44;
+ padding: 3px 7px;
}
.ReactTable .rt-tbody .rt-tr-group:last-child {
border-bottom: grey;
border-bottom-style: solid;
border-bottom-width: 1;
}
- .ReactTable .rt-td {
- border-width: 1;
- border-right-color: #aaa
- }
- .ReactTable .rt-tr-group {
- border-width: 1;
- border-bottom-color: #aaa
- }
- .imageBox-cont img {
- object-fit: contain;
- height: 100%
- }
.documentView-node:first-child {
background: grey;
.imageBox-cont img {
object-fit: contain;
- max-width: 100%;
- height: 100%
}
}
}
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index d2db93120..38217d7c4 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -15,6 +15,10 @@ 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";
+
+// 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 {
@@ -28,6 +32,9 @@ export class CollectionSchemaView extends CollectionViewBase {
@observable _selectedIndex = 0;
@observable _splitPercentage: number = 50;
+
+
+
renderCell = (rowProps: CellInfo) => {
let props: FieldViewProps = {
doc: rowProps.value[0],
@@ -40,31 +47,57 @@ export class CollectionSchemaView extends CollectionViewBase {
let contents = (
<FieldView {...props} />
)
- return (
- <EditableView contents={contents} height={36} GetValue={() => {
- let field = props.doc.Get(props.fieldKey);
- if (field && field instanceof Field) {
- return field.ToScriptString();
- }
- return field || "";
- }} SetValue={(value: string) => {
- let script = CompileScript(value);
- if (!script.compiled) {
- return false;
- }
- let field = script();
- if (field instanceof Field) {
- props.doc.Set(props.fieldKey, field);
- return true;
+ 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 {
- let dataField = ToField(field);
- if (dataField) {
- props.doc.Set(props.fieldKey, dataField);
- return true;
- }
+ document.addEventListener("pointermove", onRowMove);
+ document.addEventListener('pointerup', onRowUp);
}
- return false;
- }}></EditableView>
+ }
+ }
+ return (
+ <div onPointerDown={onRowDown} ref={reference}>
+ <EditableView contents={contents}
+ height={36} GetValue={() => {
+ let field = props.doc.Get(props.fieldKey);
+ if (field && field instanceof Field) {
+ return field.ToScriptString();
+ }
+ return field || "";
+ }} SetValue={(value: string) => {
+ let script = CompileScript(value);
+ if (!script.compiled) {
+ return false;
+ }
+ let field = script();
+ if (field instanceof Field) {
+ props.doc.Set(props.fieldKey, field);
+ return true;
+ } else {
+ let dataField = ToField(field);
+ if (dataField) {
+ props.doc.Set(props.fieldKey, dataField);
+ return true;
+ }
+ }
+ return false;
+ }}></EditableView></div>
)
}
@@ -89,21 +122,49 @@ export class CollectionSchemaView extends CollectionViewBase {
};
}
+ _startSplitPercent = 0;
@action
onDividerMove = (e: PointerEvent): void => {
let nativeWidth = this._mainCont.current!.getBoundingClientRect();
this._splitPercentage = 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;
+ }
}
onDividerDown = (e: React.PointerEvent) => {
+ this._startSplitPercent = this._splitPercentage;
e.stopPropagation();
e.preventDefault();
document.addEventListener("pointermove", this.onDividerMove);
document.addEventListener('pointerup', this.onDividerUp);
}
+ @action
+ onExpanderMove = (e: PointerEvent): void => {
+ e.stopPropagation();
+ e.preventDefault();
+ }
+ @action
+ onExpanderUp = (e: PointerEvent): void => {
+ e.stopPropagation();
+ e.preventDefault();
+ document.removeEventListener("pointermove", this.onExpanderMove);
+ document.removeEventListener('pointerup', this.onExpanderUp);
+ if (this._startSplitPercent == this._splitPercentage) {
+ this._splitPercentage = this._splitPercentage == 100 ? 66 : 100;
+ }
+ }
+ onExpanderDown = (e: React.PointerEvent) => {
+ this._startSplitPercent = this._splitPercentage;
+ e.stopPropagation();
+ e.preventDefault();
+ document.addEventListener("pointermove", this.onExpanderMove);
+ document.addEventListener('pointerup', this.onExpanderUp);
+ }
onPointerDown = (e: React.PointerEvent) => {
// if (e.button === 2 && this.active) {
@@ -111,8 +172,10 @@ export class CollectionSchemaView extends CollectionViewBase {
// e.preventDefault();
// } else
{
- if (e.buttons === 1 && this.props.active()) {
- e.stopPropagation();
+ if (e.buttons === 1) {
+ if (this.props.isSelected()) {
+ e.stopPropagation();
+ }
}
}
}
@@ -140,7 +203,7 @@ export class CollectionSchemaView extends CollectionViewBase {
let content = this._selectedIndex == -1 || !selected ? (null) : (
<Measure onResize={this.setScaling}>
{({ measureRef }) =>
- <div ref={measureRef}>
+ <div className="collectionSchemaView-content" ref={measureRef}>
<DocumentView Document={selected}
AddDocument={this.props.addDocument} RemoveDocument={this.props.removeDocument}
isTopMost={false}
@@ -153,6 +216,8 @@ export class CollectionSchemaView extends CollectionViewBase {
}
</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} />);
return (
<div onPointerDown={this.onPointerDown} ref={this._mainCont} className="collectionSchemaView-container" style={{ borderWidth: `${COLLECTION_BORDER_WIDTH}px` }} >
<Measure onResize={action((r: any) => {
@@ -173,7 +238,8 @@ export class CollectionSchemaView extends CollectionViewBase {
}))}
column={{
...ReactTableDefaults.column,
- Cell: this.renderCell
+ Cell: this.renderCell,
+
}}
getTrProps={this.getTrProps}
/>
@@ -187,6 +253,7 @@ export class CollectionSchemaView extends CollectionViewBase {
style={{ position: "relative", float: "left", width: `calc(${100 - this._splitPercentage}% - ${this.DIVIDER_WIDTH}px)`, height: "100%" }}>
{content}
</div>
+ {handle}
</div >
)
}
diff --git a/src/client/views/collections/CollectionTreeView.scss b/src/client/views/collections/CollectionTreeView.scss
new file mode 100644
index 000000000..675fc6c53
--- /dev/null
+++ b/src/client/views/collections/CollectionTreeView.scss
@@ -0,0 +1,28 @@
+ul {
+ list-style: none;
+}
+
+li {
+ margin: 5px 0;
+}
+
+.no-indent {
+ padding-left: 0;
+}
+
+/* ALL THESE SPACINGS ARE SUPER HACKY RIGHT NOW HANNAH PLS HELP */
+
+li:before {
+ content: '\2014';
+ margin-right: 0.7em;
+}
+
+.collapsed:before {
+ content: '\25b6';
+ margin-right: 0.65em;
+}
+
+.uncollapsed:before {
+ content: '\25bc';
+ margin-right: 0.5em;
+} \ No newline at end of file
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
new file mode 100644
index 000000000..52e853bf7
--- /dev/null
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -0,0 +1,104 @@
+import { observer } from "mobx-react";
+import { CollectionViewBase } from "./CollectionViewBase";
+import { Document } from "../../../fields/Document";
+import { KeyStore } from "../../../fields/KeyStore";
+import { ListField } from "../../../fields/ListField";
+import React = require("react")
+import { TextField } from "../../../fields/TextField";
+import { observable, action } from "mobx";
+import "./CollectionTreeView.scss";
+
+export interface TreeViewProps {
+ document: Document;
+}
+
+@observer
+/**
+ * Component that takes in a document prop and a boolean whether it's collapsed or not.
+ */
+class TreeView extends React.Component<TreeViewProps> {
+
+ @observable
+ collapsed: boolean = false;
+
+ /**
+ * 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.
+ */
+ renderChild(document: Document) {
+ var children = document.GetT<ListField<Document>>(KeyStore.Data, ListField);
+ let title = 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>;
+ }
+
+ // otherwise, check if it's a collection.
+ else if (children && children !== "<Waiting>") {
+ // if it's not collapsed, then render the full TreeView.
+ if (!this.collapsed) {
+ return (
+ <li className="uncollapsed" key={document.Id} onClick={action(() => this.collapsed = true)} >
+ {title.Data}
+ <ul key={document.Id}>
+ <TreeView
+ document={document}
+ />
+ </ul>
+ </li>
+ );
+ } else {
+ return <li className="collapsed" key={document.Id} onClick={action(() => this.collapsed = false)}>{title.Data}</li>
+ }
+ }
+
+ // finally, if it's a normal document, then render it as such.
+ else {
+ return <li key={document.Id}>{title.Data}</li>;
+ }
+ }
+
+ 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>;
+ }
+ }
+}
+
+
+@observer
+export class CollectionTreeView extends CollectionViewBase {
+
+ render() {
+ let titleStr = "";
+ let title = this.props.Document.GetT<TextField>(KeyStore.Title, TextField);
+ if (title && title !== "<Waiting>") {
+ titleStr = title.Data;
+ }
+ return (
+ <div>
+ <h3>{titleStr}</h3>
+ <ul className="no-indent">
+ <TreeView
+ document={this.props.Document}
+ />
+ </ul>
+ </div>
+ );
+ }
+} \ No newline at end of file
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 57d876996..6ab0f15c0 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -12,14 +12,15 @@ import { CollectionDockingView } from "./CollectionDockingView";
import { CollectionSchemaView } from "./CollectionSchemaView";
import { CollectionViewProps } from "./CollectionViewBase";
var ReactDOM = require('react-dom');
-
-
+import { CollectionTreeView } from "./CollectionTreeView";
+import { Field } from "../../../fields/Field";
export enum CollectionViewType {
Invalid,
Freeform,
Schema,
Docking,
+ Tree
}
export const COLLECTION_BORDER_WIDTH = 2;
@@ -31,7 +32,7 @@ export class CollectionView extends React.Component<CollectionViewProps> {
public static LayoutString(fieldKey: string = "DataKey") {
return `<CollectionView Document={Document}
- ScreenToLocalTransform={ScreenToLocalTransform} fieldKey={${fieldKey}} isSelected={isSelected} select={select} bindings={bindings}
+ ScreenToLocalTransform={ScreenToLocalTransform} fieldKey={${fieldKey}} panelWidth={PanelWidth} panelHeight={PanelHeight} isSelected={isSelected} select={select} bindings={bindings}
isTopMost={isTopMost} BackgroundView={BackgroundView} />`;
}
public active = () => {
@@ -42,16 +43,26 @@ export class CollectionView extends React.Component<CollectionViewProps> {
}
@action
addDocument = (doc: Document): void => {
- //TODO This won't create the field if it doesn't already exist
- const value = this.props.Document.GetData(this.props.fieldKey, ListField, new Array<Document>())
- value.push(doc);
+ if (this.props.Document.Get(this.props.fieldKey) instanceof Field) {
+ //TODO This won't create the field if it doesn't already exist
+ const value = this.props.Document.GetData(this.props.fieldKey, ListField, new Array<Document>())
+ value.push(doc);
+ } else {
+ this.props.Document.SetData(this.props.fieldKey, [doc], ListField);
+ }
}
@action
removeDocument = (doc: Document): boolean => {
//TODO This won't create the field if it doesn't already exist
const value = this.props.Document.GetData(this.props.fieldKey, ListField, new Array<Document>())
- let index = value.indexOf(doc);
+ let index = -1;
+ for (let i = 0; i < value.length; i++) {
+ if (value[i].Id == doc.Id) {
+ index = i;
+ break;
+ }
+ }
if (index !== -1) {
value.splice(index, 1)
@@ -97,11 +108,12 @@ export class CollectionView extends React.Component<CollectionViewProps> {
render() {
let viewType = this.collectionViewType;
+
switch (viewType) {
case CollectionViewType.Freeform:
return (<CollectionFreeFormView {...this.props}
addDocument={this.addDocument} removeDocument={this.removeDocument} active={this.active}
- CollectionView={this} />)
+ CollectionView={this} />);
case CollectionViewType.Schema:
return (<CollectionSchemaView {...this.props}
addDocument={this.addDocument} removeDocument={this.removeDocument} active={this.active}
@@ -110,6 +122,10 @@ export class CollectionView extends React.Component<CollectionViewProps> {
return (<CollectionDockingView {...this.props}
addDocument={this.addDocument} removeDocument={this.removeDocument} active={this.active}
CollectionView={this} />)
+ case CollectionViewType.Tree:
+ return (<CollectionTreeView {...this.props}
+ addDocument={this.addDocument} removeDocument={this.removeDocument} active={this.active}
+ CollectionView={this} />)
default:
return <div></div>
}
diff --git a/src/client/views/collections/CollectionViewBase.tsx b/src/client/views/collections/CollectionViewBase.tsx
index 7e269caf1..217536e2b 100644
--- a/src/client/views/collections/CollectionViewBase.tsx
+++ b/src/client/views/collections/CollectionViewBase.tsx
@@ -20,6 +20,8 @@ export interface CollectionViewProps {
isTopMost: boolean;
select: (ctrlPressed: boolean) => void;
bindings: any;
+ panelWidth: () => number;
+ panelHeight: () => number;
}
export interface SubCollectionViewProps extends CollectionViewProps {
active: () => boolean;
@@ -42,12 +44,16 @@ export class CollectionViewBase extends React.Component<SubCollectionViewProps>
@undoBatch
@action
protected drop(e: Event, de: DragManager.DropEvent) {
- const doc: DocumentView = de.data["document"];
- if (doc.props.ContainingCollectionView && doc.props.ContainingCollectionView !== this.props.CollectionView) {
- if (doc.props.RemoveDocument) {
- doc.props.RemoveDocument(doc.props.Document);
+ 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);
}
- this.props.addDocument(doc.props.Document);
+ this.props.addDocument(docView.props.Document);
+ } else if (doc) {
+ this.props.removeDocument(doc);
+ this.props.addDocument(doc);
}
e.stopPropagation();
}
@@ -60,7 +66,7 @@ export class CollectionViewBase extends React.Component<SubCollectionViewProps>
let html = e.dataTransfer.getData("text/html");
let text = e.dataTransfer.getData("text/plain");
- if (html) {
+ if (html && html.indexOf("<img") != 0) {
let htmlDoc = Documents.HtmlDocument(html, { ...options });
htmlDoc.SetText(KeyStore.DocumentText, text);
this.props.addDocument(htmlDoc);
@@ -72,7 +78,7 @@ export class CollectionViewBase extends React.Component<SubCollectionViewProps>
if (item.kind === "string" && item.type.indexOf("uri") != -1) {
e.dataTransfer.items[i].getAsString(function (s) {
action(() => {
- var img = Documents.ImageDocument(s, { ...options, nativeWidth: 300, nativeHeight: 300, width: 300, height: 300 })
+ var img = Documents.ImageDocument(s, { ...options, nativeWidth: 300, width: 300, })
let docs = that.props.Document.GetT(KeyStore.Data, ListField);
if (docs != FieldWaiting) {
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 17123bf52..50dc5a619 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -27,44 +27,26 @@ export class CollectionFreeFormDocumentView extends React.Component<DocumentView
return `scale(${this.props.ContentScaling()}, ${this.props.ContentScaling()}) translate(${this.props.Document.GetNumber(KeyStore.X, 0)}px, ${this.props.Document.GetNumber(KeyStore.Y, 0)}px)`;
}
- @computed
- get width(): number {
- return this.props.Document.GetNumber(KeyStore.Width, 0);
- }
-
- @computed
- get nativeWidth(): number {
- return this.props.Document.GetNumber(KeyStore.NativeWidth, 0);
- }
+ @computed get zIndex(): number { return this.props.Document.GetNumber(KeyStore.ZIndex, 0); }
+ @computed get width(): number { return this.props.Document.Width(); }
+ @computed get height(): number { return this.props.Document.Height(); }
+ @computed get nativeWidth(): number { return this.props.Document.GetNumber(KeyStore.NativeWidth, 0); }
+ @computed get nativeHeight(): number { return this.props.Document.GetNumber(KeyStore.NativeHeight, 0); }
set width(w: number) {
this.props.Document.SetData(KeyStore.Width, w, NumberField)
- if (this.nativeWidth > 0 && this.nativeHeight > 0) {
+ if (this.nativeWidth && this.nativeHeight) {
this.props.Document.SetNumber(KeyStore.Height, this.nativeHeight / this.nativeWidth * w)
}
}
- @computed
- get height(): number {
- return this.props.Document.GetNumber(KeyStore.Height, 0);
- }
- @computed
- get nativeHeight(): number {
- return this.props.Document.GetNumber(KeyStore.NativeHeight, 0);
- }
-
set height(h: number) {
this.props.Document.SetData(KeyStore.Height, h, NumberField);
- if (this.nativeWidth > 0 && this.nativeHeight > 0) {
+ if (this.nativeWidth && this.nativeHeight) {
this.props.Document.SetNumber(KeyStore.Width, this.nativeWidth / this.nativeHeight * h)
}
}
- @computed
- get zIndex(): number {
- return this.props.Document.GetNumber(KeyStore.ZIndex, 0);
- }
-
set zIndex(h: number) {
this.props.Document.SetData(KeyStore.ZIndex, h, NumberField)
}
@@ -88,7 +70,7 @@ export class CollectionFreeFormDocumentView extends React.Component<DocumentView
render() {
return (
- <div ref={this._mainCont} style={{
+ <div className="collectionFreeFormDocumentView-container" ref={this._mainCont} style={{
transformOrigin: "left top",
transform: this.transform,
width: this.width,
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index bb6c8d13a..a7632b008 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -128,7 +128,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
this._contextMenuCanOpen = false;
const [left, top] = this.props.ScreenToLocalTransform().inverse().transformPoint(0, 0);
let dragData: { [id: string]: any } = {};
- dragData["document"] = this;
+ dragData["documentView"] = this;
dragData["xOffset"] = e.x - left;
dragData["yOffset"] = e.y - top;
DragManager.StartDrag(this._mainCont.current, dragData, {
@@ -193,6 +193,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
ContextMenu.Instance.addItem({ description: "Delete", event: this.deleteClicked })
ContextMenu.Instance.addItem({ description: "Freeform", event: () => this.props.Document.SetNumber(KeyStore.ViewType, CollectionViewType.Freeform) })
ContextMenu.Instance.addItem({ description: "Schema", event: () => this.props.Document.SetNumber(KeyStore.ViewType, CollectionViewType.Schema) })
+ ContextMenu.Instance.addItem({ description: "Treeview", event: () => this.props.Document.SetNumber(KeyStore.ViewType, CollectionViewType.Tree) })
ContextMenu.Instance.addItem({ description: "Docking", event: () => this.props.Document.SetNumber(KeyStore.ViewType, CollectionViewType.Docking) })
ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15)
SelectionManager.SelectDoc(this, e.ctrlKey);
@@ -200,6 +201,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
}
@computed get mainContent() {
+ var val = this.props.Document.Id;
return <JsxParser
components={{ FormattedTextBox, ImageBox, CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, WebView }}
bindings={this._documentBindings}
@@ -242,7 +244,8 @@ export class DocumentView extends React.Component<DocumentViewProps> {
transform: `scale(${scaling},${scaling})`
}}
onContextMenu={this.onContextMenu}
- onPointerDown={this.onPointerDown} >
+ onPointerDown={this.onPointerDown}
+ >
{this.mainContent}
</div>
)
diff --git a/src/client/views/nodes/FormattedTextBox.scss b/src/client/views/nodes/FormattedTextBox.scss
index 97d7f1426..10cbf8557 100644
--- a/src/client/views/nodes/FormattedTextBox.scss
+++ b/src/client/views/nodes/FormattedTextBox.scss
@@ -11,4 +11,8 @@
.formattedTextBox-cont {
background: white;
padding: 1vw;
+ overflow-y: scroll;
+ overflow-x: hidden;
+ color: initial;
+ height: 100%;
} \ No newline at end of file
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index 640c9b208..63d00a310 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -143,14 +143,13 @@ export class FormattedTextBox extends React.Component<FieldViewProps> {
e.stopPropagation();
}
}
+ onPointerWheel = (e: React.WheelEvent): void => {
+ e.stopPropagation();
+ }
render() {
return (<div className="formattedTextBox-cont"
- style={{
- color: "initial",
- whiteSpace: "initial",
- height: "auto"
- }}
onPointerDown={this.onPointerDown}
+ onWheel={this.onPointerWheel}
ref={this._ref} />)
}
} \ No newline at end of file
diff --git a/src/client/views/nodes/ImageBox.scss b/src/client/views/nodes/ImageBox.scss
index 5b15b3329..ea459b911 100644
--- a/src/client/views/nodes/ImageBox.scss
+++ b/src/client/views/nodes/ImageBox.scss
@@ -4,12 +4,14 @@
position: relative;
text-align: center;
width: 100%;
+ height: auto;
max-width: 100%;
max-height: 100%
}
+
.imageBox-cont img {
- max-width: 100%;
- max-height: 100%
+ object-fit: contain;
+ height: 100%;
}
.imageBox-button {
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index b5ce8b28c..4fe73fb8d 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -9,12 +9,14 @@ import { FieldWaiting } from '../../../fields/Field';
import { observer } from "mobx-react"
import { observable, action } from 'mobx';
import { KeyStore } from '../../../fields/KeyStore';
+import { element } from 'prop-types';
@observer
export class ImageBox extends React.Component<FieldViewProps> {
public static LayoutString() { return FieldView.LayoutString(ImageBox) }
private _ref: React.RefObject<HTMLDivElement>;
+ private _imgRef: React.RefObject<HTMLImageElement>;
private _downX: number = 0;
private _downY: number = 0;
private _lastTap: number = 0;
@@ -25,12 +27,20 @@ export class ImageBox extends React.Component<FieldViewProps> {
super(props);
this._ref = React.createRef();
+ this._imgRef = React.createRef();
this.state = {
photoIndex: 0,
isOpen: false,
};
}
+ @action
+ onLoad = (target: any) => {
+ var h = this._imgRef.current!.naturalHeight;
+ var w = this._imgRef.current!.naturalWidth;
+ this.props.doc.SetNumber(KeyStore.NativeHeight, this.props.doc.GetNumber(KeyStore.NativeWidth, 0) * h / w)
+ }
+
componentDidMount() {
}
@@ -84,10 +94,9 @@ export class ImageBox extends React.Component<FieldViewProps> {
let path = field == FieldWaiting ? "https://image.flaticon.com/icons/svg/66/66163.svg" :
field instanceof ImageField ? field.Data.href : "http://www.cs.brown.edu/~bcz/face.gif";
let nativeWidth = this.props.doc.GetNumber(KeyStore.NativeWidth, 1);
-
return (
<div className="imageBox-cont" onPointerDown={this.onPointerDown} ref={this._ref} >
- <img src={path} width={nativeWidth} alt="Image not found" />
+ <img src={path} width={nativeWidth} alt="Image not found" ref={this._imgRef} onLoad={this.onLoad} />
{this.lightbox(path)}
</div>)
}
diff --git a/src/debug/Viewer.tsx b/src/debug/Viewer.tsx
index aff77fca3..780e9f8f2 100644
--- a/src/debug/Viewer.tsx
+++ b/src/debug/Viewer.tsx
@@ -17,7 +17,7 @@ configure({
@observer
class FieldViewer extends React.Component<{ field: BasicField<any> }> {
render() {
- return <span>{this.props.field.Data} ({this.props.field.Id})</span>;
+ return <span>{JSON.stringify(this.props.field.Data)} ({this.props.field.Id})</span>;
}
}
diff --git a/src/fields/Document.ts b/src/fields/Document.ts
index 0d7d357a0..6193ea56c 100644
--- a/src/fields/Document.ts
+++ b/src/fields/Document.ts
@@ -17,8 +17,6 @@ export class Document extends Field {
super(id)
if (save) {
- var title = (this._proxies.has(KeyStore.Title.Id) ? "???" : this.Title) + "(" + this.Id + ")";
- console.log("Save " + title);
Server.UpdateField(this)
}
}
@@ -31,7 +29,7 @@ export class Document extends Field {
}
public Width = () => { return this.GetNumber(KeyStore.Width, 0) }
- public Height = () => { return this.GetNumber(KeyStore.Height, 0) }
+ public Height = () => { return this.GetNumber(KeyStore.Height, this.GetNumber(KeyStore.NativeWidth, 0) ? this.GetNumber(KeyStore.NativeHeight, 0) / this.GetNumber(KeyStore.NativeWidth, 0) * this.GetNumber(KeyStore.Width, 0) : 0) }
public Scale = () => { return this.GetNumber(KeyStore.Scale, 1) }
@computed
diff --git a/src/fields/Field.ts b/src/fields/Field.ts
index c7e0232af..d48509a47 100644
--- a/src/fields/Field.ts
+++ b/src/fields/Field.ts
@@ -1,6 +1,7 @@
import { Utils } from "../Utils";
import { Types } from "../server/Message";
+import { computed } from "mobx";
export function Cast<T extends Field>(field: FieldValue<Field>, ctor: { new(): T }): Opt<T> {
if (field) {
@@ -25,6 +26,8 @@ export abstract class Field {
}
private id: FieldId;
+
+ @computed
get Id(): FieldId {
return this.id;
}
diff --git a/src/server/index.ts b/src/server/index.ts
index f5e66b31b..eb0527ee7 100644
--- a/src/server/index.ts
+++ b/src/server/index.ts
@@ -31,6 +31,12 @@ const MongoStore = require('connect-mongo')(session);
const mongoose = require('mongoose');
const bluebird = require('bluebird');
import { performance } from 'perf_hooks'
+import * as fs from 'fs';
+import * as request from 'request'
+
+const download = (url: string, dest: fs.PathLike) => {
+ request.get(url).pipe(fs.createWriteStream(dest));
+}
const mongoUrl = 'mongodb://localhost:27017/Dash';
// mongoose.Promise = bluebird;