aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/client/util/History.ts4
-rw-r--r--src/client/views/Main.tsx280
-rw-r--r--src/client/views/MainView.tsx274
-rw-r--r--src/client/views/PresentationView.tsx1
-rw-r--r--src/client/views/collections/CollectionTreeView.tsx8
-rw-r--r--src/debug/Viewer.tsx364
6 files changed, 468 insertions, 463 deletions
diff --git a/src/client/util/History.ts b/src/client/util/History.ts
index 92d2b2b44..545ea8629 100644
--- a/src/client/util/History.ts
+++ b/src/client/util/History.ts
@@ -1,7 +1,7 @@
import { Doc, Opt, Field } from "../../new_fields/Doc";
import { DocServer } from "../DocServer";
-import { Main } from "../views/Main";
import { RouteStore } from "../../server/RouteStore";
+import { MainView } from "../views/MainView";
export namespace HistoryUtil {
export interface DocInitializerList {
@@ -114,7 +114,7 @@ export namespace HistoryUtil {
const field = await DocServer.GetRefField(url.docId);
await Promise.all(Object.keys(url.initializers).map(id => initDoc(id, url.initializers[id])));
if (field instanceof Doc) {
- Main.Instance.openWorkspace(field, true);
+ MainView.Instance.openWorkspace(field, true);
}
}
diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index 177047a9a..3d9750a85 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -1,281 +1,11 @@
-import { IconName, library } from '@fortawesome/fontawesome-svg-core';
-import { faFilePdf, faFilm, faFont, faGlobeAsia, faImage, faMusic, faObjectGroup, faPenNib, faRedoAlt, faTable, faTree, faUndoAlt } from '@fortawesome/free-solid-svg-icons';
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed, configure, observable, runInAction, trace } from 'mobx';
-import { observer } from 'mobx-react';
-import "normalize.css";
-import * as React from 'react';
+import { MainView } from "./MainView";
+import { Docs } from "../documents/Documents";
+import { CurrentUserUtils } from "../../server/authentication/models/current_user_utils";
import * as ReactDOM from 'react-dom';
-import Measure from 'react-measure';
-import * as request from 'request';
-import { CurrentUserUtils } from '../../server/authentication/models/current_user_utils';
-import { RouteStore } from '../../server/RouteStore';
-import { emptyFunction, returnTrue, Utils, returnOne, returnZero } from '../../Utils';
-import { Docs } from '../documents/Documents';
-import { SetupDrag, DragManager } from '../util/DragManager';
-import { Transform } from '../util/Transform';
-import { UndoManager } from '../util/UndoManager';
-import { PresentationView } from './PresentationView';
-import { CollectionDockingView } from './collections/CollectionDockingView';
-import { ContextMenu } from './ContextMenu';
-import { DocumentDecorations } from './DocumentDecorations';
-import { InkingControl } from './InkingControl';
-import "./Main.scss";
-import { MainOverlayTextBox } from './MainOverlayTextBox';
-import { DocumentView } from './nodes/DocumentView';
-import { PreviewCursor } from './PreviewCursor';
-import { SearchBox } from './SearchBox';
-import { SelectionManager } from '../util/SelectionManager';
-import { FieldResult, Field, Doc, Opt } from '../../new_fields/Doc';
-import { Cast, FieldValue, StrCast } from '../../new_fields/Types';
-import { DocServer } from '../DocServer';
-import { listSpec } from '../../new_fields/Schema';
-import { Id } from '../../new_fields/RefField';
-import { HistoryUtil } from '../util/History';
-
-
-@observer
-export class Main extends React.Component {
- public static Instance: Main;
- @observable private _workspacesShown: boolean = false;
- @observable public pwidth: number = 0;
- @observable public pheight: number = 0;
-
- @computed private get mainContainer(): Opt<Doc> {
- return FieldValue(Cast(CurrentUserUtils.UserDocument.activeWorkspace, Doc));
- }
- private set mainContainer(doc: Opt<Doc>) {
- if (doc) {
- if (!("presentationView" in doc)) {
- doc.presentationView = new Doc();
- }
- CurrentUserUtils.UserDocument.activeWorkspace = doc;
- }
- }
-
- constructor(props: Readonly<{}>) {
- super(props);
- Main.Instance = this;
- // causes errors to be generated when modifying an observable outside of an action
- configure({ enforceActions: "observed" });
- if (window.location.pathname !== RouteStore.home) {
- let pathname = window.location.pathname.split("/");
- if (pathname.length > 1 && pathname[pathname.length - 2] === 'doc') {
- CurrentUserUtils.MainDocId = pathname[pathname.length - 1];
- }
- }
-
- library.add(faFont);
- library.add(faImage);
- library.add(faFilePdf);
- library.add(faObjectGroup);
- library.add(faTable);
- library.add(faGlobeAsia);
- library.add(faUndoAlt);
- library.add(faRedoAlt);
- library.add(faPenNib);
- library.add(faFilm);
- library.add(faMusic);
- library.add(faTree);
- this.initEventListeners();
- this.initAuthenticationRouters();
- }
-
- initEventListeners = () => {
- // window.addEventListener("pointermove", (e) => this.reportLocation(e))
- window.addEventListener("drop", (e) => e.preventDefault(), false); // drop event handler
- window.addEventListener("dragover", (e) => e.preventDefault(), false); // drag event handler
- window.addEventListener("keydown", (e) => {
- if (e.key === "Escape") {
- DragManager.AbortDrag();
- SelectionManager.DeselectAll();
- }
- }, false); // drag event handler
- // click interactions for the context menu
- document.addEventListener("pointerdown", action(function (e: PointerEvent) {
- if (!ContextMenu.Instance.intersects(e.pageX, e.pageY)) {
- ContextMenu.Instance.clearItems();
- }
- }), true);
- }
-
- initAuthenticationRouters = async () => {
- // Load the user's active workspace, or create a new one if initial session after signup
- if (!CurrentUserUtils.MainDocId) {
- const doc = await Cast(CurrentUserUtils.UserDocument.activeWorkspace, Doc);
- if (doc) {
- this.openWorkspace(doc);
- } else {
- this.createNewWorkspace();
- }
- } else {
- DocServer.GetRefField(CurrentUserUtils.MainDocId).then(field =>
- field instanceof Doc ? this.openWorkspace(field) :
- this.createNewWorkspace(CurrentUserUtils.MainDocId));
- }
- }
-
- @action
- createNewWorkspace = async (id?: string) => {
- const list = Cast(CurrentUserUtils.UserDocument.data, listSpec(Doc));
- if (list) {
- let freeformDoc = Docs.FreeformDocument([], { x: 0, y: 400, title: `WS collection ${list.length + 1}` });
- var dockingLayout = { content: [{ type: 'row', content: [CollectionDockingView.makeDocumentConfig(CurrentUserUtils.UserDocument, 150), CollectionDockingView.makeDocumentConfig(freeformDoc, 600)] }] };
- let mainDoc = Docs.DockDocument([CurrentUserUtils.UserDocument, freeformDoc], JSON.stringify(dockingLayout), { title: `Workspace ${list.length + 1}` });
- list.push(mainDoc);
- // bcz: strangely, we need a timeout to prevent exceptions/issues initializing GoldenLayout (the rendering engine for Main Container)
- setTimeout(() => {
- this.openWorkspace(mainDoc);
- let pendingDocument = Docs.SchemaDocument(["title"], [], { title: "New Mobile Uploads" });
- mainDoc.optionalRightCollection = pendingDocument;
- }, 0);
- }
- }
-
- @action
- openWorkspace = async (doc: Doc, fromHistory = false) => {
- CurrentUserUtils.MainDocId = doc[Id];
- this.mainContainer = doc;
- fromHistory || HistoryUtil.pushState({ type: "doc", docId: doc[Id], initializers: {} });
- const col = await Cast(CurrentUserUtils.UserDocument.optionalRightCollection, Doc);
- // if there is a pending doc, and it has new data, show it (syip: we use a timeout to prevent collection docking view from being uninitialized)
- setTimeout(async () => {
- if (col) {
- const l = Cast(col.data, listSpec(Doc));
- if (l && l.length > 0) {
- CollectionDockingView.Instance.AddRightSplit(col);
- }
- }
- }, 100);
- }
- @action
- onResize = (r: any) => {
- this.pwidth = r.offset.width;
- this.pheight = r.offset.height;
- }
- getPWidth = () => {
- return this.pwidth;
- }
- getPHeight = () => {
- return this.pheight;
- }
- @computed
- get mainContent() {
- let mainCont = this.mainContainer;
- let content = !mainCont ? (null) :
- <DocumentView Document={mainCont}
- toggleMinimized={emptyFunction}
- addDocument={undefined}
- addDocTab={emptyFunction}
- removeDocument={undefined}
- ScreenToLocalTransform={Transform.Identity}
- ContentScaling={returnOne}
- PanelWidth={this.getPWidth}
- PanelHeight={this.getPHeight}
- isTopMost={true}
- selectOnLoad={false}
- focus={emptyFunction}
- parentActive={returnTrue}
- whenActiveChanged={emptyFunction}
- bringToFront={emptyFunction}
- ContainingCollectionView={undefined} />;
- const pres = mainCont ? FieldValue(Cast(mainCont.presentationView, Doc)) : undefined;
- return <Measure offset onResize={this.onResize}>
- {({ measureRef }) =>
- <div ref={measureRef} id="mainContent-div">
- {content}
- {pres ? <PresentationView Document={pres} key="presentation" /> : null}
- </div>
- }
- </Measure>;
- }
-
- /* for the expandable add nodes menu. Not included with the miscbuttons because once it expands it expands the whole div with it, making canvas interactions limited. */
- nodesMenu() {
-
- let imgurl = "https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg";
- let pdfurl = "http://www.adobe.com/support/products/enterprise/knowledgecenter/media/c27211_sample_explain.pdf";
- let weburl = "https://cs.brown.edu/courses/cs166/";
- let audiourl = "http://techslides.com/demos/samples/sample.mp3";
- let videourl = "http://techslides.com/demos/sample-videos/small.mp4";
-
- let addTextNode = action(() => Docs.TextDocument({ borderRounding: -1, width: 200, height: 200, title: "a text note" }));
- let addColNode = action(() => Docs.FreeformDocument([], { width: 200, height: 200, title: "a freeform collection" }));
- let addSchemaNode = action(() => Docs.SchemaDocument(["title"], [], { width: 200, height: 200, title: "a schema collection" }));
- let addTreeNode = action(() => Docs.TreeDocument([CurrentUserUtils.UserDocument], { width: 250, height: 400, title: "Library:" + CurrentUserUtils.email, dropAction: "alias" }));
- // let addTreeNode = action(() => Docs.TreeDocument(this._northstarSchemas, { width: 250, height: 400, title: "northstar schemas", dropAction: "copy" }));
- let addVideoNode = action(() => Docs.VideoDocument(videourl, { width: 200, title: "video node" }));
- let addPDFNode = action(() => Docs.PdfDocument(pdfurl, { width: 200, height: 200, title: "a pdf doc" }));
- let addImageNode = action(() => Docs.ImageDocument(imgurl, { width: 200, title: "an image of a cat" }));
- let addWebNode = action(() => Docs.WebDocument(weburl, { width: 200, height: 200, title: "a sample web page" }));
- let addAudioNode = action(() => Docs.AudioDocument(audiourl, { width: 200, height: 200, title: "audio node" }));
-
- let btns: [React.RefObject<HTMLDivElement>, IconName, string, () => Doc][] = [
- [React.createRef<HTMLDivElement>(), "font", "Add Textbox", addTextNode],
- [React.createRef<HTMLDivElement>(), "image", "Add Image", addImageNode],
- [React.createRef<HTMLDivElement>(), "file-pdf", "Add PDF", addPDFNode],
- [React.createRef<HTMLDivElement>(), "film", "Add Video", addVideoNode],
- [React.createRef<HTMLDivElement>(), "music", "Add Audio", addAudioNode],
- [React.createRef<HTMLDivElement>(), "globe-asia", "Add Web Clipping", addWebNode],
- [React.createRef<HTMLDivElement>(), "object-group", "Add Collection", addColNode],
- [React.createRef<HTMLDivElement>(), "tree", "Add Tree", addTreeNode],
- [React.createRef<HTMLDivElement>(), "table", "Add Schema", addSchemaNode],
- ];
-
- return < div id="add-nodes-menu" >
- <input type="checkbox" id="add-menu-toggle" />
- <label htmlFor="add-menu-toggle" title="Add Node"><p>+</p></label>
-
- <div id="add-options-content">
- <ul id="add-options-list">
- {btns.map(btn =>
- <li key={btn[1]} ><div ref={btn[0]}>
- <button className="round-button add-button" title={btn[2]} onPointerDown={SetupDrag(btn[0], btn[3])}>
- <FontAwesomeIcon icon={btn[1]} size="sm" />
- </button>
- </div></li>)}
- </ul>
- </div>
- </div >;
- }
-
- /* @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 logoutRef = React.createRef<HTMLDivElement>();
-
- return [
- <button className="clear-db-button" key="clear-db" onClick={e => e.shiftKey && e.altKey && DocServer.DeleteDatabase()}>Clear Database</button>,
- <div id="toolbar" key="toolbar">
- <button className="toolbar-button round-button" title="Undo" onClick={() => UndoManager.Undo()}><FontAwesomeIcon icon="undo-alt" size="sm" /></button>
- <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-searchDiv" key="search" style={{ top: '34px', right: '1px', position: 'absolute' }} > <SearchBox /> </div>,
- <div className="main-buttonDiv" key="logout" style={{ bottom: '0px', right: '1px', position: 'absolute' }} ref={logoutRef}>
- <button onClick={() => request.get(DocServer.prepend(RouteStore.logout), emptyFunction)}>Log Out</button></div>
- ];
-
- }
-
- render() {
- return (
- <div id="main-div">
- <DocumentDecorations />
- {this.mainContent}
- <PreviewCursor />
- <ContextMenu />
- {this.nodesMenu()}
- {this.miscButtons}
- <InkingControl />
- <MainOverlayTextBox />
- </div>
- );
- }
-}
+import * as React from 'react';
(async () => {
await Docs.initProtos();
await CurrentUserUtils.loadCurrentUser();
- ReactDOM.render(<Main />, document.getElementById('root'));
+ ReactDOM.render(<MainView />, document.getElementById('root'));
})();
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
new file mode 100644
index 000000000..d0d77bbf4
--- /dev/null
+++ b/src/client/views/MainView.tsx
@@ -0,0 +1,274 @@
+import { IconName, library } from '@fortawesome/fontawesome-svg-core';
+import { faFilePdf, faFilm, faFont, faGlobeAsia, faImage, faMusic, faObjectGroup, faPenNib, faRedoAlt, faTable, faTree, faUndoAlt } from '@fortawesome/free-solid-svg-icons';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { action, computed, configure, observable, runInAction, trace } from 'mobx';
+import { observer } from 'mobx-react';
+import "normalize.css";
+import * as React from 'react';
+import Measure from 'react-measure';
+import * as request from 'request';
+import { CurrentUserUtils } from '../../server/authentication/models/current_user_utils';
+import { RouteStore } from '../../server/RouteStore';
+import { emptyFunction, returnTrue, Utils, returnOne, returnZero } from '../../Utils';
+import { Docs } from '../documents/Documents';
+import { SetupDrag, DragManager } from '../util/DragManager';
+import { Transform } from '../util/Transform';
+import { UndoManager } from '../util/UndoManager';
+import { PresentationView } from './PresentationView';
+import { CollectionDockingView } from './collections/CollectionDockingView';
+import { ContextMenu } from './ContextMenu';
+import { DocumentDecorations } from './DocumentDecorations';
+import { InkingControl } from './InkingControl';
+import "./Main.scss";
+import { MainOverlayTextBox } from './MainOverlayTextBox';
+import { DocumentView } from './nodes/DocumentView';
+import { PreviewCursor } from './PreviewCursor';
+import { SearchBox } from './SearchBox';
+import { SelectionManager } from '../util/SelectionManager';
+import { FieldResult, Field, Doc, Opt } from '../../new_fields/Doc';
+import { Cast, FieldValue, StrCast } from '../../new_fields/Types';
+import { DocServer } from '../DocServer';
+import { listSpec } from '../../new_fields/Schema';
+import { Id } from '../../new_fields/RefField';
+import { HistoryUtil } from '../util/History';
+
+
+@observer
+export class MainView extends React.Component {
+ public static Instance: MainView;
+ @observable private _workspacesShown: boolean = false;
+ @observable public pwidth: number = 0;
+ @observable public pheight: number = 0;
+
+ @computed private get mainContainer(): Opt<Doc> {
+ return FieldValue(Cast(CurrentUserUtils.UserDocument.activeWorkspace, Doc));
+ }
+ private set mainContainer(doc: Opt<Doc>) {
+ if (doc) {
+ if (!("presentationView" in doc)) {
+ doc.presentationView = new Doc();
+ }
+ CurrentUserUtils.UserDocument.activeWorkspace = doc;
+ }
+ }
+
+ constructor(props: Readonly<{}>) {
+ super(props);
+ MainView.Instance = this;
+ // causes errors to be generated when modifying an observable outside of an action
+ configure({ enforceActions: "observed" });
+ if (window.location.pathname !== RouteStore.home) {
+ let pathname = window.location.pathname.split("/");
+ if (pathname.length > 1 && pathname[pathname.length - 2] === 'doc') {
+ CurrentUserUtils.MainDocId = pathname[pathname.length - 1];
+ }
+ }
+
+ library.add(faFont);
+ library.add(faImage);
+ library.add(faFilePdf);
+ library.add(faObjectGroup);
+ library.add(faTable);
+ library.add(faGlobeAsia);
+ library.add(faUndoAlt);
+ library.add(faRedoAlt);
+ library.add(faPenNib);
+ library.add(faFilm);
+ library.add(faMusic);
+ library.add(faTree);
+ this.initEventListeners();
+ this.initAuthenticationRouters();
+ }
+
+ initEventListeners = () => {
+ // window.addEventListener("pointermove", (e) => this.reportLocation(e))
+ window.addEventListener("drop", (e) => e.preventDefault(), false); // drop event handler
+ window.addEventListener("dragover", (e) => e.preventDefault(), false); // drag event handler
+ window.addEventListener("keydown", (e) => {
+ if (e.key === "Escape") {
+ DragManager.AbortDrag();
+ SelectionManager.DeselectAll();
+ }
+ }, false); // drag event handler
+ // click interactions for the context menu
+ document.addEventListener("pointerdown", action(function (e: PointerEvent) {
+ if (!ContextMenu.Instance.intersects(e.pageX, e.pageY)) {
+ ContextMenu.Instance.clearItems();
+ }
+ }), true);
+ }
+
+ initAuthenticationRouters = async () => {
+ // Load the user's active workspace, or create a new one if initial session after signup
+ if (!CurrentUserUtils.MainDocId) {
+ const doc = await Cast(CurrentUserUtils.UserDocument.activeWorkspace, Doc);
+ if (doc) {
+ this.openWorkspace(doc);
+ } else {
+ this.createNewWorkspace();
+ }
+ } else {
+ DocServer.GetRefField(CurrentUserUtils.MainDocId).then(field =>
+ field instanceof Doc ? this.openWorkspace(field) :
+ this.createNewWorkspace(CurrentUserUtils.MainDocId));
+ }
+ }
+
+ @action
+ createNewWorkspace = async (id?: string) => {
+ const list = Cast(CurrentUserUtils.UserDocument.data, listSpec(Doc));
+ if (list) {
+ let freeformDoc = Docs.FreeformDocument([], { x: 0, y: 400, title: `WS collection ${list.length + 1}` });
+ var dockingLayout = { content: [{ type: 'row', content: [CollectionDockingView.makeDocumentConfig(CurrentUserUtils.UserDocument, 150), CollectionDockingView.makeDocumentConfig(freeformDoc, 600)] }] };
+ let mainDoc = Docs.DockDocument([CurrentUserUtils.UserDocument, freeformDoc], JSON.stringify(dockingLayout), { title: `Workspace ${list.length + 1}` });
+ list.push(mainDoc);
+ // bcz: strangely, we need a timeout to prevent exceptions/issues initializing GoldenLayout (the rendering engine for Main Container)
+ setTimeout(() => {
+ this.openWorkspace(mainDoc);
+ let pendingDocument = Docs.SchemaDocument(["title"], [], { title: "New Mobile Uploads" });
+ mainDoc.optionalRightCollection = pendingDocument;
+ }, 0);
+ }
+ }
+
+ @action
+ openWorkspace = async (doc: Doc, fromHistory = false) => {
+ CurrentUserUtils.MainDocId = doc[Id];
+ this.mainContainer = doc;
+ fromHistory || HistoryUtil.pushState({ type: "doc", docId: doc[Id], initializers: {} });
+ const col = await Cast(CurrentUserUtils.UserDocument.optionalRightCollection, Doc);
+ // if there is a pending doc, and it has new data, show it (syip: we use a timeout to prevent collection docking view from being uninitialized)
+ setTimeout(async () => {
+ if (col) {
+ const l = Cast(col.data, listSpec(Doc));
+ if (l && l.length > 0) {
+ CollectionDockingView.Instance.AddRightSplit(col);
+ }
+ }
+ }, 100);
+ }
+ @action
+ onResize = (r: any) => {
+ this.pwidth = r.offset.width;
+ this.pheight = r.offset.height;
+ }
+ getPWidth = () => {
+ return this.pwidth;
+ }
+ getPHeight = () => {
+ return this.pheight;
+ }
+ @computed
+ get mainContent() {
+ let mainCont = this.mainContainer;
+ let content = !mainCont ? (null) :
+ <DocumentView Document={mainCont}
+ toggleMinimized={emptyFunction}
+ addDocument={undefined}
+ addDocTab={emptyFunction}
+ removeDocument={undefined}
+ ScreenToLocalTransform={Transform.Identity}
+ ContentScaling={returnOne}
+ PanelWidth={this.getPWidth}
+ PanelHeight={this.getPHeight}
+ isTopMost={true}
+ selectOnLoad={false}
+ focus={emptyFunction}
+ parentActive={returnTrue}
+ whenActiveChanged={emptyFunction}
+ bringToFront={emptyFunction}
+ ContainingCollectionView={undefined} />;
+ const pres = mainCont ? FieldValue(Cast(mainCont.presentationView, Doc)) : undefined;
+ return <Measure offset onResize={this.onResize}>
+ {({ measureRef }) =>
+ <div ref={measureRef} id="mainContent-div">
+ {content}
+ {pres ? <PresentationView Document={pres} key="presentation" /> : null}
+ </div>
+ }
+ </Measure>;
+ }
+
+ /* for the expandable add nodes menu. Not included with the miscbuttons because once it expands it expands the whole div with it, making canvas interactions limited. */
+ nodesMenu() {
+
+ let imgurl = "https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg";
+ let pdfurl = "http://www.adobe.com/support/products/enterprise/knowledgecenter/media/c27211_sample_explain.pdf";
+ let weburl = "https://cs.brown.edu/courses/cs166/";
+ let audiourl = "http://techslides.com/demos/samples/sample.mp3";
+ let videourl = "http://techslides.com/demos/sample-videos/small.mp4";
+
+ let addTextNode = action(() => Docs.TextDocument({ borderRounding: -1, width: 200, height: 200, title: "a text note" }));
+ let addColNode = action(() => Docs.FreeformDocument([], { width: 200, height: 200, title: "a freeform collection" }));
+ let addSchemaNode = action(() => Docs.SchemaDocument(["title"], [], { width: 200, height: 200, title: "a schema collection" }));
+ let addTreeNode = action(() => Docs.TreeDocument([CurrentUserUtils.UserDocument], { width: 250, height: 400, title: "Library:" + CurrentUserUtils.email, dropAction: "alias" }));
+ // let addTreeNode = action(() => Docs.TreeDocument(this._northstarSchemas, { width: 250, height: 400, title: "northstar schemas", dropAction: "copy" }));
+ let addVideoNode = action(() => Docs.VideoDocument(videourl, { width: 200, title: "video node" }));
+ let addPDFNode = action(() => Docs.PdfDocument(pdfurl, { width: 200, height: 200, title: "a pdf doc" }));
+ let addImageNode = action(() => Docs.ImageDocument(imgurl, { width: 200, title: "an image of a cat" }));
+ let addWebNode = action(() => Docs.WebDocument(weburl, { width: 200, height: 200, title: "a sample web page" }));
+ let addAudioNode = action(() => Docs.AudioDocument(audiourl, { width: 200, height: 200, title: "audio node" }));
+
+ let btns: [React.RefObject<HTMLDivElement>, IconName, string, () => Doc][] = [
+ [React.createRef<HTMLDivElement>(), "font", "Add Textbox", addTextNode],
+ [React.createRef<HTMLDivElement>(), "image", "Add Image", addImageNode],
+ [React.createRef<HTMLDivElement>(), "file-pdf", "Add PDF", addPDFNode],
+ [React.createRef<HTMLDivElement>(), "film", "Add Video", addVideoNode],
+ [React.createRef<HTMLDivElement>(), "music", "Add Audio", addAudioNode],
+ [React.createRef<HTMLDivElement>(), "globe-asia", "Add Web Clipping", addWebNode],
+ [React.createRef<HTMLDivElement>(), "object-group", "Add Collection", addColNode],
+ [React.createRef<HTMLDivElement>(), "tree", "Add Tree", addTreeNode],
+ [React.createRef<HTMLDivElement>(), "table", "Add Schema", addSchemaNode],
+ ];
+
+ return < div id="add-nodes-menu" >
+ <input type="checkbox" id="add-menu-toggle" />
+ <label htmlFor="add-menu-toggle" title="Add Node"><p>+</p></label>
+
+ <div id="add-options-content">
+ <ul id="add-options-list">
+ {btns.map(btn =>
+ <li key={btn[1]} ><div ref={btn[0]}>
+ <button className="round-button add-button" title={btn[2]} onPointerDown={SetupDrag(btn[0], btn[3])}>
+ <FontAwesomeIcon icon={btn[1]} size="sm" />
+ </button>
+ </div></li>)}
+ </ul>
+ </div>
+ </div >;
+ }
+
+ /* @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 logoutRef = React.createRef<HTMLDivElement>();
+
+ return [
+ <button className="clear-db-button" key="clear-db" onClick={e => e.shiftKey && e.altKey && DocServer.DeleteDatabase()}>Clear Database</button>,
+ <div id="toolbar" key="toolbar">
+ <button className="toolbar-button round-button" title="Undo" onClick={() => UndoManager.Undo()}><FontAwesomeIcon icon="undo-alt" size="sm" /></button>
+ <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-searchDiv" key="search" style={{ top: '34px', right: '1px', position: 'absolute' }} > <SearchBox /> </div>,
+ <div className="main-buttonDiv" key="logout" style={{ bottom: '0px', right: '1px', position: 'absolute' }} ref={logoutRef}>
+ <button onClick={() => request.get(DocServer.prepend(RouteStore.logout), emptyFunction)}>Log Out</button></div>
+ ];
+
+ }
+
+ render() {
+ return (
+ <div id="main-div">
+ <DocumentDecorations />
+ {this.mainContent}
+ <PreviewCursor />
+ <ContextMenu />
+ {this.nodesMenu()}
+ {this.miscButtons}
+ <InkingControl />
+ <MainOverlayTextBox />
+ </div>
+ );
+ }
+}
diff --git a/src/client/views/PresentationView.tsx b/src/client/views/PresentationView.tsx
index 7d0dc2913..9c37e9000 100644
--- a/src/client/views/PresentationView.tsx
+++ b/src/client/views/PresentationView.tsx
@@ -2,7 +2,6 @@ import { observer } from "mobx-react";
import React = require("react");
import { observable, action, runInAction, reaction } from "mobx";
import "./PresentationView.scss";
-import "./Main.tsx";
import { DocumentManager } from "../util/DocumentManager";
import { Utils } from "../../Utils";
import { Doc, DocListCast, DocListCastAsync } from "../../new_fields/Doc";
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 35a9f34cf..4cc4ae6b6 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -14,12 +14,12 @@ import { Doc, DocListCast } from '../../../new_fields/Doc';
import { Id } from '../../../new_fields/RefField';
import { ContextMenu } from '../ContextMenu';
import { undoBatch } from '../../util/UndoManager';
-import { Main } from '../Main';
import { CurrentUserUtils } from '../../../server/authentication/models/current_user_utils';
import { CollectionDockingView } from './CollectionDockingView';
import { DocumentManager } from '../../util/DocumentManager';
import { List } from '../../../new_fields/List';
import { Docs } from '../../documents/Documents';
+import { MainView } from '../MainView';
export interface TreeViewProps {
@@ -52,7 +52,7 @@ class TreeView extends React.Component<TreeViewProps> {
@undoBatch openRight = async () => {
if (this.props.document.dockingConfig) {
- Main.Instance.openWorkspace(this.props.document);
+ MainView.Instance.openWorkspace(this.props.document);
} else {
CollectionDockingView.Instance.AddRightSplit(this.props.document);
}
@@ -140,7 +140,7 @@ class TreeView extends React.Component<TreeViewProps> {
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
- ContextMenu.Instance.addItem({ description: "Open as Workspace", event: undoBatch(() => Main.Instance.openWorkspace(this.props.document)) });
+ ContextMenu.Instance.addItem({ description: "Open as Workspace", event: undoBatch(() => MainView.Instance.openWorkspace(this.props.document)) });
ContextMenu.Instance.addItem({ description: "Open Right", event: () => CollectionDockingView.Instance.AddRightSplit(this.props.document) });
ContextMenu.Instance.addItem({
description: "Open Fields", event: () => CollectionDockingView.Instance.AddRightSplit(Docs.KVPDocument(this.props.document,
@@ -215,7 +215,7 @@ 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()) });
+ ContextMenu.Instance.addItem({ description: "Create Workspace", event: undoBatch(() => MainView.Instance.createNewWorkspace()) });
}
if (!ContextMenu.Instance.getItems().some(item => item.description === "Delete")) {
ContextMenu.Instance.addItem({ description: "Delete", event: undoBatch(() => this.remove(this.props.Document)) });
diff --git a/src/debug/Viewer.tsx b/src/debug/Viewer.tsx
index 4cac09dee..d9b07aac6 100644
--- a/src/debug/Viewer.tsx
+++ b/src/debug/Viewer.tsx
@@ -3,184 +3,186 @@ import "normalize.css";
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { observer } from 'mobx-react';
-
-// configure({
-// enforceActions: "observed"
-// });
-
-// @observer
-// class FieldViewer extends React.Component<{ field: BasicField<any> }> {
-// render() {
-// return <span>{JSON.stringify(this.props.field.Data)} ({this.props.field.Id})</span>;
-// }
-// }
-
-// @observer
-// class KeyViewer extends React.Component<{ field: Key }> {
-// render() {
-// return this.props.field.Name;
-// }
-// }
-
-// @observer
-// class ListViewer extends React.Component<{ field: ListField<Field> }>{
-// @observable
-// expanded = false;
-
-// render() {
-// let content;
-// if (this.expanded) {
-// content = (
-// <div>
-// {this.props.field.Data.map(field => <DebugViewer fieldId={field.Id} key={field.Id} />)}
-// </div>
-// );
-// } else {
-// content = <>[...] ({this.props.field.Id})</>;
-// }
-// return (
-// <div>
-// <button onClick={action(() => this.expanded = !this.expanded)}>Toggle</button>
-// {content}
-// </div >
-// );
-// }
-// }
-
-// @observer
-// class DocumentViewer extends React.Component<{ field: Document }> {
-// private keyMap: ObservableMap<string, Key> = new ObservableMap;
-
-// private disposer?: Lambda;
-
-// componentDidMount() {
-// let f = () => {
-// Array.from(this.props.field._proxies.keys()).forEach(id => {
-// if (!this.keyMap.has(id)) {
-// Server.GetField(id, (field) => {
-// if (field && field instanceof Key) {
-// this.keyMap.set(id, field);
-// }
-// });
-// }
-// });
-// };
-// this.disposer = this.props.field._proxies.observe(f);
-// f();
-// }
-
-// componentWillUnmount() {
-// if (this.disposer) {
-// this.disposer();
-// }
-// }
-
-// render() {
-// let fields = Array.from(this.props.field._proxies.entries()).map(kv => {
-// let key = this.keyMap.get(kv[0]);
-// return (
-// <div key={kv[0]}>
-// <b>({key ? key.Name : kv[0]}): </b>
-// <DebugViewer fieldId={kv[1]}></DebugViewer>
-// </div>
-// );
-// });
-// return (
-// <div>
-// Document ({this.props.field.Id})
-// <div style={{ paddingLeft: "25px" }}>
-// {fields}
-// </div>
-// </div>
-// );
-// }
-// }
-
-// @observer
-// class DebugViewer extends React.Component<{ fieldId: string }> {
-// @observable
-// private field?: Field;
-
-// @observable
-// private error?: string;
-
-// constructor(props: { fieldId: string }) {
-// super(props);
-// this.update();
-// }
-
-// update() {
-// Server.GetField(this.props.fieldId, action((field: Opt<Field>) => {
-// this.field = field;
-// if (!field) {
-// this.error = `Field with id ${this.props.fieldId} not found`;
-// }
-// }));
-
-// }
-
-// render() {
-// let content;
-// if (this.field) {
-// // content = this.field.ToJson();
-// if (this.field instanceof ListField) {
-// content = (<ListViewer field={this.field} />);
-// } else if (this.field instanceof Document) {
-// content = (<DocumentViewer field={this.field} />);
-// } else if (this.field instanceof BasicField) {
-// content = (<FieldViewer field={this.field} />);
-// } else if (this.field instanceof Key) {
-// content = (<KeyViewer field={this.field} />);
-// } else {
-// content = (<span>Unrecognized field type</span>);
-// }
-// } else if (this.error) {
-// content = <span>Field <b>{this.props.fieldId}</b> not found <button onClick={() => this.update()}>Refresh</button></span>;
-// } else {
-// content = <span>Field loading: {this.props.fieldId}</span>;
-// }
-// return content;
-// }
-// }
-
-// @observer
-// class Viewer extends React.Component {
-// @observable
-// private idToAdd: string = '';
-
-// @observable
-// private ids: string[] = [];
-
-// @action
-// inputOnChange = (e: React.ChangeEvent<HTMLInputElement>) => {
-// this.idToAdd = e.target.value;
-// }
-
-// @action
-// onKeyPress = (e: React.KeyboardEvent<HTMLDivElement>) => {
-// if (e.key === "Enter") {
-// this.ids.push(this.idToAdd);
-// this.idToAdd = "";
-// }
-// }
-
-// render() {
-// return (
-// <>
-// <input value={this.idToAdd}
-// onChange={this.inputOnChange}
-// onKeyDown={this.onKeyPress} />
-// <div>
-// {this.ids.map(id => <DebugViewer fieldId={id} key={id}></DebugViewer>)}
-// </div>
-// </>
-// );
-// }
-// }
-
-// ReactDOM.render((
-// <div style={{ position: "absolute", width: "100%", height: "100%" }}>
-// <Viewer />
-// </div>),
-// document.getElementById('root')
-// ); \ No newline at end of file
+import { CurrentUserUtils } from '../server/authentication/models/current_user_utils';
+import { RouteStore } from '../server/RouteStore';
+import { emptyFunction } from '../Utils';
+import { Docs } from '../client/documents/Documents';
+import { SetupDrag } from '../client/util/DragManager';
+import { Transform } from '../client/util/Transform';
+import { UndoManager } from '../client/util/UndoManager';
+import { PresentationView } from '../client/views/PresentationView';
+import { CollectionDockingView } from '../client/views/collections/CollectionDockingView';
+import { ContextMenu } from '../client/views/ContextMenu';
+import { DocumentDecorations } from '../client/views/DocumentDecorations';
+import { InkingControl } from '../client/views/InkingControl';
+import { MainOverlayTextBox } from '../client/views/MainOverlayTextBox';
+import { DocumentView } from '../client/views/nodes/DocumentView';
+import { PreviewCursor } from '../client/views/PreviewCursor';
+import { SearchBox } from '../client/views/SearchBox';
+import { SelectionManager } from '../client/util/SelectionManager';
+import { Doc, Field, FieldResult } from '../new_fields/Doc';
+import { Cast } from '../new_fields/Types';
+import { DocServer } from '../client/DocServer';
+import { listSpec } from '../new_fields/Schema';
+import { Id } from '../new_fields/RefField';
+import { HistoryUtil } from '../client/util/History';
+import { List } from '../new_fields/List';
+import { URLField } from '../new_fields/URLField';
+
+CurrentUserUtils;
+RouteStore;
+emptyFunction;
+Docs;
+SetupDrag;
+Transform;
+UndoManager;
+PresentationView;
+CollectionDockingView;
+ContextMenu;
+DocumentDecorations;
+InkingControl;
+MainOverlayTextBox;
+DocumentView;
+PreviewCursor;
+SearchBox;
+SelectionManager;
+Doc;
+Cast;
+DocServer;
+listSpec;
+Id;
+HistoryUtil;
+
+configure({
+ enforceActions: "observed"
+});
+
+@observer
+class ListViewer extends React.Component<{ field: List<Field> }>{
+ @observable
+ expanded = false;
+
+ render() {
+ let content;
+ if (this.expanded) {
+ content = (
+ <div>
+ {this.props.field.map((field, index) => <DebugViewer field={field} key={index} />)}
+ </div>
+ );
+ } else {
+ content = <>[...]</>;
+ }
+ return (
+ <div>
+ <button onClick={action(() => this.expanded = !this.expanded)}>Toggle</button>
+ {content}
+ </div >
+ );
+ }
+}
+
+@observer
+class DocumentViewer extends React.Component<{ field: Doc }> {
+ @observable
+ expanded = false;
+ render() {
+ let content;
+ if (this.expanded) {
+ const keys = Object.keys(this.props.field);
+ let fields = keys.map(key => {
+ return (
+ <div key={key}>
+ <b>({key}): </b>
+ <DebugViewer field={this.props.field[key]}></DebugViewer>
+ </div>
+ );
+ });
+ content = (
+ <div>
+ Document ({this.props.field[Id]})
+ <div style={{ paddingLeft: "25px" }}>
+ {fields}
+ </div>
+ </div>
+ );
+ } else {
+ content = <>[...] ({this.props.field[Id]})</>;
+ }
+ return (
+ <div>
+ <button onClick={action(() => this.expanded = !this.expanded)}>Toggle</button>
+ {content}
+ </div >
+ );
+ }
+}
+
+@observer
+class DebugViewer extends React.Component<{ field: FieldResult }> {
+
+ render() {
+ let content;
+ const field = this.props.field;
+ if (field instanceof List) {
+ content = (<ListViewer field={field} />);
+ } else if (field instanceof Doc) {
+ content = (<DocumentViewer field={field} />);
+ } else if (typeof field === "string") {
+ content = <p>"{field}"</p>;
+ } else if (typeof field === "number" || typeof field === "boolean") {
+ content = <p>{field}</p>;
+ } else if (field instanceof URLField) {
+ content = <p>{field.url.href}</p>;
+ } else {
+ content = <p>Unrecognized field type</p>;
+ }
+ return content;
+ }
+}
+
+@observer
+class Viewer extends React.Component {
+ @observable
+ private idToAdd: string = '';
+
+ @observable
+ private fields: Field[] = [];
+
+ @action
+ inputOnChange = (e: React.ChangeEvent<HTMLInputElement>) => {
+ this.idToAdd = e.target.value;
+ }
+
+ @action
+ onKeyPress = (e: React.KeyboardEvent<HTMLDivElement>) => {
+ if (e.key === "Enter") {
+ DocServer.GetRefField(this.idToAdd).then(action((field: any) => {
+ if (field !== undefined) {
+ this.fields.push(field);
+ }
+ }));
+ this.idToAdd = "";
+ }
+ }
+
+ render() {
+ return (
+ <>
+ <input value={this.idToAdd}
+ onChange={this.inputOnChange}
+ onKeyDown={this.onKeyPress} />
+ <div>
+ {this.fields.map((field, index) => <DebugViewer field={field} key={index}></DebugViewer>)}
+ </div>
+ </>
+ );
+ }
+}
+
+ReactDOM.render((
+ <div style={{ position: "absolute", width: "100%", height: "100%" }}>
+ <Viewer />
+ </div>),
+ document.getElementById('root')
+); \ No newline at end of file