diff options
author | Stanley Yip <stanley_yip@brown.edu> | 2019-10-13 15:58:23 -0400 |
---|---|---|
committer | Stanley Yip <stanley_yip@brown.edu> | 2019-10-13 15:58:23 -0400 |
commit | b50381f4872d4e5eaa6d1d8949ea4e10d468cfd8 (patch) | |
tree | c2939d230a5618aa609e4db84bfdee923bad80bd /src/client/views/MainView.tsx | |
parent | bd796f24677b2aacd32a371da6d2b7c7eb354018 (diff) | |
parent | baf6ed901d341cade58741d363bbc475519558ae (diff) |
Merge branch 'master' of https://github.com/browngraphicslab/Dash-Web into interaction_stanley
Diffstat (limited to 'src/client/views/MainView.tsx')
-rw-r--r-- | src/client/views/MainView.tsx | 567 |
1 files changed, 185 insertions, 382 deletions
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 53951224c..b329717c4 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -1,135 +1,57 @@ -import { IconName, library } from '@fortawesome/fontawesome-svg-core'; -import { faLink, faArrowDown, faArrowUp, faBolt, faCaretUp, faCat, faCheck, faClone, faCloudUploadAlt, faCommentAlt, faCut, faExclamation, faFilePdf, faFilm, faFont, faGlobeAsia, faLongArrowAltRight, faMusic, faObjectGroup, faPause, faPenNib, faPlay, faPortrait, faRedoAlt, faThumbtack, faTree, faUndoAlt, faTv, faChevronRight, faEllipsisV, faCompressArrowsAlt } from '@fortawesome/free-solid-svg-icons'; +import { library } from '@fortawesome/fontawesome-svg-core'; +import { faArrowDown, faArrowUp, faBolt, faCaretUp, faCat, faCheck, faClone, faCloudUploadAlt, faCommentAlt, faCut, faExclamation, faFilePdf, faFilm, faFont, faGlobeAsia, faLongArrowAltRight, faMusic, faObjectGroup, faPause, faPenNib, faPlay, faPortrait, faRedoAlt, faThumbtack, faTree, faUndoAlt, faTv, faChevronRight, faEllipsisV, faCompressArrowsAlt } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { action, computed, configure, observable, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import "normalize.css"; import * as React from 'react'; -import { SketchPicker } from 'react-color'; import Measure from 'react-measure'; -import { Doc, DocListCast, Field, FieldResult, HeightSym, Opt } from '../../new_fields/Doc'; +import { Doc, DocListCast, Field, FieldResult, Opt } from '../../new_fields/Doc'; import { Id } from '../../new_fields/FieldSymbols'; -import { InkTool } from '../../new_fields/InkField'; import { List } from '../../new_fields/List'; import { listSpec } from '../../new_fields/Schema'; -import { BoolCast, Cast, FieldValue, StrCast } from '../../new_fields/Types'; +import { Cast, FieldValue, StrCast } from '../../new_fields/Types'; import { CurrentUserUtils } from '../../server/authentication/models/current_user_utils'; import { RouteStore } from '../../server/RouteStore'; -import { emptyFunction, returnEmptyString, returnOne, returnTrue, Utils } from '../../Utils'; +import { emptyFunction, returnEmptyString, returnFalse, returnOne, returnTrue, Utils } from '../../Utils'; +import GoogleAuthenticationManager from '../apis/GoogleAuthenticationManager'; import { DocServer } from '../DocServer'; import { Docs, DocumentOptions } from '../documents/Documents'; -import { ClientUtils } from '../util/ClientUtils'; -import { DictationManager } from '../util/DictationManager'; -import { SetupDrag } from '../util/DragManager'; import { HistoryUtil } from '../util/History'; import SharingManager from '../util/SharingManager'; import { Transform } from '../util/Transform'; -import { UndoManager } from '../util/UndoManager'; +import { CollectionLinearView } from './CollectionLinearView'; import { CollectionBaseView, CollectionViewType } from './collections/CollectionBaseView'; import { CollectionDockingView } from './collections/CollectionDockingView'; -import { CollectionTreeView } from './collections/CollectionTreeView'; import { ContextMenu } from './ContextMenu'; +import { DictationOverlay } from './DictationOverlay'; import { DocumentDecorations } from './DocumentDecorations'; import KeyManager from './GlobalKeyHandler'; -import { InkingControl } from './InkingControl'; -import "./Main.scss"; -import { MainOverlayTextBox } from './MainOverlayTextBox'; -import MainViewModal from './MainViewModal'; +import "./MainView.scss"; +import { MainViewNotifs } from './MainViewNotifs'; import { DocumentView } from './nodes/DocumentView'; -import { PresBox } from './nodes/PresBox'; import { OverlayView } from './OverlayView'; import PDFMenu from './pdf/PDFMenu'; import { PreviewCursor } from './PreviewCursor'; -import { FilterBox } from './search/FilterBox'; import MarqueeOptionsMenu from './collections/collectionFreeForm/MarqueeOptionsMenu'; @observer export class MainView extends React.Component { public static Instance: MainView; - @observable addMenuToggle = React.createRef<HTMLInputElement>(); - @observable public pwidth: number = 0; - @observable public pheight: number = 0; + private _buttonBarHeight = 75; + private _flyoutSizeOnDown = 0; + private _urlState: HistoryUtil.DocUrl; - @observable private dictationState = DictationManager.placeholder; - @observable private dictationSuccessState: boolean | undefined = undefined; - @observable private dictationDisplayState = false; - @observable private dictationListeningState: DictationManager.Controls.ListeningUIStatus = false; + @observable private _panelWidth: number = 0; + @observable private _panelHeight: number = 0; + @observable private _flyoutTranslate: boolean = true; + @observable public flyoutWidth: number = 250; - public hasActiveModal = false; + @computed private get userDoc() { return CurrentUserUtils.UserDocument; } + @computed private get mainContainer() { return this.userDoc ? FieldValue(Cast(this.userDoc.activeWorkspace, Doc)) : CurrentUserUtils.GuestWorkspace; } + @computed public get mainFreeform(): Opt<Doc> { return (docs => (docs && docs.length > 1) ? docs[1] : undefined)(DocListCast(this.mainContainer!.data)); } - public overlayTimeout: NodeJS.Timeout | undefined; - - public initiateDictationFade = () => { - let duration = DictationManager.Commands.dictationFadeDuration; - this.overlayTimeout = setTimeout(() => { - this.dictationOverlayVisible = false; - this.dictationSuccess = undefined; - this.hasActiveModal = false; - setTimeout(() => this.dictatedPhrase = DictationManager.placeholder, 500); - }, duration); - } - - private urlState: HistoryUtil.DocUrl; - - @computed private get userDoc() { - return CurrentUserUtils.UserDocument; - } - - public cancelDictationFade = () => { - if (this.overlayTimeout) { - clearTimeout(this.overlayTimeout); - this.overlayTimeout = undefined; - } - } - - @computed private get mainContainer(): Opt<Doc> { - return this.userDoc ? FieldValue(Cast(this.userDoc.activeWorkspace, Doc)) : CurrentUserUtils.GuestWorkspace; - } - @computed get mainFreeform(): Opt<Doc> { - let docs = DocListCast(this.mainContainer!.data); - return (docs && docs.length > 1) ? docs[1] : undefined; - } public isPointerDown = false; - private set mainContainer(doc: Opt<Doc>) { - if (doc) { - if (!("presentationView" in doc)) { - doc.presentationView = new List<Doc>([Docs.Create.TreeDocument([], { title: "Presentation" })]); - } - this.userDoc ? (this.userDoc.activeWorkspace = doc) : (CurrentUserUtils.GuestWorkspace = doc); - } - } - - @computed public get dictatedPhrase() { - return this.dictationState; - } - - public set dictatedPhrase(value: string) { - runInAction(() => this.dictationState = value); - } - - @computed public get dictationSuccess() { - return this.dictationSuccessState; - } - - public set dictationSuccess(value: boolean | undefined) { - runInAction(() => this.dictationSuccessState = value); - } - - @computed public get dictationOverlayVisible() { - return this.dictationDisplayState; - } - - public set dictationOverlayVisible(value: boolean) { - runInAction(() => this.dictationDisplayState = value); - } - - @computed public get isListening() { - return this.dictationListeningState; - } - - public set isListening(value: DictationManager.Controls.ListeningUIStatus) { - runInAction(() => this.dictationListeningState = value); - } componentWillMount() { var tag = document.createElement('script'); @@ -139,29 +61,10 @@ export class MainView extends React.Component { firstScriptTag.parentNode!.insertBefore(tag, firstScriptTag); window.removeEventListener("keydown", KeyManager.Instance.handle); window.addEventListener("keydown", KeyManager.Instance.handle); - - if (this.userDoc) { - reaction(() => { - let workspaces = this.userDoc.workspaces; - let recent = this.userDoc.recentlyClosed; - if (!(recent instanceof Doc)) return 0; - if (!(workspaces instanceof Doc)) return 0; - let workspacesDoc = workspaces; - let recentDoc = recent; - let libraryHeight = this.getPHeight() - workspacesDoc[HeightSym]() - recentDoc[HeightSym]() - 20 + this.userDoc[HeightSym]() * 0.00001; - return libraryHeight; - }, (libraryHeight: number) => { - if (libraryHeight && Math.abs(this.userDoc[HeightSym]() - libraryHeight) > 5) { - this.userDoc.height = libraryHeight; - } - (Cast(this.userDoc.recentlyClosed, Doc) as Doc).allowClear = true; - }, { fireImmediately: true }); - } } componentWillUnMount() { window.removeEventListener("keydown", KeyManager.Instance.handle); - //close presentation window.removeEventListener("pointerdown", this.globalPointerDown); window.removeEventListener("pointerup", this.globalPointerUp); } @@ -169,7 +72,7 @@ export class MainView extends React.Component { constructor(props: Readonly<{}>) { super(props); MainView.Instance = this; - this.urlState = HistoryUtil.parseUrl(window.location) || {} as any; + this._urlState = HistoryUtil.parseUrl(window.location) || {} as any; // causes errors to be generated when modifying an observable outside of an action configure({ enforceActions: "observed" }); if (window.location.pathname !== RouteStore.home) { @@ -232,7 +135,6 @@ export class MainView extends React.Component { globalPointerUp = () => this.isPointerDown = false; 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 // click interactions for the context menu @@ -250,25 +152,17 @@ export class MainView extends React.Component { { fireImmediately: true } ); } else { - if (received && this.urlState.sharing) { - reaction( - () => { - let docking = CollectionDockingView.Instance; - return docking && docking.initialized; - }, - initialized => { - if (initialized && received) { - DocServer.GetRefField(received).then(field => { - if (field instanceof Doc && field.viewType !== CollectionViewType.Docking) { - CollectionDockingView.AddRightSplit(field, undefined); - } - }); + if (received && this._urlState.sharing) { + reaction(() => CollectionDockingView.Instance && CollectionDockingView.Instance.initialized, + initialized => initialized && received && DocServer.GetRefField(received).then(field => { + if (field instanceof Doc && field.viewType !== CollectionViewType.Docking) { + CollectionDockingView.AddRightSplit(field, undefined); } - }, + }), ); } - let doc: Opt<Doc>; - if (this.userDoc && (doc = await Cast(this.userDoc.activeWorkspace, Doc))) { + let doc = this.userDoc && await Cast(this.userDoc.activeWorkspace, Doc); + if (doc) { this.openWorkspace(doc); } else { this.createNewWorkspace(); @@ -281,39 +175,36 @@ export class MainView extends React.Component { let freeformOptions: DocumentOptions = { x: 0, y: 400, - width: this.pwidth * .7, - height: this.pheight, + width: this._panelWidth * .7, + height: this._panelHeight, title: "My Blank Collection" }; let workspaces: FieldResult<Doc>; let freeformDoc = CurrentUserUtils.GuestTarget || Docs.Create.FreeformDocument([], freeformOptions); var dockingLayout = { content: [{ type: 'row', content: [CollectionDockingView.makeDocumentConfig(freeformDoc, freeformDoc, 600)] }] }; - let mainDoc = Docs.Create.DockDocument([this.userDoc, freeformDoc], JSON.stringify(dockingLayout), {}, id); + let mainDoc = Docs.Create.DockDocument([freeformDoc], JSON.stringify(dockingLayout), {}, id); if (this.userDoc && ((workspaces = Cast(this.userDoc.workspaces, Doc)) instanceof Doc)) { - const list = Cast((workspaces).data, listSpec(Doc)); - if (list) { - if (!this.userDoc.linkManagerDoc) { - let linkManagerDoc = new Doc(); - linkManagerDoc.allLinks = new List<Doc>([]); - this.userDoc.linkManagerDoc = linkManagerDoc; - } - list.push(mainDoc); - mainDoc.title = `Workspace ${list.length}`; + if (!this.userDoc.linkManagerDoc) { + let linkManagerDoc = new Doc(); + linkManagerDoc.allLinks = new List<Doc>([]); + this.userDoc.linkManagerDoc = linkManagerDoc; } + Doc.AddDocToList(workspaces, "data", mainDoc); + mainDoc.title = `Workspace ${DocListCast(workspaces.data).length}`; } // 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.StackingDocument([], { title: "New Mobile Uploads" }); - // mainDoc.optionalRightCollection = pendingDocument; - }, 0); + setTimeout(() => this.openWorkspace(mainDoc), 0); } @action openWorkspace = async (doc: Doc, fromHistory = false) => { CurrentUserUtils.MainDocId = doc[Id]; - this.mainContainer = doc; - let state = this.urlState; + + if (doc) { // this has the side-effect of setting the main container since we're assigning the active/guest workspace + !("presentationView" in doc) && (doc.presentationView = new List<Doc>([Docs.Create.TreeDocument([], { title: "Presentation" })])); + this.userDoc ? (this.userDoc.activeWorkspace = doc) : (CurrentUserUtils.GuestWorkspace = doc); + } + let state = this._urlState; if (state.sharing === true && !this.userDoc) { DocServer.Control.makeReadOnly(); } else { @@ -332,21 +223,16 @@ export class MainView extends React.Component { } CollectionBaseView.SetSafeMode(true); } else if (state.nro || state.nro === null || state.readonly === false) { - } else if (BoolCast(doc.readOnly)) { + } else if (doc.readOnly) { DocServer.Control.makeReadOnly(); } else { DocServer.Control.makeEditable(); } } - let col: Opt<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 (this.userDoc && (col = await Cast(this.userDoc.optionalRightCollection, Doc))) { - const l = Cast(col.data, listSpec(Doc)); - if (l) { - runInAction(() => CollectionTreeView.NotifsCol = col); - } - } + const col = this.userDoc && await Cast(this.userDoc.optionalRightCollection, Doc); + col && Cast(col.data, listSpec(Doc)) && runInAction(() => MainViewNotifs.NotifsCol = col); }, 100); return true; } @@ -359,27 +245,20 @@ export class MainView extends React.Component { @action onResize = (r: any) => { - this.pwidth = r.offset.width; - this.pheight = r.offset.height; - } - getPWidth = () => { - return this.pwidth; - } - getPHeight = () => { - return this.pheight; + this._panelWidth = r.offset.width; + this._panelHeight = r.offset.height; } + getPWidth = () => this._panelWidth; + getPHeight = () => this._panelHeight; + getContentsHeight = () => this._panelHeight - this._buttonBarHeight; - @observable flyoutWidth: number = 250; - @observable flyoutTranslate: boolean = true; @computed get dockingContent() { - let flyoutWidth = this.flyoutWidth; - let countWidth = this.flyoutTranslate; - let mainCont = this.mainContainer; + const mainContainer = this.mainContainer; return <Measure offset onResize={this.onResize}> {({ measureRef }) => - <div ref={measureRef} id="mainContent-div" style={{ width: `calc(100% - ${countWidth ? flyoutWidth : 0}px`, transform: `translate(${countWidth ? flyoutWidth : 0}px, 0px)` }} onDrop={this.onDrop}> - {!mainCont ? (null) : - <DocumentView Document={mainCont} + <div ref={measureRef} className="mainView-mainDiv" onDrop={this.onDrop}> + {!mainContainer ? (null) : + <DocumentView Document={mainContainer} DataDoc={undefined} addDocument={undefined} addDocTab={this.addDocTabFunc} @@ -407,9 +286,8 @@ export class MainView extends React.Component { </Measure>; } - _downsize = 0; onPointerDown = (e: React.PointerEvent) => { - this._downsize = e.clientX; + this._flyoutSizeOnDown = e.clientX; document.removeEventListener("pointermove", this.onPointerMove); document.removeEventListener("pointerup", this.onPointerUp); document.addEventListener("pointermove", this.onPointerMove); @@ -422,15 +300,15 @@ export class MainView extends React.Component { pointerOverDragger = () => { if (this.flyoutWidth === 0) { this.flyoutWidth = 250; - this.flyoutTranslate = false; + this._flyoutTranslate = false; } } @action pointerLeaveDragger = () => { - if (!this.flyoutTranslate) { + if (!this._flyoutTranslate) { this.flyoutWidth = 0; - this.flyoutTranslate = true; + this._flyoutTranslate = true; } } @@ -440,9 +318,8 @@ export class MainView extends React.Component { } @action onPointerUp = (e: PointerEvent) => { - if (Math.abs(e.clientX - this._downsize) < 4) { - if (this.flyoutWidth < 5) this.flyoutWidth = 250; - else this.flyoutWidth = 0; + if (Math.abs(e.clientX - this._flyoutSizeOnDown) < 4) { + this.flyoutWidth = this.flyoutWidth < 5 ? 250 : 0; } document.removeEventListener("pointermove", this.onPointerMove); document.removeEventListener("pointerup", this.onPointerUp); @@ -461,65 +338,93 @@ export class MainView extends React.Component { } @computed get flyout() { - let sidebar: FieldResult<Field>; - if (!this.userDoc || !((sidebar = this.userDoc.sidebar) instanceof Doc)) { + let sidebarContent = this.userDoc && this.userDoc.sidebarContainer; + if (!(sidebarContent instanceof Doc)) { return (null); } - return <DocumentView - Document={sidebar} - DataDoc={undefined} - addDocument={undefined} - addDocTab={this.addDocTabFunc} - pinToPres={emptyFunction} - removeDocument={undefined} - ruleProvider={undefined} - onClick={undefined} - ScreenToLocalTransform={Transform.Identity} - ContentScaling={returnOne} - PanelWidth={this.flyoutWidthFunc} - PanelHeight={this.getPHeight} - renderDepth={0} - focus={emptyFunction} - backgroundColor={returnEmptyString} - parentActive={returnTrue} - whenActiveChanged={emptyFunction} - bringToFront={emptyFunction} - ContainingCollectionView={undefined} - ContainingCollectionDoc={undefined} - zoomToScale={emptyFunction} - getScale={returnOne}> - </DocumentView>; + let libraryButtonDoc = Cast(CurrentUserUtils.UserDocument.libraryButtons, Doc) as Doc; + libraryButtonDoc.columnWidth = this.flyoutWidth / 3 - 30; + return <div className="mainView-flyoutContainer"> + <div className="mainView-tabButtons" style={{ height: `${this._buttonBarHeight}px` }}> + <DocumentView + Document={libraryButtonDoc} + DataDoc={undefined} + addDocument={undefined} + addDocTab={this.addDocTabFunc} + pinToPres={emptyFunction} + removeDocument={undefined} + ruleProvider={undefined} + onClick={undefined} + ScreenToLocalTransform={Transform.Identity} + ContentScaling={returnOne} + PanelWidth={this.flyoutWidthFunc} + PanelHeight={this.getPHeight} + renderDepth={0} + focus={emptyFunction} + backgroundColor={returnEmptyString} + parentActive={returnTrue} + whenActiveChanged={emptyFunction} + bringToFront={emptyFunction} + ContainingCollectionView={undefined} + ContainingCollectionDoc={undefined} + zoomToScale={emptyFunction} + getScale={returnOne}> + </DocumentView> + </div> + <div style={{ position: "relative", height: `calc(100% - ${this._buttonBarHeight}px)`, width: "100%", overflow: "auto" }}> + <DocumentView + Document={sidebarContent} + DataDoc={undefined} + addDocument={undefined} + addDocTab={this.addDocTabFunc} + pinToPres={emptyFunction} + removeDocument={returnFalse} + ruleProvider={undefined} + onClick={undefined} + ScreenToLocalTransform={Transform.Identity} + ContentScaling={returnOne} + PanelWidth={this.flyoutWidthFunc} + PanelHeight={this.getContentsHeight} + renderDepth={0} + focus={emptyFunction} + backgroundColor={returnEmptyString} + parentActive={returnTrue} + whenActiveChanged={emptyFunction} + bringToFront={emptyFunction} + ContainingCollectionView={undefined} + ContainingCollectionDoc={undefined} + zoomToScale={emptyFunction} + getScale={returnOne}> + </DocumentView> + <button className="mainView-logout" key="logout" onClick={() => window.location.assign(Utils.prepend(RouteStore.logout))}> + {CurrentUserUtils.GuestWorkspace ? "Exit" : "Log Out"} + </button> + </div></div>; } @computed get mainContent() { - if (!this.userDoc) { - return (<div>{this.dockingContent}</div>); - } - let sidebar = this.userDoc.sidebar; - if (!(sidebar instanceof Doc)) { - return (null); - } - return ( - <div className="mainContent" style={{ width: "100%", height: "100%", position: "absolute" }}> - <div onPointerLeave={this.pointerLeaveDragger}> + const sidebar = this.userDoc && this.userDoc.sidebarContainer; + return !this.userDoc || !(sidebar instanceof Doc) ? (null) : ( + <div className="mainView-mainContent" > + <div className="mainView-flyoutContainer" onPointerLeave={this.pointerLeaveDragger}> <div className="mainView-libraryHandle" - style={{ cursor: "ew-resize", left: `${(this.flyoutWidth * (this.flyoutTranslate ? 1 : 0)) - 10}px`, backgroundColor: `${StrCast(sidebar.backgroundColor, "lightGray")}` }} + style={{ cursor: "ew-resize", left: `${(this.flyoutWidth * (this._flyoutTranslate ? 1 : 0)) - 10}px`, backgroundColor: `${StrCast(sidebar.backgroundColor, "lightGray")}` }} onPointerDown={this.onPointerDown} onPointerOver={this.pointerOverDragger}> <span title="library View Dragger" style={{ - width: (this.flyoutWidth !== 0 && this.flyoutTranslate) ? "100%" : "5vw", - height: (this.flyoutWidth !== 0 && this.flyoutTranslate) ? "100%" : "30vh", + width: (this.flyoutWidth !== 0 && this._flyoutTranslate) ? "100%" : "5vw", + height: (this.flyoutWidth !== 0 && this._flyoutTranslate) ? "100%" : "30vh", position: "absolute", - top: (this.flyoutWidth !== 0 && this.flyoutTranslate) ? "" : "-10vh" + top: (this.flyoutWidth !== 0 && this._flyoutTranslate) ? "" : "-10vh" }} /> </div> <div className="mainView-libraryFlyout" style={{ width: `${this.flyoutWidth}px`, zIndex: 1, - transformOrigin: this.flyoutTranslate ? "" : "left center", - transition: this.flyoutTranslate ? "" : "width .5s", - transform: `scale(${this.flyoutTranslate ? 1 : 0.8})`, - boxShadow: this.flyoutTranslate ? "" : "rgb(156, 147, 150) 0.2vw 0.2vw 0.8vw" + transformOrigin: this._flyoutTranslate ? "" : "left center", + transition: this._flyoutTranslate ? "" : "width .5s", + transform: `scale(${this._flyoutTranslate ? 1 : 0.8})`, + boxShadow: this._flyoutTranslate ? "" : "rgb(156, 147, 150) 0.2vw 0.2vw 0.8vw" }}> {this.flyout} {this.expandButton} @@ -530,168 +435,66 @@ export class MainView extends React.Component { } @computed get expandButton() { - return !this.flyoutTranslate ? (<div className="expandFlyoutButton" title="Re-attach sidebar" onPointerDown={() => { + return !this._flyoutTranslate ? (<div className="mainView-expandFlyoutButton" title="Re-attach sidebar" onPointerDown={() => { runInAction(() => { this.flyoutWidth = 250; - this.flyoutTranslate = true; + this._flyoutTranslate = true; }); }}><FontAwesomeIcon icon="chevron-right" color="grey" size="lg" /></div>) : (null); } - selected = (tool: InkTool) => { - if (!InkingControl.Instance || InkingControl.Instance.selectedTool === InkTool.None) return { display: "none" }; - if (InkingControl.Instance.selectedTool === tool) { - return { color: "#61aaa3", fontSize: "50%" }; - } - return { fontSize: "50%" }; - } - - onColorClick = (e: React.MouseEvent) => { - let target = (e.nativeEvent as any).path[0]; - let parent = (e.nativeEvent as any).path[1]; - if (target.localName === "input" || parent.localName === "span") { - e.stopPropagation(); - } - } - - setWriteMode = (mode: DocServer.WriteMode) => { - console.log(DocServer.WriteMode[mode]); - const mode1 = mode; - const mode2 = mode === DocServer.WriteMode.Default ? mode : DocServer.WriteMode.Playground; - DocServer.setFieldWriteMode("x", mode1); - DocServer.setFieldWriteMode("y", mode1); - DocServer.setFieldWriteMode("width", mode1); - DocServer.setFieldWriteMode("height", mode1); - - DocServer.setFieldWriteMode("panX", mode2); - DocServer.setFieldWriteMode("panY", mode2); - DocServer.setFieldWriteMode("scale", mode2); - DocServer.setFieldWriteMode("viewType", mode2); - } - - - @observable private _colorPickerDisplay = false; - /* 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 addColNode = action(() => Docs.Create.FreeformDocument([], { width: this.pwidth * .7, height: this.pheight, title: "a freeform collection" })); - let addPresNode = action(() => Doc.UserDoc().curPresentation = Docs.Create.PresDocument(new List<Doc>(), { width: 200, height: 500, title: "a presentation trail" })); - let addWebNode = action(() => Docs.Create.WebDocument("https://en.wikipedia.org/wiki/Hedgehog", { width: 300, height: 300, title: "New Webpage" })); - let addDragboxNode = action(() => Docs.Create.DragboxDocument({ width: 40, height: 40, title: "drag collection" })); - let addImageNode = action(() => Docs.Create.ImageDocument(imgurl, { width: 200, title: "an image of a cat" })); - let addButtonDocument = action(() => Docs.Create.ButtonDocument({ width: 150, height: 50, title: "Button" })); - let addImportCollectionNode = action(() => Docs.Create.DirectoryImportDocument({ title: "Directory Import", width: 400, height: 400 })); - // let youtubeurl = "https://www.youtube.com/embed/TqcApsGRzWw"; - // let addYoutubeSearcher = action(() => Docs.Create.YoutubeDocument(youtubeurl, { width: 600, height: 600, title: "youtube search" })); - - // let googlePhotosSearch = () => GooglePhotosClientUtils.CollectionFromSearch(Docs.Create.MasonryDocument, { included: [GooglePhotosClientUtils.ContentCategories.LANDSCAPES] }); - - let btns: [React.RefObject<HTMLDivElement>, IconName, string, () => Doc | Promise<Doc>][] = [ - [React.createRef<HTMLDivElement>(), "object-group", "Add Collection", addColNode], - [React.createRef<HTMLDivElement>(), "tv", "Add Presentation Trail", addPresNode], - [React.createRef<HTMLDivElement>(), "globe-asia", "Add Website", addWebNode], - [React.createRef<HTMLDivElement>(), "bolt", "Add Button", addButtonDocument], - [React.createRef<HTMLDivElement>(), "file", "Add Document Dragger", addDragboxNode], - // [React.createRef<HTMLDivElement>(), "object-group", "Test Google Photos Search", googlePhotosSearch], - [React.createRef<HTMLDivElement>(), "cloud-upload-alt", "Import Directory", addImportCollectionNode], //remove at some point in favor of addImportCollectionNode - //[React.createRef<HTMLDivElement>(), "play", "Add Youtube Searcher", addYoutubeSearcher], - ]; - if (!ClientUtils.RELEASE) btns.unshift([React.createRef<HTMLDivElement>(), "cat", "Add Cat Image", addImageNode]); - - return < div id="add-nodes-menu" style={{ left: (this.flyoutTranslate ? this.flyoutWidth : 0) + 20, bottom: 20 }} > - - <input type="checkbox" id="add-menu-toggle" ref={this.addMenuToggle} /> - <label htmlFor="add-menu-toggle" style={{ marginTop: 2 }} title="Close Menu"><p>+</p></label> - - <div id="add-options-content"> - <ul id="add-options-list"> - <li key="search"><button className="add-button round-button" title="Search" onClick={this.toggleSearch}><FontAwesomeIcon icon="search" size="sm" /></button></li> - <li key="undo"><button className="add-button round-button" title="Undo" style={{ opacity: UndoManager.CanUndo() ? 1 : 0.5, transition: "0.4s ease all" }} onClick={() => UndoManager.Undo()}><FontAwesomeIcon icon="undo-alt" size="sm" /></button></li> - <li key="redo"><button className="add-button round-button" title="Redo" style={{ opacity: UndoManager.CanRedo() ? 1 : 0.5, transition: "0.4s ease all" }} onClick={() => UndoManager.Redo()}><FontAwesomeIcon icon="redo-alt" size="sm" /></button></li> - {btns.map(btn => - <li key={btn[1]} ><div ref={btn[0]}> - <button className="round-button add-button" title={btn[2]} onPointerDown={SetupDrag(btn[0], btn[3])}> - <FontAwesomeIcon icon={btn[1]} size="sm" /> - </button> - </div></li>)} - <li key="undoTest"><button className="add-button round-button" title="Click if undo isn't working" onClick={() => UndoManager.TraceOpenBatches()}><FontAwesomeIcon icon="exclamation" size="sm" /></button></li> - <li key="color"><button className="add-button round-button" title="Select Color" style={{ zIndex: 1000 }} onClick={() => this.toggleColorPicker()}><div className="toolbar-color-button" style={{ backgroundColor: InkingControl.Instance.selectedColor }} > - <div className="toolbar-color-picker" onClick={this.onColorClick} style={this._colorPickerDisplay ? { color: "black", display: "block" } : { color: "black", display: "none" }}> - <SketchPicker color={InkingControl.Instance.selectedColor} onChange={InkingControl.Instance.switchColor} /> - </div> - </div></button></li> - <li key="ink" style={{ paddingRight: "6px" }}><button className="toolbar-button round-button" title="Ink" onClick={() => InkingControl.Instance.toggleDisplay()}><FontAwesomeIcon icon="pen-nib" size="sm" /> </button></li> - <li key="pen"><button onClick={() => InkingControl.Instance.switchTool(InkTool.Pen)} title="Pen" style={this.selected(InkTool.Pen)}><FontAwesomeIcon icon="pen" size="lg" /></button></li> - <li key="marker"><button onClick={() => InkingControl.Instance.switchTool(InkTool.Highlighter)} title="Highlighter" style={this.selected(InkTool.Highlighter)}><FontAwesomeIcon icon="highlighter" size="lg" /></button></li> - <li key="eraser"><button onClick={() => InkingControl.Instance.switchTool(InkTool.Eraser)} title="Eraser" style={this.selected(InkTool.Eraser)}><FontAwesomeIcon icon="eraser" size="lg" /></button></li> - <li key="inkControls"><InkingControl /></li> - <li key="logout"><button onClick={() => window.location.assign(Utils.prepend(RouteStore.logout))}>{CurrentUserUtils.GuestWorkspace ? "Exit" : "Log Out"}</button></li> - </ul> - </div> - </div >; - } - - - - @action - toggleColorPicker = (close = false) => { - this._colorPickerDisplay = close ? false : !this._colorPickerDisplay; - } - - /* @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() { - return [ - this.isSearchVisible ? <div className="main-searchDiv" key="search" style={{ top: '34px', right: '1px', position: 'absolute' }} > <FilterBox /> </div> : null, - ]; - + addButtonDoc = (doc: Doc) => { + Doc.AddDocToList(CurrentUserUtils.UserDocument, "docButtons", doc); + return true; } - - @observable isSearchVisible = false; - @action.bound - toggleSearch = () => { - this.isSearchVisible = !this.isSearchVisible; + remButtonDoc = (doc: Doc) => { + Doc.RemoveDocFromList(CurrentUserUtils.UserDocument, "docButtons", doc); + return true; } - - @computed private get dictationOverlay() { - let success = this.dictationSuccess; - let result = this.isListening && !this.isListening.interim ? DictationManager.placeholder : `"${this.dictatedPhrase}"`; - let dialogueBoxStyle = { - background: success === undefined ? "gainsboro" : success ? "lawngreen" : "red", - borderColor: this.isListening ? "red" : "black", - fontStyle: "italic" - }; - let overlayStyle = { - backgroundColor: this.isListening ? "red" : "darkslategrey" - }; - return ( - <MainViewModal - contents={result} - isDisplayed={this.dictationOverlayVisible} - interactive={false} - dialogueBoxStyle={dialogueBoxStyle} - overlayStyle={overlayStyle} - /> - ); + @computed get docButtons() { + return <div className="mainView-docButtons" style={{ left: (this._flyoutTranslate ? this.flyoutWidth : 0) + 20 }} > + <MainViewNotifs /> + <CollectionLinearView Document={CurrentUserUtils.UserDocument} DataDoc={undefined} + fieldKey={"docButtons"} + fieldExt={""} + showHiddenControls={true} + select={emptyFunction} + chromeCollapsed={true} + active={returnFalse} + isSelected={returnFalse} + moveDocument={returnFalse} + CollectionView={undefined} + addDocument={this.addButtonDoc} + addDocTab={this.addDocTabFunc} + pinToPres={emptyFunction} + removeDocument={this.remButtonDoc} + ruleProvider={undefined} + onClick={undefined} + ScreenToLocalTransform={Transform.Identity} + ContentScaling={returnOne} + PanelWidth={this.flyoutWidthFunc} + PanelHeight={this.getContentsHeight} + renderDepth={0} + focus={emptyFunction} + whenActiveChanged={emptyFunction} + ContainingCollectionView={undefined} + ContainingCollectionDoc={undefined} /> + </div>; } render() { - return ( - <div id="main-div"> - {this.dictationOverlay} - <SharingManager /> - <DocumentDecorations /> - {this.mainContent} - <PreviewCursor /> - <ContextMenu /> - {this.nodesMenu()} - {this.miscButtons} - <PDFMenu /> - <MarqueeOptionsMenu /> - <MainOverlayTextBox firstinstance={true} /> - <OverlayView /> - </div > - ); + return (<div className="mainView-container"> + <DictationOverlay /> + <SharingManager /> + <GoogleAuthenticationManager /> + <DocumentDecorations /> + {this.mainContent} + <PreviewCursor /> + <ContextMenu /> + {this.docButtons} + <PDFMenu /> + <MarqueeOptionsMenu /> + <OverlayView /> + </div >); } } |