From 04fd1451fb33bba806f9fe406d51c13d8d4dd541 Mon Sep 17 00:00:00 2001 From: Monika Hedman Date: Tue, 16 Apr 2019 17:17:41 -0400 Subject: checking other search --- src/client/views/Main.tsx | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src/client/views/Main.tsx') diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx index 0469211fa..7d47c9932 100644 --- a/src/client/views/Main.tsx +++ b/src/client/views/Main.tsx @@ -262,6 +262,9 @@ export class Main extends React.Component { ,
, + +
hello
, +
]; -- cgit v1.2.3-70-g09d2 From 2181bc0f19b04a5c1a3e6e20206fc04a086995d2 Mon Sep 17 00:00:00 2001 From: Monika Hedman Date: Tue, 16 Apr 2019 17:28:56 -0400 Subject: attempting graphics --- src/client/views/Main.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/client/views/Main.tsx') diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx index 7d47c9932..92c7fa7dd 100644 --- a/src/client/views/Main.tsx +++ b/src/client/views/Main.tsx @@ -263,7 +263,7 @@ export class Main extends React.Component {
, -
hello
, +
,
-- cgit v1.2.3-70-g09d2 From 39bb878a32821d2e14110f4158471890234f4769 Mon Sep 17 00:00:00 2001 From: Monika Hedman Date: Mon, 29 Apr 2019 19:52:35 -0400 Subject: search UI --- src/client/views/Main.scss | 4 -- src/client/views/Main.tsx | 5 ++- src/client/views/SearchBox.scss | 64 +++++++++++++++++++++++++++++ src/client/views/SearchBox.tsx | 89 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 156 insertions(+), 6 deletions(-) create mode 100644 src/client/views/SearchBox.scss create mode 100644 src/client/views/SearchBox.tsx (limited to 'src/client/views/Main.tsx') diff --git a/src/client/views/Main.scss b/src/client/views/Main.scss index 26284974a..bca75da47 100644 --- a/src/client/views/Main.scss +++ b/src/client/views/Main.scss @@ -81,10 +81,6 @@ button:hover { cursor: pointer; } -search { - background: $dark-color; -} - .clear-db-button { position: absolute; right: 45%; diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx index 92c7fa7dd..87eefe67f 100644 --- a/src/client/views/Main.tsx +++ b/src/client/views/Main.tsx @@ -38,6 +38,7 @@ import "./Main.scss"; import { MainOverlayTextBox } from './MainOverlayTextBox'; import { DocumentView } from './nodes/DocumentView'; import { PreviewCursor } from './PreviewCursor'; +import { SearchBox } from './SearchBox'; @observer @@ -263,9 +264,9 @@ export class Main extends React.Component {
, -
, +
, -
+
]; } diff --git a/src/client/views/SearchBox.scss b/src/client/views/SearchBox.scss new file mode 100644 index 000000000..e1a1de142 --- /dev/null +++ b/src/client/views/SearchBox.scss @@ -0,0 +1,64 @@ +@import "globalCssVariables"; + +.searchBox { + height: 32px; + //display: flex; + //padding: 4px; + -webkit-transition: width 0.4s ease-in-out; + transition: width 0.4s ease-in-out; + align-items: center; + + .submit-search { + display: inline-block; + text-align: right; + color: $dark-color; + -webkit-transition: right 0.4s; + transition: right 0.4s; + } + + .submit-search:hover { + color: $main-accent; + transform: scale(1.05); + cursor: pointer; + } + + input[type=text] { + width: 130px; + -webkit-transition: width 0.4s; + transition: width 0.4s; + position: absolute; + right: 100px; + } + + input[type=text]:focus { + width: 500px; + outline: 3px solid lightblue; + } + + .filter-button { + position: absolute; + right: 30px; + } +} + +.filter-form { + background: $dark-color; + height: 400px; + width: 400px; + position: relative; + right: 1px; + color: $light-color; + padding: 10px; + flex-direction: column; +} + +#header { + text-transform: uppercase; + letter-spacing: 2px; + font-size: 100%; + height: 40px; +} + +#option { + height: 20px; +} \ No newline at end of file diff --git a/src/client/views/SearchBox.tsx b/src/client/views/SearchBox.tsx new file mode 100644 index 000000000..7f388719d --- /dev/null +++ b/src/client/views/SearchBox.tsx @@ -0,0 +1,89 @@ +import * as React from 'react'; +import { observer } from 'mobx-react'; +import { observable, action } from 'mobx'; +import { Utils } from '../../Utils'; +import { MessageStore } from '../../server/Message'; +import { Server } from '../Server'; +import "./SearchBox.scss"; +import { faSearch } from '@fortawesome/free-solid-svg-icons'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { library } from '@fortawesome/fontawesome-svg-core'; +import { actionFieldDecorator } from 'mobx/lib/internal'; + +library.add(faSearch); + +@observer +export class SearchBox extends React.Component { + @observable + searchString: string = ""; + + @observable private _open: boolean = false; + + @action.bound + onChange(e: React.ChangeEvent) { + this.searchString = e.target.value; + + } + + submitSearch = () => { + Utils.EmitCallback(Server.Socket, MessageStore.SearchFor, this.searchString, (results: string[]) => { + for (const result of results) { + console.log(result); + Utils.GetQueryVariable() + } + }); + } + + @action + handleClick = (e: Event): void => { + var className = (e.target as any).className; + var id = (e.target as any).id; + console.log(id); + //let imgPrev = document.getElementById("img_preview"); + console.log(className); + if (className !== "filter-button" && className !== "filter-form") { + console.log("false"); + this._open = false; + } + + } + + componentWillMount() { + document.addEventListener('mousedown', this.handleClick, false); + } + + componentWillUnmount() { + document.removeEventListener('mousedown', this.handleClick, false); + } + + @action + toggleDisplay = () => { + this._open = !this._open; + } + + render() { + return ( +
+
+ {/* + +
+
+
+ +
+ filter by collection, key, type of node +
+ +
+
+ + ); + } +} \ No newline at end of file -- cgit v1.2.3-70-g09d2 From 7d35ba0df5be5f3c2cd95c0f0d148c0c9c04de7a Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Sun, 5 May 2019 00:54:44 -0400 Subject: fix duplicate goldenlayout caused by presentation view --- src/client/views/Main.tsx | 9 +---- src/client/views/PresentationView.tsx | 62 ++++++++++++++++++++++------------- 2 files changed, 41 insertions(+), 30 deletions(-) (limited to 'src/client/views/Main.tsx') diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx index c3b48d20f..1a9b1cf3c 100644 --- a/src/client/views/Main.tsx +++ b/src/client/views/Main.tsx @@ -51,9 +51,6 @@ export class Main extends React.Component { } private set mainContainer(doc: Opt) { if (doc) { - if (!("presentationView" in doc)) { - doc.presentationView = new Doc(); - } CurrentUserUtils.UserDocument.activeWorkspace = doc; } } @@ -180,11 +177,7 @@ export class Main extends React.Component { @computed get presentationView() { - if (this.mainContainer) { - let presentation = FieldValue(Cast(this.mainContainer.presentationView, Doc)); - return presentation ? : (null); - } - return (null); + return ; } @computed diff --git a/src/client/views/PresentationView.tsx b/src/client/views/PresentationView.tsx index b4c12d057..f2d685443 100644 --- a/src/client/views/PresentationView.tsx +++ b/src/client/views/PresentationView.tsx @@ -1,18 +1,19 @@ import { observer } from "mobx-react"; import React = require("react") -import { observable, action } from "mobx"; +import { observable, action, runInAction, reaction } from "mobx"; import "./PresentationView.scss" import "./Main.tsx"; import { DocumentManager } from "../util/DocumentManager"; import { Utils } from "../../Utils"; import { Doc } from "../../new_fields/Doc"; import { listSpec } from "../../new_fields/Schema"; -import { Cast, NumCast, FieldValue } from "../../new_fields/Types"; +import { Cast, NumCast, FieldValue, PromiseValue } from "../../new_fields/Types"; import { Id } from "../../new_fields/RefField"; import { List } from "../../new_fields/List"; +import { CurrentUserUtils } from "../../server/authentication/models/current_user_utils"; export interface PresViewProps { - Document: Doc; + //Document: Doc; } @@ -22,6 +23,11 @@ export interface PresViewProps { */ class PresentationViewItem extends React.Component { + @observable Document?: Doc; + constructor(props: PresViewProps) { + super(props); + this.Document = FieldValue(Cast(FieldValue(Cast(CurrentUserUtils.UserDocument.activeWorkspace, Doc))!.presentationView, Doc))!; + } //look at CollectionFreeformView.focusDocument(d) @action openDoc = (doc: Doc) => { @@ -36,7 +42,7 @@ class PresentationViewItem extends React.Component { **/ @action public RemoveDoc(doc: Doc) { - const value = Cast(this.props.Document.data, listSpec(Doc), []); + const value = Cast(this.Document!.data, listSpec(Doc), []); let index = -1; for (let i = 0; i < value.length; i++) { if (value[i][Id] === doc[Id]) { @@ -57,10 +63,10 @@ class PresentationViewItem extends React.Component { let title = document.title; //to get currently selected presentation doc - let selected = NumCast(this.props.Document.selectedDoc, 0); + let selected = NumCast(this.Document!.selectedDoc, 0); // finally, if it's a normal document, then render it as such. - const children = Cast(this.props.Document.data, listSpec(Doc)); + const children = Cast(this.Document!.data, listSpec(Doc)); const styles: any = {}; if (children && children[selected] === document) { //this doc is selected @@ -76,7 +82,7 @@ class PresentationViewItem extends React.Component { } render() { - const children = Cast(this.props.Document.data, listSpec(Doc), []); + const children = Cast(this.Document!.data, listSpec(Doc), []); return (
@@ -94,13 +100,13 @@ export class PresentationView extends React.Component { //observable means render is re-called every time variable is changed @observable collapsed: boolean = false; - closePresentation = action(() => this.props.Document.width = 0); + closePresentation = action(() => this.Document!.width = 0); next = () => { - const current = NumCast(this.props.Document.selectedDoc); - const allDocs = FieldValue(Cast(this.props.Document.data, listSpec(Doc))); + const current = NumCast(this.Document!.selectedDoc); + const allDocs = FieldValue(Cast(this.Document!.data, listSpec(Doc))); if (allDocs && current < allDocs.length + 1) { //can move forwards - this.props.Document.selectedDoc = current + 1; + this.Document!.selectedDoc = current + 1; const doc = allDocs[current + 1]; let docView = DocumentManager.Instance.getDocumentView(doc); if (docView) { @@ -110,11 +116,11 @@ export class PresentationView extends React.Component { } back = () => { - const current = NumCast(this.props.Document.selectedDoc); - const allDocs = FieldValue(Cast(this.props.Document.data, listSpec(Doc))); + const current = NumCast(this.Document!.selectedDoc); + const allDocs = FieldValue(Cast(this.Document!.data, listSpec(Doc))); if (allDocs && current - 1 >= 0) { //can move forwards - this.props.Document.selectedDoc = current - 1; + this.Document!.selectedDoc = current - 1; const doc = allDocs[current - 1]; let docView = DocumentManager.Instance.getDocumentView(doc); if (docView) { @@ -125,9 +131,21 @@ export class PresentationView extends React.Component { private ref = React.createRef(); + @observable Document?: Doc; //initilize class variables constructor(props: PresViewProps) { super(props); + let self = this; + reaction(() => + CurrentUserUtils.UserDocument.activeWorkspace, + (activeW) => { + if (activeW && activeW instanceof Doc) { + PromiseValue(Cast(activeW.presentationView, Doc)). + then(pv => runInAction(() => + self.Document = pv ? pv : (activeW.presentationView = new Doc()))) + } + }, + { fireImmediately: true }); PresentationView.Instance = this; } @@ -137,19 +155,21 @@ export class PresentationView extends React.Component { @action public PinDoc(doc: Doc) { //add this new doc to props.Document - const data = Cast(this.props.Document.data, listSpec(Doc)); + const data = Cast(this.Document!.data, listSpec(Doc)); if (data) { data.push(doc); } else { - this.props.Document.data = new List([doc]); + this.Document!.data = new List([doc]); } - this.props.Document.width = 300; + this.Document!.width = 300; } render() { - let titleStr = this.props.Document.Title; - let width = NumCast(this.props.Document.width); + if (!this.Document) + return (null); + let titleStr = this.Document.Title; + let width = NumCast(this.Document.width); //TODO: next and back should be icons return ( @@ -163,9 +183,7 @@ export class PresentationView extends React.Component {
    - +
); -- cgit v1.2.3-70-g09d2 From 9a00685a16ba70f9bed18979508f17f316965045 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Sun, 5 May 2019 00:57:12 -0400 Subject: from last --- src/client/views/Main.tsx | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) (limited to 'src/client/views/Main.tsx') diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx index 1a9b1cf3c..617580332 100644 --- a/src/client/views/Main.tsx +++ b/src/client/views/Main.tsx @@ -174,19 +174,12 @@ export class Main extends React.Component { } }, 100); } - - @computed - get presentationView() { - return ; - } - @computed get mainContent() { let pwidthFunc = () => this.pwidth; let pheightFunc = () => this.pheight; let noScaling = () => 1; let mainCont = this.mainContainer; - let pcontent = this.presentationView; return { this.pwidth = r.entry.width; this.pheight = r.entry.height; })}> {({ measureRef }) =>
@@ -206,7 +199,7 @@ export class Main extends React.Component { whenActiveChanged={emptyFunction} bringToFront={emptyFunction} ContainingCollectionView={undefined} />} - {pcontent} +
}
; -- cgit v1.2.3-70-g09d2 From 11ab63f6c91093951fdc293c3d67e63073fb2f4c Mon Sep 17 00:00:00 2001 From: Monika Hedman Date: Tue, 7 May 2019 17:42:37 -0400 Subject: navigate to searched doc --- src/client/util/DocumentManager.ts | 30 +++++++++++++++++++++++++++++- src/client/views/Main.tsx | 2 +- src/client/views/SearchBox.tsx | 2 +- src/client/views/SearchItem.tsx | 16 ++-------------- src/client/views/nodes/LinkBox.tsx | 23 +---------------------- 5 files changed, 34 insertions(+), 39 deletions(-) (limited to 'src/client/views/Main.tsx') diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index 69964e2c9..3151bcfb5 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -1,8 +1,10 @@ import { computed, observable } from 'mobx'; import { DocumentView } from '../views/nodes/DocumentView'; import { Doc } from '../../new_fields/Doc'; -import { FieldValue, Cast } from '../../new_fields/Types'; +import { FieldValue, Cast, NumCast } from '../../new_fields/Types'; import { listSpec } from '../../new_fields/Schema'; +import { undoBatch } from './UndoManager'; +import { CollectionDockingView } from '../views/collections/CollectionDockingView'; export class DocumentManager { @@ -87,4 +89,30 @@ export class DocumentManager { return pairs; }, [] as { a: DocumentView, b: DocumentView, l: Doc }[]); } + + @undoBatch + public jumpToDocument = async (doc: Doc): Promise => { + let docView = DocumentManager.Instance.getDocumentView(doc); + if (docView) { + docView.props.focus(docView.props.Document); + } else { + const contextDoc = await Cast(doc.annotationOn, Doc); + if (!contextDoc) { + CollectionDockingView.Instance.AddRightSplit(Doc.MakeDelegate(doc)); + } else { + const page = NumCast(doc.page, undefined); + const curPage = NumCast(contextDoc.curPage, undefined); + if (page !== curPage) { + contextDoc.curPage = page; + } + let contextView = DocumentManager.Instance.getDocumentView(contextDoc); + if (contextView) { + contextDoc.panTransformType = "Ease"; + contextView.props.focus(contextDoc); + } else { + CollectionDockingView.Instance.AddRightSplit(contextDoc); + } + } + } + } } \ No newline at end of file diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx index 677902c5b..c9d5c395c 100644 --- a/src/client/views/Main.tsx +++ b/src/client/views/Main.tsx @@ -267,7 +267,7 @@ export class Main extends React.Component { ,
, -
+
]; diff --git a/src/client/views/SearchBox.tsx b/src/client/views/SearchBox.tsx index a52598f4c..827d468df 100644 --- a/src/client/views/SearchBox.tsx +++ b/src/client/views/SearchBox.tsx @@ -17,6 +17,7 @@ import { constant } from 'async'; import { DocServer } from '../DocServer'; import { Doc } from '../../new_fields/Doc'; import { Id } from '../../new_fields/RefField'; +import { DocumentManager } from '../util/DocumentManager'; library.add(faSearch); @@ -35,7 +36,6 @@ export class SearchBox extends React.Component { @action.bound onChange(e: React.ChangeEvent) { this.searchString = e.target.value; - console.log(this.searchString) } @action diff --git a/src/client/views/SearchItem.tsx b/src/client/views/SearchItem.tsx index 82cb5404c..81da7ebd2 100644 --- a/src/client/views/SearchItem.tsx +++ b/src/client/views/SearchItem.tsx @@ -1,27 +1,15 @@ import React = require("react"); import { Doc } from "../../new_fields/Doc"; +import { DocumentManager } from "../util/DocumentManager"; export interface SearchProps { doc: Doc; - //description: string; - //event: (e: React.MouseEvent) => void; } -// export interface SubmenuProps { -// description: string; -// subitems: ContextMenuProps[]; -// } - -// export interface ContextMenuItemProps { -// type: ContextMenuProps | SubmenuProps; -// } - - - export class SearchItem extends React.Component { onClick = () => { - console.log("document clicked: ", this.props.doc); + DocumentManager.Instance.jumpToDocument(this.props.doc) } render() { diff --git a/src/client/views/nodes/LinkBox.tsx b/src/client/views/nodes/LinkBox.tsx index 08cfa590b..611cb66b6 100644 --- a/src/client/views/nodes/LinkBox.tsx +++ b/src/client/views/nodes/LinkBox.tsx @@ -31,28 +31,7 @@ export class LinkBox extends React.Component { @undoBatch onViewButtonPressed = async (e: React.PointerEvent): Promise => { e.stopPropagation(); - let docView = DocumentManager.Instance.getDocumentView(this.props.pairedDoc); - if (docView) { - docView.props.focus(docView.props.Document); - } else { - const contextDoc = await Cast(this.props.pairedDoc.annotationOn, Doc); - if (!contextDoc) { - CollectionDockingView.Instance.AddRightSplit(Doc.MakeDelegate(this.props.pairedDoc)); - } else { - const page = NumCast(this.props.pairedDoc.page, undefined); - const curPage = NumCast(contextDoc.curPage, undefined); - if (page !== curPage) { - contextDoc.curPage = page; - } - let contextView = DocumentManager.Instance.getDocumentView(contextDoc); - if (contextView) { - contextDoc.panTransformType = "Ease"; - contextView.props.focus(contextDoc); - } else { - CollectionDockingView.Instance.AddRightSplit(contextDoc); - } - } - } + DocumentManager.Instance.jumpToDocument(this.props.pairedDoc); } onEditButtonPressed = (e: React.PointerEvent): void => { -- cgit v1.2.3-70-g09d2 From 0103346e8bae5113f5ea6f34d2745addac3a192e Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Sat, 11 May 2019 22:55:44 -0400 Subject: Changed measure params that we use --- src/client/views/Main.tsx | 2 +- src/client/views/collections/CollectionDockingView.tsx | 2 +- src/client/views/nodes/IconBox.tsx | 2 +- src/client/views/nodes/PDFBox.tsx | 4 ++-- src/client/views/nodes/VideoBox.tsx | 4 ++-- 5 files changed, 7 insertions(+), 7 deletions(-) (limited to 'src/client/views/Main.tsx') diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx index c9d5c395c..158de31f5 100644 --- a/src/client/views/Main.tsx +++ b/src/client/views/Main.tsx @@ -180,7 +180,7 @@ export class Main extends React.Component { let pheightFunc = () => this.pheight; let noScaling = () => 1; let mainCont = this.mainContainer; - return { this.pwidth = r.entry.width; this.pheight = r.entry.height; })}> + return { this.pwidth = r.offset.width; this.pheight = r.offset.height; })}> {({ measureRef }) =>
{!mainCont ? (null) : diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 5ee943da9..efcee9c02 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -419,7 +419,7 @@ export class DockedFrameRenderer extends React.Component { render() { let theContent = this.content; return !this._document ? (null) : - { this._panelWidth = r.entry.width; this._panelHeight = r.entry.height; })}> + { this._panelWidth = r.offset.width; this._panelHeight = r.offset.height; })}> {({ measureRef }) =>
{theContent}
}
; } diff --git a/src/client/views/nodes/IconBox.tsx b/src/client/views/nodes/IconBox.tsx index 48aecdcd0..071930633 100644 --- a/src/client/views/nodes/IconBox.tsx +++ b/src/client/views/nodes/IconBox.tsx @@ -59,7 +59,7 @@ export class IconBox extends React.Component { return (
{this.minimizedIcon} - runInAction(() => { if (r.entry.width || BoolCast(this.props.Document.hideLabel)) this.props.Document.nativeWidth = this.props.Document.width = (r.entry.width + Number(MINIMIZED_ICON_SIZE)); })}> + runInAction(() => { if (r.offset!.width || BoolCast(this.props.Document.hideLabel)) this.props.Document.nativeWidth = this.props.Document.width = (r.offset!.width + Number(MINIMIZED_ICON_SIZE)); })}> {({ measureRef }) => {label} } diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index caa66cbeb..f2fa6f47c 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -279,7 +279,7 @@ export class PDFBox extends DocComponent(PdfDocumen // so this design is flawed. var nativeWidth = FieldValue(this.Document.nativeWidth, 0); if (!FieldValue(this.Document.nativeHeight, 0)) { - var nativeHeight = nativeWidth * r.entry.height / r.entry.width; + var nativeHeight = nativeWidth * r.offset.height / r.offset.width; this.props.Document.height = nativeHeight / nativeWidth * FieldValue(this.Document.width, 0); this.props.Document.nativeHeight = nativeHeight; } @@ -297,7 +297,7 @@ export class PDFBox extends DocComponent(PdfDocumen let xf = FieldValue(this.Document.nativeHeight, 0) / renderHeight; let body = NumCast(this.props.Document.nativeHeight) ? this.pdfPage : - + {({ measureRef }) =>
{this.pdfPage} diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index 422508f90..97c5d8818 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -35,7 +35,7 @@ export class VideoBox extends DocComponent(VideoD // bcz: the nativeHeight should really be set when the document is imported. var nativeWidth = FieldValue(this.Document.nativeWidth, 0); var nativeHeight = FieldValue(this.Document.nativeHeight, 0); - var newNativeHeight = nativeWidth * r.entry.height / r.entry.width; + var newNativeHeight = nativeWidth * r.offset.height / r.offset.width; if (!nativeHeight && newNativeHeight !== nativeHeight && !isNaN(newNativeHeight)) { this.Document.height = newNativeHeight / nativeWidth * FieldValue(this.Document.width, 0); this.Document.nativeHeight = newNativeHeight; @@ -71,7 +71,7 @@ export class VideoBox extends DocComponent(VideoD let content = this.videoContent(field.url.href); return NumCast(this.props.Document.nativeHeight) ? content : - + {({ measureRef }) =>
{content} -- cgit v1.2.3-70-g09d2 From c3a24f3cd2d1d3baf5738c649552baadf3677385 Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Tue, 14 May 2019 01:31:11 -0400 Subject: Refactored most of presentation view --- src/client/views/Main.scss | 19 --- src/client/views/Main.tsx | 57 ++++--- src/client/views/PresentationView.scss | 71 +++++---- src/client/views/PresentationView.tsx | 166 ++++++++------------- src/client/views/TemplateMenu.tsx | 1 - .../views/collections/CollectionDockingView.tsx | 13 +- src/client/views/nodes/LinkMenu.tsx | 2 +- src/new_fields/Doc.ts | 10 +- 8 files changed, 155 insertions(+), 184 deletions(-) (limited to 'src/client/views/Main.tsx') diff --git a/src/client/views/Main.scss b/src/client/views/Main.scss index 2430e8f6c..d63b0147b 100644 --- a/src/client/views/Main.scss +++ b/src/client/views/Main.scss @@ -27,25 +27,6 @@ div { z-index: 9999; } -h1 { - font-size: 50px; - position: fixed; - top: 30px; - left: 50%; - transform: translateX(-50%); - color: $dark-color; - text-shadow: -1px -1px 0 #fff, 1px -1px 0 #fff, -1px 1px 0 #fff, 1px 1px 0 #fff; - z-index: 9999; - font-family: $sans-serif; - font-weight: 700; - -webkit-touch-callout: none; - -webkit-user-select: none; - -khtml-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; -} - .jsx-parser { width: 100%; pointer-events: none; diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx index 158de31f5..66205f8ca 100644 --- a/src/client/views/Main.tsx +++ b/src/client/views/Main.tsx @@ -1,7 +1,7 @@ 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 } from 'mobx'; +import { action, computed, configure, observable, runInAction, trace } from 'mobx'; import { observer } from 'mobx-react'; import "normalize.css"; import * as React from 'react'; @@ -51,6 +51,9 @@ export class Main extends React.Component { } private set mainContainer(doc: Opt) { if (doc) { + if (!("presentationView" in doc)) { + doc.presentationView = new Doc(); + } CurrentUserUtils.UserDocument.activeWorkspace = doc; } } @@ -174,32 +177,42 @@ export class Main extends React.Component { } }, 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 pwidthFunc = () => this.pwidth; - let pheightFunc = () => this.pheight; - let noScaling = () => 1; let mainCont = this.mainContainer; - return { this.pwidth = r.offset.width; this.pheight = r.offset.height; })}> + let content = !mainCont ? (null) : + ; + const pres = mainCont ? FieldValue(Cast(mainCont.presentationView, Doc)) : undefined; + return {({ measureRef }) =>
- {!mainCont ? (null) : - } - + {content} + {pres ? : null}
}
; diff --git a/src/client/views/PresentationView.scss b/src/client/views/PresentationView.scss index 7c5677f0d..fb4a851c4 100644 --- a/src/client/views/PresentationView.scss +++ b/src/client/views/PresentationView.scss @@ -4,15 +4,14 @@ z-index: 1; box-shadow: #AAAAAA .2vw .2vw .4vw; right: 0; - top:0; - bottom:0; + top: 0; + bottom: 0; } .presentationView-item { - width: 220px; - height: 40px; - vertical-align: center; - padding-top: 15px; + padding: 10px; + display: inline-block; + width: 100%; -webkit-touch-callout: none; -webkit-user-select: none; -khtml-user-select: none; @@ -22,47 +21,59 @@ transition: all .1s; } +.presentationView-listCont { + padding-left: 10px; + padding-right: 10px; +} + .presentationView-item:hover { transition: all .1s; background: #AAAAAA } +.presentationView-selected { + background: gray; +} + .presentationView-heading { - margin-top: 0px; - height: 40px; background: lightseagreen; - padding: 30px; + padding: 10px; + display: inline-block; + width: 100%; } + .presentationView-title { - padding-top: 3px; - padding-bottom: 3px; - font-size: 25px; - float:left; + padding-top: 3px; + padding-bottom: 3px; + font-size: 25px; + display: inline-block; } -.presentation-icon{ + +.presentation-icon { float: right; - display: inline; - width: 10px; - margin-top: 7px; } -.presentationView-header { - padding-top: 1px; - padding-bottom: 1px; + +.presentationView-name { font-size: 15px; - float:left; - } + display: inline-block; +} + +.presentation-button { + margin-right: 12.5%; + margin-left: 12.5%; + width: 25%; +} - .presentation-next{ - float: right; - } - .presentation-back{ - float: left; - } - .presentation-next:hover{ +.presentation-buttons { + padding: 10px; +} + +.presentation-next:hover { transition: all .1s; background: #AAAAAA } -.presentation-back:hover{ + +.presentation-back:hover { transition: all .1s; background: #AAAAAA } \ No newline at end of file diff --git a/src/client/views/PresentationView.tsx b/src/client/views/PresentationView.tsx index 3fb24a339..098e725c7 100644 --- a/src/client/views/PresentationView.tsx +++ b/src/client/views/PresentationView.tsx @@ -5,15 +5,20 @@ import "./PresentationView.scss" import "./Main.tsx"; import { DocumentManager } from "../util/DocumentManager"; import { Utils } from "../../Utils"; -import { Doc } from "../../new_fields/Doc"; +import { Doc, DocListCast, DocListCastAsync } from "../../new_fields/Doc"; import { listSpec } from "../../new_fields/Schema"; -import { Cast, NumCast, FieldValue, PromiseValue } from "../../new_fields/Types"; +import { Cast, NumCast, FieldValue, PromiseValue, StrCast } from "../../new_fields/Types"; import { Id } from "../../new_fields/RefField"; import { List } from "../../new_fields/List"; import { CurrentUserUtils } from "../../server/authentication/models/current_user_utils"; export interface PresViewProps { - //Document: Doc; + Document: Doc; +} + +interface PresListProps extends PresViewProps { + deleteDocument(index: number): void; + gotoDocument(index: number): void; } @@ -21,72 +26,40 @@ export interface PresViewProps { /** * Component that takes in a document prop and a boolean whether it's collapsed or not. */ -class PresentationViewItem extends React.Component { - - @observable Document: Doc; - constructor(props: PresViewProps) { - super(props); - this.Document = FieldValue(Cast(FieldValue(Cast(CurrentUserUtils.UserDocument.activeWorkspace, Doc))!.presentationView, Doc))!; - } - //look at CollectionFreeformView.focusDocument(d) - @action - openDoc = (doc: Doc) => { - let docView = DocumentManager.Instance.getDocumentView(doc); - if (docView) { - docView.props.focus(docView.props.Document); - } - } - - /** - * Removes a document from the presentation view - **/ - @action - public RemoveDoc(doc: Doc) { - const value = Cast(this.Document.data, listSpec(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); - } - } +class PresentationViewList extends React.Component { /** * Renders a single child document. It will just append a list element. * @param document The document to render. */ - renderChild(document: Doc) { + renderChild = (document: Doc, index: number) => { let title = document.title; //to get currently selected presentation doc - let selected = NumCast(this.Document.selectedDoc, 0); + let selected = NumCast(this.props.Document.selectedDoc, 0); - // finally, if it's a normal document, then render it as such. - const children = Cast(this.Document.data, listSpec(Doc)); - const styles: any = {}; - if (children && children[selected] === document) { + let className = "presentationView-item"; + if (selected === index) { //this doc is selected - styles.background = "gray"; + className += " presentationView-selected"; } return ( -
  • -
    this.openDoc(document)}>{title}
    -
    this.RemoveDoc(document)}>X
    -
  • +
    { this.props.gotoDocument(index); e.stopPropagation(); }}> + + {`${index + 1}. ${title}`} + + +
    ); } render() { - const children = Cast(this.Document.data, listSpec(Doc), []); + const children = DocListCast(this.props.Document.data); return ( -
    - {children.map(value => this.renderChild(value))} +
    + {children.map(this.renderChild)}
    ); } @@ -100,59 +73,42 @@ export class PresentationView extends React.Component { //observable means render is re-called every time variable is changed @observable collapsed: boolean = false; - closePresentation = action(() => this.Document!.width = 0); + closePresentation = action(() => this.props.Document.width = 0); next = () => { - const current = NumCast(this.Document!.selectedDoc); - const allDocs = FieldValue(Cast(this.Document!.data, listSpec(Doc))); - if (allDocs && current < allDocs.length + 1) { - //can move forwards - this.Document!.selectedDoc = current + 1; - const doc = allDocs[current + 1]; - let docView = DocumentManager.Instance.getDocumentView(doc); - if (docView) { - docView.props.focus(docView.props.Document); - } - } + const current = NumCast(this.props.Document.selectedDoc); + this.gotoDocument(current + 1); } back = () => { - const current = NumCast(this.Document!.selectedDoc); - const allDocs = FieldValue(Cast(this.Document!.data, listSpec(Doc))); - if (allDocs && current - 1 >= 0) { - //can move forwards - this.Document!.selectedDoc = current - 1; - const doc = allDocs[current - 1]; - let docView = DocumentManager.Instance.getDocumentView(doc); - if (docView) { - docView.props.focus(docView.props.Document); - } + const current = NumCast(this.props.Document.selectedDoc); + this.gotoDocument(current - 1); + } + + @action + public RemoveDoc = (index: number) => { + const value = FieldValue(Cast(this.props.Document.data, listSpec(Doc))); + if (value) { + value.splice(index, 1); } } - private ref = React.createRef(); + public gotoDocument = async (index: number) => { + const list = FieldValue(Cast(this.props.Document.data, listSpec(Doc))); + if (!list) { + return; + } + if (index < 0 || index >= list.length) { + return; + } + + this.props.Document.selectedDoc = index; + const doc = await list[index]; + DocumentManager.Instance.jumpToDocument(doc); + } - @observable Document?: Doc; //initilize class variables constructor(props: PresViewProps) { super(props); - let self = this; - reaction(() => - CurrentUserUtils.UserDocument.activeWorkspace, - (activeW) => { - if (activeW && activeW instanceof Doc) { - PromiseValue(Cast(activeW.presentationView, Doc)). - then(pv => runInAction(() => { - if (pv) self.Document = pv; - else { - pv = new Doc(); - pv.title = "Presentation Doc"; - activeW.presentationView = pv; - self.Document = pv; - } - })) - } - }, - { fireImmediately: true }); PresentationView.Instance = this; } @@ -162,36 +118,32 @@ export class PresentationView extends React.Component { @action public PinDoc(doc: Doc) { //add this new doc to props.Document - const data = Cast(this.Document!.data, listSpec(Doc)); + const data = Cast(this.props.Document.data, listSpec(Doc)); if (data) { data.push(doc); } else { - this.Document!.data = new List([doc]); + this.props.Document.data = new List([doc]); } - this.Document!.width = 300; + this.props.Document.width = 300; } render() { - if (!this.Document) - return (null); - let titleStr = this.Document.Title; - let width = NumCast(this.Document.width); + let titleStr = StrCast(this.props.Document.title); + let width = NumCast(this.props.Document.width); //TODO: next and back should be icons return (
    {titleStr}
    -
    X
    -
    -
    back
    -
    next
    - + +
    +
    + +
    -
      - -
    +
    ); } diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx index e2b3bd07a..22c4edc25 100644 --- a/src/client/views/TemplateMenu.tsx +++ b/src/client/views/TemplateMenu.tsx @@ -38,7 +38,6 @@ export class TemplateMenu extends React.Component { constructor(props: TemplateMenuProps) { super(props); - console.log(""); } @action diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index a755e0f91..6651a834d 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -1,6 +1,6 @@ import 'golden-layout/src/css/goldenlayout-base.css'; import 'golden-layout/src/css/goldenlayout-dark-theme.css'; -import { action, observable, reaction } from "mobx"; +import { action, observable, reaction, Lambda } from "mobx"; import { observer } from "mobx-react"; import * as ReactDOM from 'react-dom'; import Measure from "react-measure"; @@ -188,12 +188,16 @@ export class CollectionDockingView extends React.Component void = () => { + console.log("Docking mount"); if (this._containerRef.current) { - reaction( + this.reactionDisposer = reaction( () => StrCast(this.props.Document.dockingConfig), () => { if (!this._goldenLayout || this._ignoreStateChange !== JSON.stringify(this._goldenLayout.toConfig())) { + // Because this is in a set timeout, if this component unmounts right after mounting, + // we will leak a GoldenLayout, because we try to destroy it before we ever create it setTimeout(() => this.setupGoldenLayout(), 1); } this._ignoreStateChange = ""; @@ -203,6 +207,7 @@ export class CollectionDockingView extends React.Component void = () => { + console.log("Docking unmount"); try { this._goldenLayout.unbind('itemDropped', this.itemDropped); this._goldenLayout.unbind('tabCreated', this.tabCreated); @@ -214,6 +219,10 @@ export class CollectionDockingView extends React.Component { diff --git a/src/client/views/nodes/LinkMenu.tsx b/src/client/views/nodes/LinkMenu.tsx index 5dabfc30d..11117122d 100644 --- a/src/client/views/nodes/LinkMenu.tsx +++ b/src/client/views/nodes/LinkMenu.tsx @@ -36,7 +36,7 @@ export class LinkMenu extends React.Component { if (this._editingLink === undefined) { return (
    - + {/* */}
    {this.renderLinkItems(linkTo, "linkedTo", "Destination: ")} {this.renderLinkItems(linkFrom, "linkedFrom", "Source: ")} diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index 4c837fcbd..89901490d 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -29,15 +29,21 @@ export const SelfProxy = Symbol("SelfProxy"); export const WidthSym = Symbol("Width"); export const HeightSym = Symbol("Height"); +/** + * Cast any field to either a List of Docs or undefined if the given field isn't a List of Docs. + * If a default value is given, that will be returned instead of undefined. + * If a default value is given, the returned value should not be modified as it might be a temporary value. + * If no default value is given, and the returned value is not undefined, it can be safely modified. + */ export function DocListCastAsync(field: FieldResult): Promise; export function DocListCastAsync(field: FieldResult, defaultValue: Doc[]): Promise; export function DocListCastAsync(field: FieldResult, defaultValue?: Doc[]) { const list = Cast(field, listSpec(Doc)); - return list ? Promise.all(list) : Promise.resolve(defaultValue); + return list ? Promise.all(list).then(() => list) : Promise.resolve(defaultValue); } export function DocListCast(field: FieldResult) { - return Cast(field, listSpec(Doc), []).filter(d => d && d instanceof Doc).map(d => d as Doc) + return Cast(field, listSpec(Doc), []).filter(d => d && d instanceof Doc).map(d => d as Doc); } @Deserializable("doc").withFields(["id"]) -- cgit v1.2.3-70-g09d2 From 281bfa589bd1ca66823003ca91cc11ce895fe5e2 Mon Sep 17 00:00:00 2001 From: bob Date: Tue, 14 May 2019 18:59:32 -0400 Subject: fixes for northstar --- src/client/documents/Documents.ts | 9 ++- src/client/northstar/dash-fields/HistogramField.ts | 11 ++-- .../dash-nodes/HistogramBinPrimitiveCollection.ts | 4 +- src/client/northstar/dash-nodes/HistogramBox.tsx | 16 +++--- .../dash-nodes/HistogramBoxPrimitives.tsx | 3 - src/client/northstar/manager/Gateway.ts | 12 ++++ src/client/northstar/model/ModelHelpers.ts | 10 +++- src/client/northstar/model/idea/idea.ts | 6 ++ src/client/northstar/operations/BaseOperation.ts | 17 ------ .../northstar/operations/HistogramOperation.ts | 12 ++-- src/client/views/Main.tsx | 54 +----------------- .../views/collections/CollectionSchemaView.tsx | 39 ++++++++++++- .../collections/collectionFreeForm/MarqueeView.tsx | 7 ++- src/client/views/nodes/DocumentView.tsx | 2 +- .../authentication/models/current_user_utils.ts | 65 ++++++++++++++++++++-- 15 files changed, 162 insertions(+), 105 deletions(-) (limited to 'src/client/views/Main.tsx') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 11929455c..ed260d42e 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -33,6 +33,7 @@ import { DocServer } from "../DocServer"; import { StrokeData, InkField } from "../../new_fields/InkField"; import { dropActionType } from "../util/DragManager"; import { DateField } from "../../new_fields/DateField"; +import { schema } from "prosemirror-schema-basic"; export interface DocumentOptions { x?: number; @@ -207,16 +208,18 @@ export namespace Docs { export function PdfDocument(url: string, options: DocumentOptions = {}) { return CreateInstance(pdfProto, new PdfField(new URL(url)), options); } + export async function DBDocument(url: string, options: DocumentOptions = {}) { let schemaName = options.title ? options.title : "-no schema-"; let ctlog = await Gateway.Instance.GetSchema(url, schemaName); if (ctlog && ctlog.schemas) { let schema = ctlog.schemas[0]; let schemaDoc = Docs.TreeDocument([], { ...options, nativeWidth: undefined, nativeHeight: undefined, width: 150, height: 100, title: schema.displayName! }); - let schemaDocuments = Cast(schemaDoc.data, listSpec(Doc)); + let schemaDocuments = Cast(schemaDoc.data, listSpec(Doc), []); if (!schemaDocuments) { return; } + CurrentUserUtils.AddNorthstarSchema(schema, schemaDoc); const docs = schemaDocuments; CurrentUserUtils.GetAllNorthstarColumnAttributes(schema).map(attr => { DocServer.GetRefField(attr.displayName! + ".alias").then(action((field: Opt) => { @@ -251,8 +254,8 @@ export namespace Docs { } return CreateInstance(collProto, new List(documents), { schemaColumns: new List(["title"]), ...options, viewType: CollectionViewType.Freeform }); } - export function SchemaDocument(documents: Array, options: DocumentOptions) { - return CreateInstance(collProto, new List(documents), { schemaColumns: new List(["title"]), ...options, viewType: CollectionViewType.Schema }); + export function SchemaDocument(schemaColumns: string[], documents: Array, options: DocumentOptions) { + return CreateInstance(collProto, new List(documents), { schemaColumns: new List(schemaColumns), ...options, viewType: CollectionViewType.Schema }); } export function TreeDocument(documents: Array, options: DocumentOptions) { return CreateInstance(collProto, new List(documents), { schemaColumns: new List(["title"]), ...options, viewType: CollectionViewType.Tree }); diff --git a/src/client/northstar/dash-fields/HistogramField.ts b/src/client/northstar/dash-fields/HistogramField.ts index f01f08487..aabc77bb2 100644 --- a/src/client/northstar/dash-fields/HistogramField.ts +++ b/src/client/northstar/dash-fields/HistogramField.ts @@ -9,7 +9,8 @@ import { OmitKeys } from "../../../Utils"; import { Deserializable } from "../../util/SerializationHelper"; function serialize(field: HistogramField) { - return OmitKeys(field.HistoOp, ['Links', 'BrushLinks', 'Result', 'BrushColors', 'FilterModels', 'FilterOperand']).omit; + let obj = OmitKeys(field, ['Links', 'BrushLinks', 'Result', 'BrushColors', 'FilterModels', 'FilterOperand']).omit; + return obj; } function deserialize(jp: any) { @@ -31,10 +32,10 @@ function deserialize(jp: any) { } }); if (X && Y && V) { - return new HistogramField(new HistogramOperation(jp.SchemaName, X, Y, V, jp.Normalization)); + return new HistogramOperation(jp.SchemaName, X, Y, V, jp.Normalization); } } - return new HistogramField(HistogramOperation.Empty); + return HistogramOperation.Empty; } @Deserializable("histogramField") @@ -50,6 +51,8 @@ export class HistogramField extends ObjectField { } [Copy]() { - return new HistogramField(this.HistoOp.Copy()); + let y = this.HistoOp; + let z = this.HistoOp["Copy"]; + return new HistogramField(HistogramOperation.Duplicate(this.HistoOp)); } } \ No newline at end of file diff --git a/src/client/northstar/dash-nodes/HistogramBinPrimitiveCollection.ts b/src/client/northstar/dash-nodes/HistogramBinPrimitiveCollection.ts index 6291ec1fc..3e9145a1b 100644 --- a/src/client/northstar/dash-nodes/HistogramBinPrimitiveCollection.ts +++ b/src/client/northstar/dash-nodes/HistogramBinPrimitiveCollection.ts @@ -198,8 +198,8 @@ export class HistogramBinPrimitiveCollection { var marginParams = new MarginAggregateParameters(); marginParams.aggregateFunction = axis.AggregateFunction; var marginAggregateKey = ModelHelpers.CreateAggregateKey(this.histoOp.Schema!.distinctAttributeParameters, axis, this.histoResult, brush.brushIndex!, marginParams); - var marginResult = ModelHelpers.GetAggregateResult(bin, marginAggregateKey) as MarginAggregateResult; - return !marginResult ? 0 : marginResult.absolutMargin!; + let aggResult = ModelHelpers.GetAggregateResult(bin, marginAggregateKey); + return aggResult instanceof MarginAggregateResult && aggResult.absolutMargin ? aggResult.absolutMargin : 0; } private createBinPrimitive(barAxis: number, brush: Brush, marginRect: PIXIRectangle, diff --git a/src/client/northstar/dash-nodes/HistogramBox.tsx b/src/client/northstar/dash-nodes/HistogramBox.tsx index ed556cf45..eb1ad69b7 100644 --- a/src/client/northstar/dash-nodes/HistogramBox.tsx +++ b/src/client/northstar/dash-nodes/HistogramBox.tsx @@ -17,9 +17,8 @@ import "./HistogramBox.scss"; import { HistogramBoxPrimitives } from './HistogramBoxPrimitives'; import { HistogramLabelPrimitives } from "./HistogramLabelPrimitives"; import { StyleConstants } from "../utils/StyleContants"; -import { NumCast, Cast } from "../../../new_fields/Types"; -import { listSpec } from "../../../new_fields/Schema"; -import { Doc, DocListCast } from "../../../new_fields/Doc"; +import { Cast } from "../../../new_fields/Types"; +import { Doc, DocListCast, DocListCastAsync } from "../../../new_fields/Doc"; import { Id } from "../../../new_fields/RefField"; @@ -119,15 +118,16 @@ export class HistogramBox extends React.Component { if (this.HistoOp !== HistogramOperation.Empty) { reaction(() => DocListCast(this.props.Document.linkedFromDocs), (docs) => this.HistoOp.Links.splice(0, this.HistoOp.Links.length, ...docs), { fireImmediately: true }); reaction(() => DocListCast(this.props.Document.brushingDocs).length, - () => { - let brushingDocs = DocListCast(this.props.Document.brushingDocs); + async () => { + let brushingDocs = await DocListCastAsync(this.props.Document.brushingDocs); const proto = this.props.Document.proto; - if (proto) { - this.HistoOp.BrushLinks.splice(0, this.HistoOp.BrushLinks.length, ...brushingDocs.map((brush, i) => { + if (proto && brushingDocs) { + let mapped = brushingDocs.map((brush, i) => { brush.backgroundColor = StyleConstants.BRUSH_COLORS[i % StyleConstants.BRUSH_COLORS.length]; let brushed = DocListCast(brush.brushingDocs); return { l: brush, b: brushed[0][Id] === proto[Id] ? brushed[1] : brushed[0] }; - })); + }); + this.HistoOp.BrushLinks.splice(0, this.HistoOp.BrushLinks.length, ...mapped); } }, { fireImmediately: true }); reaction(() => this.createOperationParamsCache, () => this.HistoOp.Update(), { fireImmediately: true }); diff --git a/src/client/northstar/dash-nodes/HistogramBoxPrimitives.tsx b/src/client/northstar/dash-nodes/HistogramBoxPrimitives.tsx index 721bf6a89..350987695 100644 --- a/src/client/northstar/dash-nodes/HistogramBoxPrimitives.tsx +++ b/src/client/northstar/dash-nodes/HistogramBoxPrimitives.tsx @@ -11,9 +11,6 @@ import { StyleConstants } from "../../northstar/utils/StyleContants"; import { HistogramBinPrimitiveCollection, HistogramBinPrimitive } from "./HistogramBinPrimitiveCollection"; import { HistogramBox } from "./HistogramBox"; import "./HistogramBoxPrimitives.scss"; -import { JSXElement } from "babel-types"; -import { Utils } from "../utils/Utils"; -import { all } from "bluebird"; export interface HistogramPrimitivesProps { HistoBox: HistogramBox; diff --git a/src/client/northstar/manager/Gateway.ts b/src/client/northstar/manager/Gateway.ts index d26f2724f..c541cce6a 100644 --- a/src/client/northstar/manager/Gateway.ts +++ b/src/client/northstar/manager/Gateway.ts @@ -23,6 +23,18 @@ export class Gateway { } } + public async PostSchema(csvdata: string, schemaname: string): Promise { + try { + const json = await this.MakePostJsonRequest("postSchema", { csv: csvdata, schema: schemaname }); + // const cat = Catalog.fromJS(json); + // return cat; + return json; + } + catch (error) { + throw new Error("can not reach northstar's backend"); + } + } + public async GetSchema(pathname: string, schemaname: string): Promise { try { const json = await this.MakeGetRequest("schema", undefined, { path: pathname, schema: schemaname }); diff --git a/src/client/northstar/model/ModelHelpers.ts b/src/client/northstar/model/ModelHelpers.ts index ac807b41f..80bb71224 100644 --- a/src/client/northstar/model/ModelHelpers.ts +++ b/src/client/northstar/model/ModelHelpers.ts @@ -31,7 +31,15 @@ export class ModelHelpers { } public static GetAggregateParametersIndex(histogramResult: HistogramResult, aggParameters?: AggregateParameters): number { - return ArrayUtil.IndexOfWithEqual(histogramResult.aggregateParameters!, aggParameters); + return Array.from(histogramResult.aggregateParameters!).findIndex((value, i, set) => { + if (set[i] instanceof CountAggregateParameters && value instanceof CountAggregateParameters) + return true; + if (set[i] instanceof MarginAggregateParameters && value instanceof MarginAggregateParameters) + return true; + if (set[i] instanceof SumAggregateParameters && value instanceof SumAggregateParameters) + return true; + return false; + }); } public static GetAggregateParameter(distinctAttributeParameters: AttributeParameters | undefined, atm: AttributeTransformationModel): AggregateParameters | undefined { diff --git a/src/client/northstar/model/idea/idea.ts b/src/client/northstar/model/idea/idea.ts index 9d9d60678..c73a822c7 100644 --- a/src/client/northstar/model/idea/idea.ts +++ b/src/client/northstar/model/idea/idea.ts @@ -22,6 +22,9 @@ export abstract class AggregateParameters implements IAggregateParameters { protected _discriminator: string; + public Equals(other: Object): boolean { + return this == other; + } constructor(data?: IAggregateParameters) { if (data) { for (var property in data) { @@ -204,6 +207,9 @@ export interface IAverageAggregateParameters extends ISingleDimensionAggregatePa export abstract class AttributeParameters implements IAttributeParameters { visualizationHints?: VisualizationHint[] | undefined; rawName?: string | undefined; + public Equals(other: Object): boolean { + return this == other; + } protected _discriminator: string; diff --git a/src/client/northstar/operations/BaseOperation.ts b/src/client/northstar/operations/BaseOperation.ts index c6d5f0a15..0d1361ebf 100644 --- a/src/client/northstar/operations/BaseOperation.ts +++ b/src/client/northstar/operations/BaseOperation.ts @@ -25,23 +25,6 @@ export abstract class BaseOperation { @computed public get FilterString(): string { - - // let filterModels: FilterModel[] = []; - // return FilterModel.GetFilterModelsRecursive(this, new Set>(), filterModels, true) - // if (this.OverridingFilters.length > 0) { - // return "(" + this.OverridingFilters.filter(fm => fm !== null).map(fm => fm.ToPythonString()).join(" || ") + ")"; - // } - // let rdg = MainManager.Instance.MainViewModel.FilterReverseDependencyGraph; - // let sliceModel = this.TypedViewModel.IncomingSliceModel; - // if (sliceModel !== null && sliceModel.Source !== null && instanceOfIBaseFilterProvider(sliceModel.Source) && rdg.has(sliceModel.Source)) { - // let filterModels = sliceModel.Source.FilterModels.map(f => f); - // return FilterModel.GetFilterModelsRecursive(rdg.get(sliceModel.Source), new Set>(), filterModels, false); - // } - - // if (rdg.has(this.TypedViewModel)) { - // let filterModels = []; - // return FilterModel.GetFilterModelsRecursive(rdg.get(this.TypedViewModel), new Set>(), filterModels, true) - // } return ""; } diff --git a/src/client/northstar/operations/HistogramOperation.ts b/src/client/northstar/operations/HistogramOperation.ts index 78b206bdc..74e23ea48 100644 --- a/src/client/northstar/operations/HistogramOperation.ts +++ b/src/client/northstar/operations/HistogramOperation.ts @@ -30,7 +30,7 @@ export class HistogramOperation extends BaseOperation implements IBaseFilterCons @observable public V: AttributeTransformationModel; @observable public SchemaName: string; @observable public QRange: QuantitativeBinRange | undefined; - @computed public get Schema() { return CurrentUserUtils.GetNorthstarSchema(this.SchemaName); } + public get Schema() { return CurrentUserUtils.GetNorthstarSchema(this.SchemaName); } constructor(schemaName: string, x: AttributeTransformationModel, y: AttributeTransformationModel, v: AttributeTransformationModel, normalized?: number) { super(); @@ -41,7 +41,11 @@ export class HistogramOperation extends BaseOperation implements IBaseFilterCons this.SchemaName = schemaName; } - Copy(): HistogramOperation { + public static Duplicate(op: HistogramOperation) { + + return new HistogramOperation(op.SchemaName, op.X, op.Y, op.V, op.Normalization); + } + public Copy(): HistogramOperation { return new HistogramOperation(this.SchemaName, this.X, this.Y, this.V, this.Normalization); } @@ -50,7 +54,7 @@ export class HistogramOperation extends BaseOperation implements IBaseFilterCons } - @computed public get FilterModels() { + public get FilterModels() { return this.BarFilterModels; } @action @@ -71,9 +75,7 @@ export class HistogramOperation extends BaseOperation implements IBaseFilterCons return FilterModel.GetFilterModelsRecursive(this, new Set(), filterModels, true); } - @computed public get BrushString(): string[] { - trace(); let brushes: string[] = []; this.BrushLinks.map(brushLink => { let brushHistogram = Cast(brushLink.b.data, HistogramField); diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx index 66205f8ca..385fc6ead 100644 --- a/src/client/views/Main.tsx +++ b/src/client/views/Main.tsx @@ -12,13 +12,6 @@ import { CurrentUserUtils } from '../../server/authentication/models/current_use import { RouteStore } from '../../server/RouteStore'; import { emptyFunction, returnTrue, Utils, returnOne, returnZero } from '../../Utils'; import { Docs } from '../documents/Documents'; -import { ColumnAttributeModel } from '../northstar/core/attribute/AttributeModel'; -import { AttributeTransformationModel } from '../northstar/core/attribute/AttributeTransformationModel'; -import { Gateway, NorthstarSettings } from '../northstar/manager/Gateway'; -import { AggregateFunction, Catalog } from '../northstar/model/idea/idea'; -import '../northstar/model/ModelExtensions'; -import { HistogramOperation } from '../northstar/operations/HistogramOperation'; -import '../northstar/utils/Extensions'; import { SetupDrag, DragManager } from '../util/DragManager'; import { Transform } from '../util/Transform'; import { UndoManager } from '../util/UndoManager'; @@ -70,8 +63,6 @@ export class Main extends React.Component { } } - CurrentUserUtils.loadCurrentUser(); - library.add(faFont); library.add(faImage); library.add(faFilePdf); @@ -84,15 +75,8 @@ export class Main extends React.Component { library.add(faFilm); library.add(faMusic); library.add(faTree); - this.initEventListeners(); this.initAuthenticationRouters(); - - // try { - // this.initializeNorthstar(); - // } catch (e) { - - // } } componentDidMount() { window.onpopstate = this.onHistory; } @@ -155,7 +139,7 @@ export class Main extends React.Component { // 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: "New Mobile Uploads" }); + let pendingDocument = Docs.SchemaDocument(["title"], [], { title: "New Mobile Uploads" }); mainDoc.optionalRightCollection = pendingDocument; }, 0); } @@ -229,7 +213,7 @@ export class Main extends React.Component { 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([], { width: 200, height: 200, title: "a schema 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" })); @@ -300,40 +284,6 @@ export class Main extends React.Component {
    ); } - - // --------------- Northstar hooks ------------- / - private _northstarSchemas: Doc[] = []; - - @action SetNorthstarCatalog(ctlog: Catalog) { - CurrentUserUtils.NorthstarDBCatalog = ctlog; - if (ctlog && ctlog.schemas) { - ctlog.schemas.map(schema => { - let schemaDocuments: Doc[] = []; - let attributesToBecomeDocs = CurrentUserUtils.GetAllNorthstarColumnAttributes(schema); - Promise.all(attributesToBecomeDocs.reduce((promises, attr) => { - promises.push(DocServer.GetRefField(attr.displayName! + ".alias").then(action((field: Opt) => { - if (field instanceof Doc) { - schemaDocuments.push(field); - } else { - var atmod = new ColumnAttributeModel(attr); - let histoOp = new HistogramOperation(schema.displayName!, - new AttributeTransformationModel(atmod, AggregateFunction.None), - new AttributeTransformationModel(atmod, AggregateFunction.Count), - new AttributeTransformationModel(atmod, AggregateFunction.Count)); - schemaDocuments.push(Docs.HistogramDocument(histoOp, { width: 200, height: 200, title: attr.displayName! })); - } - }))); - return promises; - }, [] as Promise[])).finally(() => - this._northstarSchemas.push(Docs.TreeDocument(schemaDocuments, { width: 50, height: 100, title: schema.displayName! }))); - }); - } - } - async initializeNorthstar(): Promise { - const getEnvironment = await fetch("/assets/env.json", { redirect: "follow", method: "GET", credentials: "include" }); - NorthstarSettings.Instance.UpdateEnvironment(await getEnvironment.json()); - Gateway.Instance.ClearCatalog().then(async () => this.SetNorthstarCatalog(await Gateway.Instance.GetCatalog())); - } } (async () => { diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index 9ecccc559..f4ad5b357 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -20,11 +20,20 @@ import { FieldView, FieldViewProps } from "../nodes/FieldView"; import "./CollectionSchemaView.scss"; import { CollectionSubView } from "./CollectionSubView"; import { Opt, Field, Doc, DocListCastAsync, DocListCast } from "../../../new_fields/Doc"; -import { Cast, FieldValue, NumCast } from "../../../new_fields/Types"; +import { Cast, FieldValue, NumCast, StrCast } from "../../../new_fields/Types"; import { listSpec } from "../../../new_fields/Schema"; import { List } from "../../../new_fields/List"; import { Id } from "../../../new_fields/RefField"; import { isUndefined } from "typescript-collections/dist/lib/util"; +import { CurrentUserUtils } from "../../../server/authentication/models/current_user_utils"; +import { Gateway } from "../../northstar/manager/Gateway"; +import { DocServer } from "../../DocServer"; +import { ColumnAttributeModel } from "../../northstar/core/attribute/AttributeModel"; +import { HistogramOperation } from "../../northstar/operations/HistogramOperation"; +import { AggregateFunction } from "../../northstar/model/idea/idea"; +import { AttributeTransformationModel } from "../../northstar/core/attribute/AttributeTransformationModel"; +import { Docs } from "../../documents/Documents"; +import { ContextMenu } from "../ContextMenu"; // bcz: need to add drag and drop of rows and columns. This seems like it might work for rows: https://codesandbox.io/s/l94mn1q657 @@ -207,6 +216,32 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { } } + onContextMenu = (e: React.MouseEvent): void => { + if (!e.isPropagationStopped() && this.props.Document[Id] !== "mainDoc") { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7 + ContextMenu.Instance.addItem({ description: "Make DB", event: this.makeDB }); + } + } + + @action + makeDB = async () => { + let csv: string = this.columns.reduce((val, col) => val + col + ",", ""); + csv = csv.substr(0, csv.length - 1) + "\n"; + let self = this; + DocListCast(this.props.Document.data).map(doc => { + csv += self.columns.reduce((val, col) => val + (doc[col] ? doc[col]!.toString() : "") + ",", ""); + csv = csv.substr(0, csv.length - 1) + "\n"; + }) + csv.substring(0, csv.length - 1); + let dbName = StrCast(this.props.Document.title); + let res = await Gateway.Instance.PostSchema(csv, dbName); + if (self.props.CollectionView.props.addDocument) { + let schemaDoc = await Docs.DBDocument("https://www.cs.brown.edu/" + dbName, { title: dbName }); + if (schemaDoc) { + self.props.CollectionView.props.addDocument(schemaDoc, false); + } + } + } + @action addColumn = () => { this.columns.push(this._newKeyName); @@ -325,7 +360,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { const children = this.children; return (
    this.onDrop(e, {})} ref={this.createTarget}> + onDrop={(e: React.DragEvent) => this.onDrop(e, {})} onContextMenu={this.onContextMenu} ref={this.createTarget}>
    ({ diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index 24dea200e..be7cddca6 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -17,6 +17,9 @@ import { InkField, StrokeData } from "../../../../new_fields/InkField"; import { List } from "../../../../new_fields/List"; import { ImageField } from "../../../../new_fields/URLField"; import { Template, Templates } from "../../Templates"; +import { Gateway } from "../../../northstar/manager/Gateway"; +import { DocServer } from "../../../DocServer"; +import { Id } from "../../../../new_fields/RefField"; interface MarqueeViewProps { getContainerTransform: () => Transform; @@ -111,8 +114,8 @@ export class MarqueeView extends React.Component doc.title = i.toString(); docList.push(doc); } - let newCol = Docs.SchemaDocument(docList, { x: x, y: y, title: "-dropped table-", width: 300, height: 100 }); - newCol.proto!.schemaColumns = new List([...(groupAttr ? ["_group"] : []), ...columns.filter(c => c)]); + let newCol = Docs.SchemaDocument([...(groupAttr ? ["_group"] : []), ...columns.filter(c => c)], docList, { x: x, y: y, title: "droppedTable", width: 300, height: 100 }); + this.props.addDocument(newCol, false); } })(); diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index b7ad9249a..c97bcbd94 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -306,7 +306,7 @@ export class DocumentView extends DocComponent(Docu cm.addItem({ description: "Find aliases", event: async () => { const aliases = await SearchUtil.GetAliasesOfDocument(this.props.Document); - CollectionDockingView.Instance.AddRightSplit(Docs.SchemaDocument(aliases, {})); + CollectionDockingView.Instance.AddRightSplit(Docs.SchemaDocument(["title"], aliases, {})); } }); cm.addItem({ description: "Copy URL", event: () => Utils.CopyText(DocServer.prepend("/doc/" + this.props.Document[Id])) }); diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts index 5f45d7bcc..5b63ac356 100644 --- a/src/server/authentication/models/current_user_utils.ts +++ b/src/server/authentication/models/current_user_utils.ts @@ -1,15 +1,21 @@ import { computed, observable, action, runInAction } from "mobx"; import * as rp from 'request-promise'; import { Docs } from "../../../client/documents/Documents"; -import { Attribute, AttributeGroup, Catalog, Schema } from "../../../client/northstar/model/idea/idea"; +import { Attribute, AttributeGroup, Catalog, Schema, AggregateFunction } from "../../../client/northstar/model/idea/idea"; import { ArrayUtil } from "../../../client/northstar/utils/ArrayUtil"; import { RouteStore } from "../../RouteStore"; import { DocServer } from "../../../client/DocServer"; -import { Doc } from "../../../new_fields/Doc"; +import { Doc, Opt, Field } from "../../../new_fields/Doc"; import { List } from "../../../new_fields/List"; import { CollectionViewType } from "../../../client/views/collections/CollectionBaseView"; import { CollectionTreeView } from "../../../client/views/collections/CollectionTreeView"; import { CollectionView } from "../../../client/views/collections/CollectionView"; +import { NorthstarSettings, Gateway } from "../../../client/northstar/manager/Gateway"; +import { AttributeTransformationModel } from "../../../client/northstar/core/attribute/AttributeTransformationModel"; +import { ColumnAttributeModel } from "../../../client/northstar/core/attribute/AttributeModel"; +import { HistogramOperation } from "../../../client/northstar/operations/HistogramOperation"; +import { Cast, PromiseValue } from "../../../new_fields/Types"; +import { listSpec } from "../../../new_fields/Schema"; export class CurrentUserUtils { private static curr_email: string; @@ -31,13 +37,13 @@ export class CurrentUserUtils { doc.title = this.email; doc.data = new List(); doc.excludeFromLibrary = true; - doc.optionalRightCollection = Docs.SchemaDocument([], { title: "Pending documents" }); + doc.optionalRightCollection = Docs.SchemaDocument(["title"], [], { title: "Pending documents" }); // doc.library = Docs.TreeDocument([doc], { title: `Library: ${CurrentUserUtils.email}` }); // (doc.library as Doc).excludeFromLibrary = true; return doc; } - public static loadCurrentUser(): Promise { + public static async loadCurrentUser(): Promise { let userPromise = rp.get(DocServer.prepend(RouteStore.getCurrUser)).then(response => { if (response) { let obj = JSON.parse(response); @@ -47,7 +53,7 @@ export class CurrentUserUtils { throw new Error("There should be a user! Why does Dash think there isn't one?"); } }); - let userDocPromise = rp.get(DocServer.prepend(RouteStore.getUserDocumentId)).then(id => { + let userDocPromise = await rp.get(DocServer.prepend(RouteStore.getUserDocumentId)).then(id => { if (id) { return DocServer.GetRefField(id).then(field => runInAction(() => this.user_document = field instanceof Doc ? field : this.createUserDocument(id))); @@ -55,14 +61,63 @@ export class CurrentUserUtils { throw new Error("There should be a user id! Why does Dash think there isn't one?"); } }); + try { + const getEnvironment = await fetch("/assets/env.json", { redirect: "follow", method: "GET", credentials: "include" }); + NorthstarSettings.Instance.UpdateEnvironment(await getEnvironment.json()); + await Gateway.Instance.ClearCatalog(); + const extraSchemas = Cast(CurrentUserUtils.UserDocument.DBSchemas, listSpec("string"), []); + let extras = await Promise.all(extraSchemas.map(async sc => await Gateway.Instance.GetSchema("", sc))); + let catprom = CurrentUserUtils.SetNorthstarCatalog(await Gateway.Instance.GetCatalog(), extras); + if (catprom) await Promise.all(catprom); + } catch (e) { + + } return Promise.all([userPromise, userDocPromise]); } /* Northstar catalog ... really just for testing so this should eventually go away */ + // --------------- Northstar hooks ------------- / + static _northstarSchemas: Doc[] = []; @observable private static _northstarCatalog?: Catalog; @computed public static get NorthstarDBCatalog() { return this._northstarCatalog; } + + @action static SetNorthstarCatalog(ctlog: Catalog, extras: Catalog[]) { + CurrentUserUtils.NorthstarDBCatalog = ctlog; + if (ctlog && ctlog.schemas) { + extras.map(ex => ctlog.schemas!.push(ex)); + return ctlog.schemas.map(async schema => { + let schemaDocuments: Doc[] = []; + let attributesToBecomeDocs = CurrentUserUtils.GetAllNorthstarColumnAttributes(schema); + await Promise.all(attributesToBecomeDocs.reduce((promises, attr) => { + promises.push(DocServer.GetRefField(attr.displayName! + ".alias").then(action((field: Opt) => { + if (field instanceof Doc) { + schemaDocuments.push(field); + } else { + var atmod = new ColumnAttributeModel(attr); + let histoOp = new HistogramOperation(schema.displayName!, + new AttributeTransformationModel(atmod, AggregateFunction.None), + new AttributeTransformationModel(atmod, AggregateFunction.Count), + new AttributeTransformationModel(atmod, AggregateFunction.Count)); + schemaDocuments.push(Docs.HistogramDocument(histoOp, { width: 200, height: 200, title: attr.displayName! })); + } + }))); + return promises; + }, [] as Promise[])); + return CurrentUserUtils._northstarSchemas.push(Docs.TreeDocument(schemaDocuments, { width: 50, height: 100, title: schema.displayName! })); + }); + } + } public static set NorthstarDBCatalog(ctlog: Catalog | undefined) { this._northstarCatalog = ctlog; } + public static AddNorthstarSchema(schema: Schema, schemaDoc: Doc) { + if (this._northstarCatalog && CurrentUserUtils._northstarSchemas) { + this._northstarCatalog.schemas!.push(schema); + CurrentUserUtils._northstarSchemas.push(schemaDoc); + let schemas = Cast(CurrentUserUtils.UserDocument.DBSchemas, listSpec("string"), []); + schemas.push(schema.displayName!); + CurrentUserUtils.UserDocument.DBSchemas = new List(schemas); + } + } public static GetNorthstarSchema(name: string): Schema | undefined { return !this._northstarCatalog || !this._northstarCatalog.schemas ? undefined : ArrayUtil.FirstOrDefault(this._northstarCatalog.schemas, (s: Schema) => s.displayName === name); -- cgit v1.2.3-70-g09d2 From 1ae0370e6a116404085f6864c8b644fcde80f460 Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Tue, 14 May 2019 19:59:23 -0400 Subject: Added History module and ability to go back and forward when following links --- src/client/util/History.ts | 122 +++++++++++++++++++++ src/client/views/Main.tsx | 19 +--- .../views/collections/CollectionDockingView.tsx | 2 - .../collectionFreeForm/CollectionFreeFormView.tsx | 36 +++++- src/new_fields/Doc.ts | 7 +- 5 files changed, 159 insertions(+), 27 deletions(-) create mode 100644 src/client/util/History.ts (limited to 'src/client/views/Main.tsx') diff --git a/src/client/util/History.ts b/src/client/util/History.ts new file mode 100644 index 000000000..92d2b2b44 --- /dev/null +++ b/src/client/util/History.ts @@ -0,0 +1,122 @@ +import { Doc, Opt, Field } from "../../new_fields/Doc"; +import { DocServer } from "../DocServer"; +import { Main } from "../views/Main"; +import { RouteStore } from "../../server/RouteStore"; + +export namespace HistoryUtil { + export interface DocInitializerList { + [key: string]: string | number; + } + + export interface DocUrl { + type: "doc"; + docId: string; + initializers: { + [docId: string]: DocInitializerList; + }; + } + + export type ParsedUrl = DocUrl; + + // const handlers: ((state: ParsedUrl | null) => void)[] = []; + function onHistory(e: PopStateEvent) { + if (window.location.pathname !== RouteStore.home) { + const url = e.state as ParsedUrl || parseUrl(window.location.pathname); + if (url) { + switch (url.type) { + case "doc": + onDocUrl(url); + break; + } + } + } + // for (const handler of handlers) { + // handler(e.state); + // } + } + + export function pushState(state: ParsedUrl) { + history.pushState(state, "", createUrl(state)); + } + + export function replaceState(state: ParsedUrl) { + history.replaceState(state, "", createUrl(state)); + } + + function copyState(state: ParsedUrl): ParsedUrl { + return JSON.parse(JSON.stringify(state)); + } + + export function getState(): ParsedUrl { + return copyState(history.state); + } + + // export function addHandler(handler: (state: ParsedUrl | null) => void) { + // handlers.push(handler); + // } + + // export function removeHandler(handler: (state: ParsedUrl | null) => void) { + // const index = handlers.indexOf(handler); + // if (index !== -1) { + // handlers.splice(index, 1); + // } + // } + + export function parseUrl(pathname: string): ParsedUrl | undefined { + let pathnameSplit = pathname.split("/"); + if (pathnameSplit.length !== 2) { + return undefined; + } + const type = pathnameSplit[0]; + const data = pathnameSplit[1]; + + if (type === "doc") { + const s = data.split("?"); + if (s.length < 1 || s.length > 2) { + return undefined; + } + const docId = s[0]; + const initializers = s.length === 2 ? JSON.parse(decodeURIComponent(s[1])) : {}; + return { + type: "doc", + docId, + initializers + }; + } + + return undefined; + } + + export function createUrl(params: ParsedUrl): string { + let baseUrl = DocServer.prepend(`/${params.type}`); + switch (params.type) { + case "doc": + const initializers = encodeURIComponent(JSON.stringify(params.initializers)); + const id = params.docId; + let url = baseUrl + `/${id}`; + if (Object.keys(params.initializers).length) { + url += `?${initializers}`; + } + return url; + } + return ""; + } + + export async function initDoc(id: string, initializer: DocInitializerList) { + const doc = await DocServer.GetRefField(id); + if (!(doc instanceof Doc)) { + return; + } + Doc.assign(doc, initializer); + } + + async function onDocUrl(url: DocUrl) { + 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); + } + } + + window.onpopstate = onHistory; +} diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx index 66205f8ca..55252ab1d 100644 --- a/src/client/views/Main.tsx +++ b/src/client/views/Main.tsx @@ -38,6 +38,8 @@ 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 { @@ -95,21 +97,6 @@ export class Main extends React.Component { // } } - componentDidMount() { window.onpopstate = this.onHistory; } - - componentWillUnmount() { window.onpopstate = null; } - - onHistory = () => { - if (window.location.pathname !== RouteStore.home) { - let pathname = window.location.pathname.split("/"); - DocServer.GetRefField(pathname[pathname.length - 1]).then(action((field: Opt) => { - if (field instanceof Doc) { - this.openWorkspace(field, true); - } - })); - } - } - initEventListeners = () => { // window.addEventListener("pointermove", (e) => this.reportLocation(e)) window.addEventListener("drop", (e) => e.preventDefault(), false); // drop event handler @@ -165,7 +152,7 @@ export class Main extends React.Component { openWorkspace = async (doc: Doc, fromHistory = false) => { CurrentUserUtils.MainDocId = doc[Id]; this.mainContainer = doc; - fromHistory || window.history.pushState(null, StrCast(doc.title), "/doc/" + doc[Id]); + 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 () => { diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 6651a834d..58f1e33a1 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -190,7 +190,6 @@ export class CollectionDockingView extends React.Component void = () => { - console.log("Docking mount"); if (this._containerRef.current) { this.reactionDisposer = reaction( () => StrCast(this.props.Document.dockingConfig), @@ -207,7 +206,6 @@ export class CollectionDockingView extends React.Component void = () => { - console.log("Docking unmount"); try { this._goldenLayout.unbind('itemDropped', this.itemDropped); this._goldenLayout.unbind('tabCreated', this.tabCreated); diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index c36c708db..e60bb2fb2 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -24,6 +24,7 @@ import { FieldValue, Cast, NumCast } from "../../../../new_fields/Types"; import { pageSchema } from "../../nodes/ImageBox"; import { Id } from "../../../../new_fields/RefField"; import { InkField, StrokeData } from "../../../../new_fields/InkField"; +import { HistoryUtil } from "../../../util/History"; export const panZoomSchema = createSchema({ panX: "number", @@ -219,6 +220,8 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { @action setPan(panX: number, panY: number) { + this.panDisposer && clearTimeout(this.panDisposer); + this.props.Document.panTransformType = "None"; var scale = this.getLocalTransform().inverse().Scale; const newPanX = Math.min((1 - 1 / scale) * this.nativeWidth, Math.max(0, panX)); const newPanY = Math.min((1 - 1 / scale) * this.nativeHeight, Math.max(0, panY)); @@ -245,16 +248,37 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { doc.zIndex = docs.length + 1; } + panDisposer?: NodeJS.Timeout; focusDocument = (doc: Doc) => { + const panX = this.Document.panX; + const panY = this.Document.panY; + const id = this.Document[Id]; + const state = HistoryUtil.getState(); + // TODO This technically isn't correct if type !== "doc", as + // currently nothing is done, but we should probably push a new state + if (state.type === "doc" && panX !== undefined && panY !== undefined) { + const init = state.initializers[id]; + if (!init) { + state.initializers[id] = { + panX, panY + }; + HistoryUtil.pushState(state); + } else if (init.panX !== panX || init.panY !== panY) { + init.panX = panX; + init.panY = panY; + HistoryUtil.pushState(state); + } + } SelectionManager.DeselectAll(); + const newPanX = NumCast(doc.x) + NumCast(doc.width) / 2; + const newPanY = NumCast(doc.y) + NumCast(doc.height) / 2; + const newState = HistoryUtil.getState(); + newState.initializers[id] = { panX: newPanX, panY: newPanY }; + HistoryUtil.pushState(newState); + this.setPan(newPanX, newPanY); this.props.Document.panTransformType = "Ease"; - this.setPan( - NumCast(doc.x) + NumCast(doc.width) / 2, - NumCast(doc.y) + NumCast(doc.height) / 2); this.props.focus(this.props.Document); - if (this.props.Document.panTransformType === "Ease") { - setTimeout(() => this.props.Document.panTransformType = "None", 2000); // wait 3 seconds, then reset to false - } + this.panDisposer = setTimeout(() => this.props.Document.panTransformType = "None", 2000); // wait 3 seconds, then reset to false } diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index 89901490d..d6043ef7a 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -159,9 +159,10 @@ export namespace Doc { for (const key in fields) { if (fields.hasOwnProperty(key)) { const value = fields[key]; - if (value !== undefined) { - doc[key] = value; - } + // Do we want to filter out undefineds? + // if (value !== undefined) { + doc[key] = value; + // } } } return doc; -- cgit v1.2.3-70-g09d2 From c96e61de2cea0fee1f6c80caf426535dbdc3d37c Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Thu, 16 May 2019 16:08:53 -0400 Subject: Changed Clear DB Button --- .vscode/launch.json | 9 +++++++++ src/client/views/Main.tsx | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) (limited to 'src/client/views/Main.tsx') diff --git a/.vscode/launch.json b/.vscode/launch.json index e92a4949a..452acc823 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -13,6 +13,15 @@ "url": "http://localhost:1050/login", "webRoot": "${workspaceFolder}", }, + { + "type": "chrome", + "request": "launch", + "name": "Launch Chrome against Dash server", + "sourceMaps": true, + "breakOnLoad": true, + "url": "http://dash-web.eastus2.cloudapp.azure.com:1050/login", + "webRoot": "${workspaceFolder}", + }, { "type": "node", "request": "attach", diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx index 7aef7d3e5..866da010a 100644 --- a/src/client/views/Main.tsx +++ b/src/client/views/Main.tsx @@ -244,7 +244,7 @@ export class Main extends React.Component { let logoutRef = React.createRef(); return [ - , + ,
    -- cgit v1.2.3-70-g09d2 From 6e65b1e32eb972358d9c8dbd7f8d984d3baefc4b Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Thu, 16 May 2019 22:56:19 -0400 Subject: fixed links so that you can adjust the pan location of a collection when its a link endoint --- src/client/util/DocumentManager.ts | 5 +++-- src/client/util/DragManager.ts | 2 +- src/client/util/TooltipTextMenu.tsx | 1 - src/client/views/Main.tsx | 2 +- src/client/views/nodes/CollectionFreeFormDocumentView.tsx | 3 ++- src/client/views/nodes/DocumentView.tsx | 11 ++++++----- src/client/views/nodes/FormattedTextBox.tsx | 4 +--- src/client/views/nodes/LinkBox.tsx | 2 +- src/client/views/nodes/LinkMenu.tsx | 5 ++--- src/new_fields/Doc.ts | 10 ++++++---- 10 files changed, 23 insertions(+), 22 deletions(-) (limited to 'src/client/views/Main.tsx') diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index 51f5fbe9f..c6962ad8d 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -110,7 +110,8 @@ export class DocumentManager { } @undoBatch - public jumpToDocument = async (doc: Doc): Promise => { + public jumpToDocument = async (docDelegate: Doc, makeCopy: boolean = true): Promise => { + let doc = docDelegate.proto ? docDelegate.proto : docDelegate; const page = NumCast(doc.page, undefined); const contextDoc = await Cast(doc.annotationOn, Doc); if (contextDoc) { @@ -122,7 +123,7 @@ export class DocumentManager { docView.props.focus(docView.props.Document); } else { if (!contextDoc) { - CollectionDockingView.Instance.AddRightSplit(Doc.MakeDelegate(doc)); + CollectionDockingView.Instance.AddRightSplit(docDelegate ? (makeCopy ? Doc.MakeCopy(docDelegate) : docDelegate) : Doc.MakeDelegate(doc)); } else { let contextView = DocumentManager.Instance.getDocumentView(contextDoc); if (contextView) { diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 05eb5c38a..7f75a95f0 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -336,7 +336,7 @@ export namespace DragManager { x: e.x, y: e.y, data: dragData, - mods: e.ctrlKey ? "Control" : "" + mods: e.altKey ? "AltKey" : "" } }) ); diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx index 223921428..4d40d09b2 100644 --- a/src/client/util/TooltipTextMenu.tsx +++ b/src/client/util/TooltipTextMenu.tsx @@ -186,7 +186,6 @@ export class TooltipTextMenu { let node = this.view.state.selection.$from.nodeAfter; let link = node && node.marks.find(m => m.type.name === "link"); if (link) { - console.log("Link to : " + link.attrs.href); let href: string = link.attrs.href; if (href.indexOf(DocServer.prepend("/doc/")) === 0) { let docid = href.replace(DocServer.prepend("/doc/"), ""); diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx index 866da010a..51ad0aa49 100644 --- a/src/client/views/Main.tsx +++ b/src/client/views/Main.tsx @@ -244,7 +244,7 @@ export class Main extends React.Component { let logoutRef = React.createRef(); return [ - , + ,
    diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index d4f660b3f..001ce3095 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -205,11 +205,12 @@ export class CollectionFreeFormDocumentView extends DocComponent(Docu let sourceDoc = de.data.linkSourceDocument; let destDoc = this.props.Document; - const protoDest = destDoc.proto; - const protoSrc = sourceDoc.proto; - if (de.mods === "Control") { + if (de.mods === "AltKey") { + const protoDest = destDoc.proto; + const protoSrc = sourceDoc.proto; let src = protoSrc ? protoSrc : sourceDoc; let dst = protoDest ? protoDest : destDoc; - dst.data = src; + dst.data = (src.data! as ObjectField)[Copy](); dst.nativeWidth = src.nativeWidth; dst.nativeHeight = src.nativeHeight; } else { - Doc.MakeLink(protoSrc ? protoSrc : sourceDoc, protoDest ? protoDest : destDoc); + Doc.MakeLink(sourceDoc, destDoc); de.data.droppedDocuments.push(destDoc); } e.stopPropagation(); diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index f30022508..bf98fb40b 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -111,9 +111,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe let sourceDoc = de.data.linkSourceDocument; let destDoc = this.props.Document; - const protoDest = destDoc.proto; - const protoSrc = sourceDoc.proto; - Doc.MakeLink(protoSrc ? protoSrc : sourceDoc, protoDest ? protoDest : destDoc); + Doc.MakeLink(sourceDoc, destDoc); de.data.droppedDocuments.push(destDoc); e.stopPropagation(); } diff --git a/src/client/views/nodes/LinkBox.tsx b/src/client/views/nodes/LinkBox.tsx index 611cb66b6..68b692aad 100644 --- a/src/client/views/nodes/LinkBox.tsx +++ b/src/client/views/nodes/LinkBox.tsx @@ -31,7 +31,7 @@ export class LinkBox extends React.Component { @undoBatch onViewButtonPressed = async (e: React.PointerEvent): Promise => { e.stopPropagation(); - DocumentManager.Instance.jumpToDocument(this.props.pairedDoc); + DocumentManager.Instance.jumpToDocument(this.props.pairedDoc, e.altKey); } onEditButtonPressed = (e: React.PointerEvent): void => { diff --git a/src/client/views/nodes/LinkMenu.tsx b/src/client/views/nodes/LinkMenu.tsx index 11117122d..4cf798249 100644 --- a/src/client/views/nodes/LinkMenu.tsx +++ b/src/client/views/nodes/LinkMenu.tsx @@ -6,8 +6,7 @@ import { LinkEditor } from "./LinkEditor"; import './LinkMenu.scss'; import React = require("react"); import { Doc, DocListCast } from "../../../new_fields/Doc"; -import { Cast, FieldValue } from "../../../new_fields/Types"; -import { listSpec } from "../../../new_fields/Schema"; +import { Cast, FieldValue, StrCast } from "../../../new_fields/Types"; import { Id } from "../../../new_fields/RefField"; interface Props { @@ -24,7 +23,7 @@ export class LinkMenu extends React.Component { return links.map(link => { let doc = FieldValue(Cast(link[key], Doc)); if (doc) { - return this._editingLink = link)} type={type} />; + return this._editingLink = link)} type={type} />; } }); } diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index c898f697b..ad3b880cd 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -216,6 +216,8 @@ export namespace Doc { } export function MakeLink(source: Doc, target: Doc) { + let protoSrc = source.proto ? source.proto : source; + let protoTarg = target.proto ? target.proto : target; UndoManager.RunInBatch(() => { let linkDoc = Docs.TextDocument({ width: 100, height: 30, borderRounding: -1 }); //let linkDoc = new Doc; @@ -226,15 +228,15 @@ export namespace Doc { linkDoc.proto!.linkedTo = target; linkDoc.proto!.linkedFrom = source; - let linkedFrom = Cast(target.linkedFromDocs, listSpec(Doc)); + let linkedFrom = Cast(protoTarg.linkedFromDocs, listSpec(Doc)); if (!linkedFrom) { - target.linkedFromDocs = linkedFrom = new List(); + protoTarg.linkedFromDocs = linkedFrom = new List(); } linkedFrom.push(linkDoc); - let linkedTo = Cast(source.linkedToDocs, listSpec(Doc)); + let linkedTo = Cast(protoSrc.linkedToDocs, listSpec(Doc)); if (!linkedTo) { - source.linkedToDocs = linkedTo = new List(); + protoSrc.linkedToDocs = linkedTo = new List(); } linkedTo.push(linkDoc); return linkDoc; -- cgit v1.2.3-70-g09d2 From 4e244951b7b18d7973360f423e8de80c42466228 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Fri, 17 May 2019 19:50:32 -0400 Subject: added right-click "open Tab" and double-click open tab --- src/client/views/Main.tsx | 1 + .../views/collections/CollectionDockingView.tsx | 20 ++++++++++++++++++-- .../collectionFreeForm/CollectionFreeFormView.tsx | 1 + .../views/nodes/CollectionFreeFormDocumentView.tsx | 19 +++++++++++++++++++ src/client/views/nodes/DocumentView.tsx | 2 ++ src/client/views/nodes/FieldView.tsx | 2 ++ src/client/views/nodes/ImageBox.tsx | 4 +++- 7 files changed, 46 insertions(+), 3 deletions(-) (limited to 'src/client/views/Main.tsx') diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx index 51ad0aa49..177047a9a 100644 --- a/src/client/views/Main.tsx +++ b/src/client/views/Main.tsx @@ -166,6 +166,7 @@ export class Main extends React.Component { { @observable private _panelWidth = 0; @observable private _panelHeight = 0; @observable private _document: Opt; - + _stack: any; constructor(props: any) { super(props); + this._stack = (this.props as any).glContainer.parent.parent; DocServer.GetRefField(this.props.documentId).then(action((f: Opt) => this._document = f as Doc)); } @@ -420,6 +432,9 @@ export class DockedFrameRenderer extends React.Component { } get previewPanelCenteringOffset() { return (this._panelWidth - this.nativeWidth() * this.contentScaling()) / 2; } + addDocTab = (doc: Doc) => { + CollectionDockingView.Instance.AddTab(this._stack, doc); + } get content() { if (!this._document) { return (null); @@ -441,6 +456,7 @@ export class DockedFrameRenderer extends React.Component { parentActive={returnTrue} whenActiveChanged={emptyFunction} focus={emptyFunction} + addDocTab={this.addDocTab} ContainingCollectionView={undefined} />
    ); } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 6e4c79e62..ad32f70dc 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -300,6 +300,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { parentActive: this.props.active, whenActiveChanged: this.props.whenActiveChanged, bringToFront: this.bringToFront, + addDocTab: this.props.addDocTab, }; } diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index 00aa71656..a0efc3154 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -159,6 +159,7 @@ export class CollectionFreeFormDocumentView extends DocComponent { this._downX = e.clientX; @@ -166,6 +167,24 @@ export class CollectionFreeFormDocumentView extends DocComponent { + + document.removeEventListener("pointerup", this.onPointerUp); + if (Math.abs(e.clientX - this._downX) < 2 && Math.abs(e.clientY - this._downY) < 2) { + this.props.addDocTab(this.props.Document); + } + e.stopPropagation(); } onClick = async (e: React.MouseEvent) => { e.stopPropagation(); diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 5264773b0..9a5d21b49 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -57,6 +57,7 @@ export interface DocumentViewProps { whenActiveChanged: (isActive: boolean) => void; toggleMinimized: () => void; bringToFront: (doc: Doc) => void; + addDocTab: (doc: Doc) => void; } const schema = createSchema({ @@ -307,6 +308,7 @@ export class DocumentView extends DocComponent(Docu cm.addItem({ description: this.props.Document.isButton ? "Remove Button" : "Make Button", event: this.makeButton }); cm.addItem({ description: "Fields", event: this.fieldsClicked }); cm.addItem({ description: "Center", event: () => this.props.focus(this.props.Document) }); + cm.addItem({ description: "Open Tab", event: () => this.props.addDocTab && this.props.addDocTab(this.props.Document) }); cm.addItem({ description: "Open Right", event: () => CollectionDockingView.Instance.AddRightSplit(this.props.Document) }); cm.addItem({ description: "Find aliases", event: async () => { diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx index 34b6c5e70..b5dfd18eb 100644 --- a/src/client/views/nodes/FieldView.tsx +++ b/src/client/views/nodes/FieldView.tsx @@ -35,6 +35,7 @@ export interface FieldViewProps { isTopMost: boolean; selectOnLoad: boolean; addDocument?: (document: Doc, allowDuplicates?: boolean) => boolean; + addDocTab: (document: Doc) => boolean; removeDocument?: (document: Doc) => boolean; moveDocument?: (document: Doc, targetCollection: Doc, addDocument: (document: Doc) => boolean) => boolean; ScreenToLocalTransform: () => Transform; @@ -87,6 +88,7 @@ export class FieldView extends React.Component { return ( (ImageD let interactive = InkingControl.Instance.selectedTool ? "" : "-interactive"; let id = (this.props as any).id; // bcz: used to set id = "isExpander" in templates.tsx return ( -
    +
    Date: Sat, 18 May 2019 15:25:42 -0400 Subject: Refactored some things to get Debug viewer working again --- src/client/util/History.ts | 4 +- src/client/views/Main.tsx | 280 +--------------- src/client/views/MainView.tsx | 274 ++++++++++++++++ src/client/views/PresentationView.tsx | 1 - .../views/collections/CollectionTreeView.tsx | 8 +- src/debug/Viewer.tsx | 364 +++++++++++---------- 6 files changed, 468 insertions(+), 463 deletions(-) create mode 100644 src/client/views/MainView.tsx (limited to 'src/client/views/Main.tsx') 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 { - return FieldValue(Cast(CurrentUserUtils.UserDocument.activeWorkspace, Doc)); - } - private set mainContainer(doc: Opt) { - 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) : - ; - const pres = mainCont ? FieldValue(Cast(mainCont.presentationView, Doc)) : undefined; - return - {({ measureRef }) => -
    - {content} - {pres ? : null} -
    - } -
    ; - } - - /* 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, IconName, string, () => Doc][] = [ - [React.createRef(), "font", "Add Textbox", addTextNode], - [React.createRef(), "image", "Add Image", addImageNode], - [React.createRef(), "file-pdf", "Add PDF", addPDFNode], - [React.createRef(), "film", "Add Video", addVideoNode], - [React.createRef(), "music", "Add Audio", addAudioNode], - [React.createRef(), "globe-asia", "Add Web Clipping", addWebNode], - [React.createRef(), "object-group", "Add Collection", addColNode], - [React.createRef(), "tree", "Add Tree", addTreeNode], - [React.createRef(), "table", "Add Schema", addSchemaNode], - ]; - - return < div id="add-nodes-menu" > - - - -
    -
      - {btns.map(btn => -
    • - -
    • )} -
    -
    -
    ; - } - - /* @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(); - - return [ - , -
    - - - -
    , -
    , -
    -
    - ]; - - } - - render() { - return ( -
    - - {this.mainContent} - - - {this.nodesMenu()} - {this.miscButtons} - - -
    - ); - } -} +import * as React from 'react'; (async () => { await Docs.initProtos(); await CurrentUserUtils.loadCurrentUser(); - ReactDOM.render(
    , document.getElementById('root')); + ReactDOM.render(, 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 { + return FieldValue(Cast(CurrentUserUtils.UserDocument.activeWorkspace, Doc)); + } + private set mainContainer(doc: Opt) { + 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) : + ; + const pres = mainCont ? FieldValue(Cast(mainCont.presentationView, Doc)) : undefined; + return + {({ measureRef }) => +
    + {content} + {pres ? : null} +
    + } +
    ; + } + + /* 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, IconName, string, () => Doc][] = [ + [React.createRef(), "font", "Add Textbox", addTextNode], + [React.createRef(), "image", "Add Image", addImageNode], + [React.createRef(), "file-pdf", "Add PDF", addPDFNode], + [React.createRef(), "film", "Add Video", addVideoNode], + [React.createRef(), "music", "Add Audio", addAudioNode], + [React.createRef(), "globe-asia", "Add Web Clipping", addWebNode], + [React.createRef(), "object-group", "Add Collection", addColNode], + [React.createRef(), "tree", "Add Tree", addTreeNode], + [React.createRef(), "table", "Add Schema", addSchemaNode], + ]; + + return < div id="add-nodes-menu" > + + + +
    +
      + {btns.map(btn => +
    • + +
    • )} +
    +
    +
    ; + } + + /* @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(); + + return [ + , +
    + + + +
    , +
    , +
    +
    + ]; + + } + + render() { + return ( +
    + + {this.mainContent} + + + {this.nodesMenu()} + {this.miscButtons} + + +
    + ); + } +} 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 { @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 { 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 }> { -// render() { -// return {JSON.stringify(this.props.field.Data)} ({this.props.field.Id}); -// } -// } - -// @observer -// class KeyViewer extends React.Component<{ field: Key }> { -// render() { -// return this.props.field.Name; -// } -// } - -// @observer -// class ListViewer extends React.Component<{ field: ListField }>{ -// @observable -// expanded = false; - -// render() { -// let content; -// if (this.expanded) { -// content = ( -//
    -// {this.props.field.Data.map(field => )} -//
    -// ); -// } else { -// content = <>[...] ({this.props.field.Id}); -// } -// return ( -//
    -// -// {content} -//
    -// ); -// } -// } - -// @observer -// class DocumentViewer extends React.Component<{ field: Document }> { -// private keyMap: ObservableMap = 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 ( -//
    -// ({key ? key.Name : kv[0]}): -// -//
    -// ); -// }); -// return ( -//
    -// Document ({this.props.field.Id}) -//
    -// {fields} -//
    -//
    -// ); -// } -// } - -// @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) => { -// 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 = (); -// } else if (this.field instanceof Document) { -// content = (); -// } else if (this.field instanceof BasicField) { -// content = (); -// } else if (this.field instanceof Key) { -// content = (); -// } else { -// content = (Unrecognized field type); -// } -// } else if (this.error) { -// content = Field {this.props.fieldId} not found ; -// } else { -// content = Field loading: {this.props.fieldId}; -// } -// return content; -// } -// } - -// @observer -// class Viewer extends React.Component { -// @observable -// private idToAdd: string = ''; - -// @observable -// private ids: string[] = []; - -// @action -// inputOnChange = (e: React.ChangeEvent) => { -// this.idToAdd = e.target.value; -// } - -// @action -// onKeyPress = (e: React.KeyboardEvent) => { -// if (e.key === "Enter") { -// this.ids.push(this.idToAdd); -// this.idToAdd = ""; -// } -// } - -// render() { -// return ( -// <> -// -//
    -// {this.ids.map(id => )} -//
    -// -// ); -// } -// } - -// ReactDOM.render(( -//
    -// -//
    ), -// 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 }>{ + @observable + expanded = false; + + render() { + let content; + if (this.expanded) { + content = ( +
    + {this.props.field.map((field, index) => )} +
    + ); + } else { + content = <>[...]; + } + return ( +
    + + {content} +
    + ); + } +} + +@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 ( +
    + ({key}): + +
    + ); + }); + content = ( +
    + Document ({this.props.field[Id]}) +
    + {fields} +
    +
    + ); + } else { + content = <>[...] ({this.props.field[Id]}); + } + return ( +
    + + {content} +
    + ); + } +} + +@observer +class DebugViewer extends React.Component<{ field: FieldResult }> { + + render() { + let content; + const field = this.props.field; + if (field instanceof List) { + content = (); + } else if (field instanceof Doc) { + content = (); + } else if (typeof field === "string") { + content =

    "{field}"

    ; + } else if (typeof field === "number" || typeof field === "boolean") { + content =

    {field}

    ; + } else if (field instanceof URLField) { + content =

    {field.url.href}

    ; + } else { + content =

    Unrecognized field type

    ; + } + return content; + } +} + +@observer +class Viewer extends React.Component { + @observable + private idToAdd: string = ''; + + @observable + private fields: Field[] = []; + + @action + inputOnChange = (e: React.ChangeEvent) => { + this.idToAdd = e.target.value; + } + + @action + onKeyPress = (e: React.KeyboardEvent) => { + if (e.key === "Enter") { + DocServer.GetRefField(this.idToAdd).then(action((field: any) => { + if (field !== undefined) { + this.fields.push(field); + } + })); + this.idToAdd = ""; + } + } + + render() { + return ( + <> + +
    + {this.fields.map((field, index) => )} +
    + + ); + } +} + +ReactDOM.render(( +
    + +
    ), + document.getElementById('root') +); \ No newline at end of file -- cgit v1.2.3-70-g09d2