aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbob <bcz@cs.brown.edu>2019-05-02 12:19:31 -0400
committerbob <bcz@cs.brown.edu>2019-05-02 12:19:31 -0400
commit5f8f133040918713ace577cfe82f38254ea07964 (patch)
tree3955761d53b2df4138ecd2771ab978861bd38854
parente8c14f1d0a301d6bb5ca2ce0e048fb4fb3f7728f (diff)
library and workspace stuff.
-rw-r--r--src/client/views/Main.tsx28
-rw-r--r--src/client/views/TemplateMenu.tsx7
-rw-r--r--src/client/views/collections/CollectionDockingView.tsx27
-rw-r--r--src/client/views/collections/CollectionTreeView.scss1
-rw-r--r--src/client/views/collections/CollectionTreeView.tsx57
-rw-r--r--src/client/views/collections/CollectionView.tsx1
-rw-r--r--src/client/views/nodes/DocumentView.tsx10
-rw-r--r--src/server/authentication/controllers/WorkspacesMenu.css3
-rw-r--r--src/server/authentication/controllers/WorkspacesMenu.tsx90
9 files changed, 60 insertions, 164 deletions
diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index 4e918221c..b2ca4a196 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -100,8 +100,7 @@ export class Main extends React.Component {
onHistory = () => {
if (window.location.pathname !== RouteStore.home) {
let pathname = window.location.pathname.split("/");
- CurrentUserUtils.MainDocId = pathname[pathname.length - 1];
- DocServer.GetRefField(CurrentUserUtils.MainDocId).then(action((field: Opt<Field>) => {
+ DocServer.GetRefField(pathname[pathname.length - 1]).then(action((field: Opt<Field>) => {
if (field instanceof Doc) {
this.openWorkspace(field, true);
}
@@ -132,7 +131,6 @@ export class Main extends React.Component {
if (!CurrentUserUtils.MainDocId) {
const doc = await Cast(CurrentUserUtils.UserDocument.activeWorkspace, Doc);
if (doc) {
- CurrentUserUtils.MainDocId = doc[Id];
this.openWorkspace(doc);
} else {
this.createNewWorkspace();
@@ -148,11 +146,12 @@ export class Main extends React.Component {
createNewWorkspace = async (id?: string) => {
const list = Cast(CurrentUserUtils.UserDocument.data, listSpec(Doc));
if (list) {
+ let libraryDoc = Docs.TreeDocument([CurrentUserUtils.UserDocument], { x: 0, y: 400, title: `Library: ${CurrentUserUtils.email} ${list.length + 1}` });
+ libraryDoc.excludeFromLibrary = true;
let freeformDoc = Docs.FreeformDocument([], { x: 0, y: 400, title: `WS collection ${list.length + 1}` });
- var dockingLayout = { content: [{ type: 'row', content: [CollectionDockingView.makeDocumentConfig(freeformDoc)] }] };
- let mainDoc = Docs.DockDocument([freeformDoc], JSON.stringify(dockingLayout), { title: `Workspace ${list.length + 1}` });
+ var dockingLayout = { content: [{ type: 'row', content: [CollectionDockingView.makeDocumentConfig(libraryDoc, 150), CollectionDockingView.makeDocumentConfig(freeformDoc, 600)] }] };
+ let mainDoc = Docs.DockDocument([libraryDoc, freeformDoc], JSON.stringify(dockingLayout), { title: `Workspace ${list.length + 1}` });
list.push(mainDoc);
- CurrentUserUtils.MainDocId = mainDoc[Id];
// bcz: strangely, we need a timeout to prevent exceptions/issues initializing GoldenLayout (the rendering engine for Main Container)
setTimeout(() => {
this.openWorkspace(mainDoc);
@@ -164,6 +163,7 @@ export class Main extends React.Component {
@action
openWorkspace = async (doc: Doc, fromHistory = false) => {
+ CurrentUserUtils.MainDocId = doc[Id];
this.mainContainer = doc;
fromHistory || window.history.pushState(null, StrCast(doc.title), "/doc/" + doc[Id]);
const col = await Cast(CurrentUserUtils.UserDocument.optionalRightCollection, Doc);
@@ -259,9 +259,7 @@ export class Main extends React.Component {
/* @TODO this should really be moved into a moveable toolbar component, but for now let's put it here to meet the deadline */
@computed
get miscButtons() {
- let workspacesRef = React.createRef<HTMLDivElement>();
let logoutRef = React.createRef<HTMLDivElement>();
- let toggleWorkspaces = () => runInAction(() => this._workspacesShown = !this._workspacesShown);
return [
<button className="clear-db-button" key="clear-db" onClick={DocServer.DeleteDatabase}>Clear Database</button>,
@@ -270,24 +268,11 @@ export class Main extends React.Component {
<button className="toolbar-button round-button" title="Redo" onClick={() => UndoManager.Redo()}><FontAwesomeIcon icon="redo-alt" size="sm" /></button>
<button className="toolbar-button round-button" title="Ink" onClick={() => InkingControl.Instance.toggleDisplay()}><FontAwesomeIcon icon="pen-nib" size="sm" /></button>
</div >,
- <div className="main-buttonDiv" key="workspaces" style={{ top: '34px', left: '2px', position: 'absolute' }} ref={workspacesRef}>
- <button onClick={toggleWorkspaces}>Workspaces</button></div>,
<div className="main-buttonDiv" key="logout" style={{ top: '34px', right: '1px', position: 'absolute' }} ref={logoutRef}>
<button onClick={() => request.get(DocServer.prepend(RouteStore.logout), emptyFunction)}>Log Out</button></div>
];
}
- @computed
- get workspaceMenu() {
- let areWorkspacesShown = () => this._workspacesShown;
- let toggleWorkspaces = () => runInAction(() => this._workspacesShown = !this._workspacesShown);
- let workspaces = Cast(CurrentUserUtils.UserDocument.data, listSpec(Doc));
- return (!workspaces || !this.mainContainer) ? (null) :
- <WorkspacesMenu active={this.mainContainer} open={this.openWorkspace}
- new={this.createNewWorkspace} allWorkspaces={workspaces}
- isShown={areWorkspacesShown} toggle={toggleWorkspaces} />;
- }
-
render() {
return (
<div id="main-div">
@@ -297,7 +282,6 @@ export class Main extends React.Component {
<ContextMenu />
{this.nodesMenu()}
{this.miscButtons}
- {this.workspaceMenu}
<InkingControl />
<MainOverlayTextBox />
</div>
diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx
index 7be846e05..f29d9c8a1 100644
--- a/src/client/views/TemplateMenu.tsx
+++ b/src/client/views/TemplateMenu.tsx
@@ -59,12 +59,9 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> {
}
render() {
- trace();
let templateMenu: Array<JSX.Element> = [];
- this.props.templates.forEach((checked, template) => {
- console.log("checked + " + checked + " " + this.props.templates.get(template));
- templateMenu.push(<TemplateToggle key={template.Name} template={template} checked={checked} toggle={this.toggleTemplate} />);
- });
+ this.props.templates.forEach((checked, template) =>
+ templateMenu.push(<TemplateToggle key={template.Name} template={template} checked={checked} toggle={this.toggleTemplate} />));
return (
<div className="templating-menu" >
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 014773ab6..cfb1aef7d 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -19,15 +19,17 @@ import { List } from "../../../new_fields/List";
import { DocServer } from "../../DocServer";
import { listSpec } from "../../../new_fields/Schema";
import { Id, FieldId } from "../../../new_fields/RefField";
+import { faSignInAlt } from "@fortawesome/free-solid-svg-icons";
@observer
export class CollectionDockingView extends React.Component<SubCollectionViewProps> {
public static Instance: CollectionDockingView;
- public static makeDocumentConfig(document: Doc) {
+ public static makeDocumentConfig(document: Doc, width?: number) {
return {
type: 'react-component',
component: 'DocumentFrameRenderer',
title: document.title,
+ width: width,
props: {
documentId: document[Id],
//collectionDockingView: CollectionDockingView.Instance
@@ -37,7 +39,6 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
private _goldenLayout: any = null;
private _containerRef = React.createRef<HTMLDivElement>();
- private _fullScreen: any = null;
private _flush: boolean = false;
private _ignoreStateChange = "";
@@ -67,20 +68,9 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
this._goldenLayout.root.contentItems[0].addChild(docconfig);
docconfig.callDownwards('_$init');
this._goldenLayout._$maximiseItem(docconfig);
- this._fullScreen = docconfig;
this._ignoreStateChange = JSON.stringify(this._goldenLayout.toConfig());
this.stateChanged();
}
- @action
- public CloseFullScreen() {
- if (this._fullScreen) {
- this._goldenLayout._$minimiseItem(this._fullScreen);
- this._goldenLayout.root.contentItems[0].removeChild(this._fullScreen);
- this._fullScreen = null;
- this._ignoreStateChange = JSON.stringify(this._goldenLayout.toConfig());
- this.stateChanged();
- }
- }
//
// Creates a vertical split on the right side of the docking view, and then adds the Document to that split
@@ -330,7 +320,6 @@ interface DockedFrameProps {
}
@observer
export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
-
_mainCont = React.createRef<HTMLDivElement>();
@observable private _panelWidth = 0;
@observable private _panelHeight = 0;
@@ -347,10 +336,7 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
const nativeH = this.nativeHeight();
const nativeW = this.nativeWidth();
let wscale = this._panelWidth / nativeW;
- if (wscale * nativeH > this._panelHeight) {
- return this._panelHeight / nativeH;
- }
- return wscale;
+ return wscale * nativeH > this._panelHeight ? this._panelHeight / nativeH : wscale;
}
ScreenToLocalTransform = () => {
@@ -364,6 +350,8 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
get previewPanelCenteringOffset() { return (this._panelWidth - this.nativeWidth() * this.contentScaling()) / 2; }
get content() {
+ if (!this._document)
+ return (null);
return (
<div className="collectionDockingView-content" ref={this._mainCont}
style={{ transform: `translate(${this.previewPanelCenteringOffset}px, 0px)` }}>
@@ -385,9 +373,10 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
}
render() {
+ let theContent = this.content;
return !this._document ? (null) :
<Measure onResize={action((r: any) => { this._panelWidth = r.entry.width; this._panelHeight = r.entry.height; })}>
- {({ measureRef }) => <div ref={measureRef}> {this.content} </div>}
+ {({ measureRef }) => <div ref={measureRef}> {theContent} </div>}
</Measure>;
}
} \ No newline at end of file
diff --git a/src/client/views/collections/CollectionTreeView.scss b/src/client/views/collections/CollectionTreeView.scss
index 95df5edb9..ecb5f78bb 100644
--- a/src/client/views/collections/CollectionTreeView.scss
+++ b/src/client/views/collections/CollectionTreeView.scss
@@ -41,7 +41,6 @@
.coll-title {
font-size: 24px;
- margin-bottom: 20px;
}
.docContainer {
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index f148c2b2f..0520e0f88 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -9,10 +9,15 @@ import { CollectionSubView } from "./CollectionSubView";
import "./CollectionTreeView.scss";
import React = require("react");
import { Document, listSpec } from '../../../new_fields/Schema';
-import { Cast, StrCast, BoolCast } from '../../../new_fields/Types';
+import { Cast, StrCast, BoolCast, FieldValue } from '../../../new_fields/Types';
import { Doc } from '../../../new_fields/Doc';
import { Id } from '../../../new_fields/RefField';
import { Utils } from '../../../Utils';
+import { JSXElement } from 'babel-types';
+import { ContextMenu } from '../ContextMenu';
+import { undoBatch } from '../../util/UndoManager';
+import { Main } from '../Main';
+import { CurrentUserUtils } from '../../../server/authentication/models/current_user_utils';
export interface TreeViewProps {
@@ -42,11 +47,14 @@ class TreeView extends React.Component<TreeViewProps> {
delete = () => this.props.deleteDoc(this.props.document);
+ get children() {
+ return Cast(this.props.document.data, listSpec(Doc), []).filter(doc => FieldValue(doc));
+ }
+
@action
remove = (document: Document) => {
- var children = Cast(this.props.document.data, listSpec(Doc));
- if (children) {
- children.splice(children.indexOf(document), 1);
+ if (this.children) {
+ this.children.splice(this.children.indexOf(document), 1);
}
}
@@ -94,27 +102,38 @@ class TreeView extends React.Component<TreeViewProps> {
</div >);
}
+ onWorkspaceContextMenu = (e: React.MouseEvent): void => {
+ if (!e.isPropagationStopped() && this.props.document[Id] !== CurrentUserUtils.MainDocId) { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7
+ if (!ContextMenu.Instance.getItems().some(item => item.description === "Open as Workspace")) {
+ ContextMenu.Instance.addItem({ description: "Open as Workspace", event: undoBatch(() => Main.Instance.openWorkspace(this.props.document)) });
+ }
+ }
+ }
render() {
let bulletType = BulletType.List;
- let childElements: JSX.Element | undefined = undefined;
+ let contentElement: JSX.Element | null = (null);
var children = Cast(this.props.document.data, listSpec(Doc));
if (children) { // add children for a collection
if (!this._collapsed) {
bulletType = BulletType.Collapsible;
- childElements = <ul>
- {children.map(value => <TreeView key={value[Id]} document={value} deleteDoc={this.remove} moveDocument={this.move} copyOnDrag={this.props.copyOnDrag} />)}
+ contentElement = <ul>
+ {TreeView.GetChildElements(children, this.remove, this.move, this.props.copyOnDrag)}
</ul >;
}
else bulletType = BulletType.Collapsed;
}
- return <div className="treeViewItem-container" >
+ return <div className="treeViewItem-container" onContextMenu={this.onWorkspaceContextMenu} >
<li className="collection-child">
{this.renderBullet(bulletType)}
{this.renderTitle()}
- {childElements ? childElements : (null)}
+ {contentElement}
</li>
</div>;
}
+ public static GetChildElements(docs: Doc[], remove: ((doc: Doc) => void), move: DragManager.MoveFunction, copyOnDrag: boolean) {
+ return docs.filter(child => !child.excludeFromLibrary).map(child =>
+ <TreeView document={child} key={child[Id]} deleteDoc={remove} moveDocument={move} copyOnDrag={copyOnDrag} />);
+ }
}
@observer
@@ -128,21 +147,30 @@ export class CollectionTreeView extends CollectionSubView(Document) {
}
}
+ onContextMenu = (e: React.MouseEvent): void => {
+ if (!e.isPropagationStopped() && this.props.Document[Id] !== CurrentUserUtils.MainDocId) { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7
+ ContextMenu.Instance.addItem({ description: "Create Workspace", event: undoBatch(() => Main.Instance.createNewWorkspace()) });
+ }
+ }
render() {
const children = this.children;
let copyOnDrag = BoolCast(this.props.Document.copyDraggedItems, false);
- let childrenElement = !children ? (null) :
- (children.map(value =>
- <TreeView document={value} key={value[Id]} deleteDoc={this.remove} moveDocument={this.props.moveDocument} copyOnDrag={copyOnDrag} />));
+ if (!children) {
+ return (null);
+ }
+ let testForLibrary = children && children.length === 1 && children[0] === CurrentUserUtils.UserDocument;
+ var subchildren = testForLibrary ? Cast(children[0].data, listSpec(Doc), children) : children;
+ let childElements = TreeView.GetChildElements(subchildren, this.remove, this.props.moveDocument, copyOnDrag);
return (
<div id="body" className="collectionTreeView-dropTarget"
style={{ borderRadius: "inherit" }}
+ onContextMenu={this.onContextMenu}
onWheel={(e: React.WheelEvent) => e.stopPropagation()}
onDrop={(e: React.DragEvent) => this.onDrop(e, {})} ref={this.createDropTarget}>
<div className="coll-title">
<EditableView
- contents={this.props.Document.Title}
+ contents={this.props.Document.title}
display={"inline"}
height={72}
GetValue={() => StrCast(this.props.Document.title)}
@@ -151,9 +179,8 @@ export class CollectionTreeView extends CollectionSubView(Document) {
return true;
}} />
</div>
- <hr />
<ul className="no-indent">
- {childrenElement}
+ {childElements}
</ul>
</div >
);
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index c2049a09a..8c1442d38 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -11,6 +11,7 @@ import { observer } from 'mobx-react';
import { undoBatch } from '../../util/UndoManager';
import { trace } from 'mobx';
import { Id } from '../../../new_fields/RefField';
+import { Main } from '../Main';
@observer
export class CollectionView extends React.Component<FieldViewProps> {
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 5588fa1ac..86d34725d 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -205,16 +205,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
CollectionDockingView.Instance.OpenFullScreen(doc);
}
ContextMenu.Instance.clearItems();
- ContextMenu.Instance.addItem({ description: "Close Full Screen", event: this.closeFullScreenClicked });
- ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15);
- }
- closeFullScreenClicked = (e: React.MouseEvent): void => {
- CollectionDockingView.Instance.CloseFullScreen();
- ContextMenu.Instance.clearItems();
- ContextMenu.Instance.addItem({ description: "Full Screen", event: this.fullScreenClicked });
- ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15);
+ SelectionManager.DeselectAll();
}
-
@undoBatch
@action
drop = async (e: Event, de: DragManager.DropEvent) => {
diff --git a/src/server/authentication/controllers/WorkspacesMenu.css b/src/server/authentication/controllers/WorkspacesMenu.css
deleted file mode 100644
index b89039965..000000000
--- a/src/server/authentication/controllers/WorkspacesMenu.css
+++ /dev/null
@@ -1,3 +0,0 @@
-.ids:hover {
- color: darkblue;
-} \ No newline at end of file
diff --git a/src/server/authentication/controllers/WorkspacesMenu.tsx b/src/server/authentication/controllers/WorkspacesMenu.tsx
deleted file mode 100644
index 91756315d..000000000
--- a/src/server/authentication/controllers/WorkspacesMenu.tsx
+++ /dev/null
@@ -1,90 +0,0 @@
-import * as React from 'react';
-import { observable, action, configure, reaction, computed, ObservableMap, runInAction } from 'mobx';
-import { observer } from "mobx-react";
-import './WorkspacesMenu.css';
-import { EditableView } from '../../../client/views/EditableView';
-import { Doc } from '../../../new_fields/Doc';
-import { StrCast } from '../../../new_fields/Types';
-import { Id } from '../../../new_fields/RefField';
-
-export interface WorkspaceMenuProps {
- active: Doc | undefined;
- open: (workspace: Doc) => void;
- new: () => void;
- allWorkspaces: Doc[];
- isShown: () => boolean;
- toggle: () => void;
-}
-
-@observer
-export class WorkspacesMenu extends React.Component<WorkspaceMenuProps> {
- constructor(props: WorkspaceMenuProps) {
- super(props);
- this.addNewWorkspace = this.addNewWorkspace.bind(this);
- }
-
- @action
- addNewWorkspace() {
- this.props.new();
- this.props.toggle();
- }
-
- render() {
- return (
- <div
- style={{
- width: "auto",
- maxHeight: '200px',
- overflow: 'scroll',
- borderRadius: 5,
- position: "absolute",
- top: 78,
- left: this.props.isShown() ? 11 : -500,
- background: "white",
- border: "black solid 2px",
- transition: "all 1s ease",
- zIndex: 15,
- padding: 10,
- paddingRight: 12,
- }}>
- <img
- src="https://bit.ly/2IBBkxk"
- style={{
- width: 20,
- height: 20,
- marginTop: 3,
- marginLeft: 3,
- marginBottom: 3,
- cursor: "grab"
- }}
- onClick={this.addNewWorkspace}
- />
- {this.props.allWorkspaces.map((s, i) =>
- <div
- key={s[Id]}
- onContextMenu={(e) => {
- e.preventDefault();
- this.props.open(s);
- }}
- style={{
- marginTop: 10,
- color: s === this.props.active ? "red" : "black"
- }}
- >
- <span>{i + 1} - </span>
- <EditableView
- display={"inline"}
- GetValue={() => StrCast(s.title)}
- SetValue={(title: string): boolean => {
- s.title = title;
- return true;
- }}
- contents={s.Title}
- height={20}
- />
- </div>
- )}
- </div>
- );
- }
-} \ No newline at end of file