diff options
-rw-r--r-- | src/client/util/DictationManager.ts | 21 | ||||
-rw-r--r-- | src/client/util/SharingManager.tsx | 5 | ||||
-rw-r--r-- | src/client/views/CollectionLinearView.scss | 74 | ||||
-rw-r--r-- | src/client/views/CollectionLinearView.tsx | 106 | ||||
-rw-r--r-- | src/client/views/DictationOverlay.tsx | 71 | ||||
-rw-r--r-- | src/client/views/GlobalKeyHandler.ts | 8 | ||||
-rw-r--r-- | src/client/views/InkingControl.tsx | 5 | ||||
-rw-r--r-- | src/client/views/Main.scss | 219 | ||||
-rw-r--r-- | src/client/views/MainView.scss | 97 | ||||
-rw-r--r-- | src/client/views/MainView.tsx | 268 | ||||
-rw-r--r-- | src/client/views/nodes/DocumentView.tsx | 9 | ||||
-rw-r--r-- | src/client/views/nodes/ImageBox.scss | 4 |
12 files changed, 421 insertions, 466 deletions
diff --git a/src/client/util/DictationManager.ts b/src/client/util/DictationManager.ts index 3b2307073..182cfb70a 100644 --- a/src/client/util/DictationManager.ts +++ b/src/client/util/DictationManager.ts @@ -14,6 +14,7 @@ import { HistogramField } from "../northstar/dash-fields/HistogramField"; import { MainView } from "../views/MainView"; import { Utils } from "../../Utils"; import { RichTextField } from "../../new_fields/RichTextField"; +import { DictationOverlay } from "../views/DictationOverlay"; /** * This namespace provides a singleton instance of a manager that @@ -88,12 +89,11 @@ export namespace DictationManager { export const listen = async (options?: Partial<ListeningOptions>) => { let results: string | undefined; - let main = MainView.Instance; let overlay = options !== undefined && options.useOverlay; if (overlay) { - main.dictationOverlayVisible = true; - main.isListening = { interim: false }; + DictationOverlay.Instance.dictationOverlayVisible = true; + DictationOverlay.Instance.isListening = { interim: false }; } try { @@ -101,21 +101,21 @@ export namespace DictationManager { if (results) { Utils.CopyText(results); if (overlay) { - main.isListening = false; + DictationOverlay.Instance.isListening = false; let execute = options && options.tryExecute; - main.dictatedPhrase = execute ? results.toLowerCase() : results; - main.dictationSuccess = execute ? await DictationManager.Commands.execute(results) : true; + DictationOverlay.Instance.dictatedPhrase = execute ? results.toLowerCase() : results; + DictationOverlay.Instance.dictationSuccess = execute ? await DictationManager.Commands.execute(results) : true; } options && options.tryExecute && await DictationManager.Commands.execute(results); } } catch (e) { if (overlay) { - main.isListening = false; - main.dictatedPhrase = results = `dictation error: ${"error" in e ? e.error : "unknown error"}`; - main.dictationSuccess = false; + DictationOverlay.Instance.isListening = false; + DictationOverlay.Instance.dictatedPhrase = results = `dictation error: ${"error" in e ? e.error : "unknown error"}`; + DictationOverlay.Instance.dictationSuccess = false; } } finally { - overlay && main.initiateDictationFade(); + overlay && DictationOverlay.Instance.initiateDictationFade(); } return results; @@ -146,7 +146,6 @@ export namespace DictationManager { recognizer.start(); return new Promise<string>((resolve, reject) => { - recognizer.onerror = (e: SpeechRecognitionError) => { if (!(indefinite && e.error === "no-speech")) { recognizer.stop(); diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx index d37cd1b80..2082d6324 100644 --- a/src/client/util/SharingManager.tsx +++ b/src/client/util/SharingManager.tsx @@ -18,6 +18,7 @@ import { DocumentView } from "../views/nodes/DocumentView"; import { SelectionManager } from "./SelectionManager"; import { DocumentManager } from "./DocumentManager"; import { CollectionView } from "../views/collections/CollectionView"; +import { DictationOverlay } from "../views/DictationOverlay"; library.add(fa.faCopy); @@ -71,7 +72,7 @@ export default class SharingManager extends React.Component<{}> { this.populateUsers().then(action(() => { this.targetDocView = target; this.targetDoc = target.props.Document; - MainView.Instance.hasActiveModal = true; + DictationOverlay.Instance.hasActiveModal = true; this.isOpen = true; if (!this.sharingDoc) { this.sharingDoc = new Doc; @@ -84,7 +85,7 @@ export default class SharingManager extends React.Component<{}> { this.users = []; setTimeout(action(() => { this.copied = false; - MainView.Instance.hasActiveModal = false; + DictationOverlay.Instance.hasActiveModal = false; this.targetDoc = undefined; }), 500); }); diff --git a/src/client/views/CollectionLinearView.scss b/src/client/views/CollectionLinearView.scss new file mode 100644 index 000000000..30be07a9f --- /dev/null +++ b/src/client/views/CollectionLinearView.scss @@ -0,0 +1,74 @@ +@import "globalCssVariables"; +@import "nodeModuleOverrides"; + +.collectionLinearView { + + >label { + background: $dark-color; + color: $light-color; + display: inline-block; + border-radius: 18px; + font-size: 25px; + width: 36px; + height: 36px; + margin-right: 10px; + cursor: pointer; + transition: transform 0.2s; + } + + label p { + padding-left: 10.5px; + } + + label:hover { + background: $main-accent; + transform: scale(1.15); + } + + >input { + display: none; + } + >input:not(:checked)~.collectionLinearView-content { + display: none; + } + + >input:checked~label { + transform: rotate(45deg); + transition: transform 0.5s; + cursor: pointer; + } + + .collectionLinearView-content { + display: flex; + opacity: 1; + margin: 0; + padding: 0; + position: relative; + float: right; + .collectionFreeFormDocumentView-container { + position: relative; + } + .collectionLinearView-docBtn { + position:relative; + margin-right: 10px; + width: 35px; + height: 35px; + } + .collectionLinearView-round-button { + width: 36px; + height: 36px; + border-radius: 18px; + font-size: 15px; + } + + .collectionLinearView-round-button:hover { + transform: scale(1.15); + } + + } + + .collectionLinearView-add-button { + position: relative; + margin-right: 10px; + } +} diff --git a/src/client/views/CollectionLinearView.tsx b/src/client/views/CollectionLinearView.tsx new file mode 100644 index 000000000..18e3598a5 --- /dev/null +++ b/src/client/views/CollectionLinearView.tsx @@ -0,0 +1,106 @@ +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { action, observable, computed } from 'mobx'; +import { observer } from 'mobx-react'; +import * as React from 'react'; +import { Doc, DocListCast, Opt } from '../../new_fields/Doc'; +import { InkTool } from '../../new_fields/InkField'; +import { ObjectField } from '../../new_fields/ObjectField'; +import { ScriptField } from '../../new_fields/ScriptField'; +import { NumCast, StrCast } from '../../new_fields/Types'; +import { emptyFunction, returnEmptyString, returnOne, returnTrue, returnFalse } from '../../Utils'; +import { Docs } from '../documents/Documents'; +import { DragManager } from '../util/DragManager'; +import { Transform } from '../util/Transform'; +import { UndoManager } from '../util/UndoManager'; +import { InkingControl } from './InkingControl'; +import { DocumentView } from './nodes/DocumentView'; +import "./CollectionLinearView.scss"; + +interface CollectionLinearViewProps { + Document: Doc; + fieldKey: string; +} + +@observer +export class CollectionLinearView extends React.Component<CollectionLinearViewProps>{ + @observable public addMenuToggle = React.createRef<HTMLInputElement>(); + private _dropDisposer?: DragManager.DragDropDisposer; + + componentWillUnmount() { + this._dropDisposer && this._dropDisposer(); + } + + protected createDropTarget = (ele: HTMLLabelElement) => { //used for stacking and masonry view + this._dropDisposer && this._dropDisposer(); + if (ele) { + this._dropDisposer = DragManager.MakeDropTarget(ele, { handlers: { drop: this.drop.bind(this) } }); + } + } + + drop = action((e: Event, de: DragManager.DropEvent) => { + (de.data as DragManager.DocumentDragData).draggedDocuments.map(doc => { + if (!doc.onDragStart) { + let dbox = Docs.Create.FontIconDocument({ nativeWidth: 100, nativeHeight: 100, width: 100, height: 100, backgroundColor: StrCast(doc.backgroundColor), title: "Custom", icon: "bolt" }); + dbox.dragFactory = doc; + dbox.removeDropProperties = doc.removeDropProperties instanceof ObjectField ? ObjectField.MakeCopy(doc.removeDropProperties) : undefined; + dbox.onDragStart = ScriptField.MakeFunction('getCopy(this.dragFactory, true)'); + doc = dbox; + } + Doc.AddDocToList(this.props.Document, this.props.fieldKey, doc); + }); + }); + + 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%" }; + } + + render() { + return <div className="collectionLinearView"> + <input id="collectionLinearView-toggle" type="checkbox" ref={this.addMenuToggle} /> + <label htmlFor="collectionLinearView-toggle" ref={this.createDropTarget} title="Close Menu"><p>+</p></label> + + <div className="collectionLinearView-content"> + <button key="undo" className="collectionLinearView-add-button collectionLinearView-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> + <button key="redo" className="collectionLinearView-add-button collectionLinearView-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> + + {DocListCast(this.props.Document[this.props.fieldKey]).map(doc => <div className="collectionLinearView-docBtn" key={StrCast(doc.title)} > + <DocumentView + Document={doc} + DataDoc={undefined} + addDocument={undefined} + addDocTab={returnFalse} + pinToPres={emptyFunction} + removeDocument={(doc: Doc) => Doc.RemoveDocFromList(this.props.Document, this.props.fieldKey, doc)} + ruleProvider={undefined} + onClick={undefined} + ScreenToLocalTransform={Transform.Identity} + ContentScaling={() => 35 / NumCast(doc.nativeWidth, 35)} + PanelWidth={() => 35} + PanelHeight={() => 35} + renderDepth={0} + focus={emptyFunction} + backgroundColor={returnEmptyString} + parentActive={returnTrue} + whenActiveChanged={emptyFunction} + bringToFront={emptyFunction} + ContainingCollectionView={undefined} + ContainingCollectionDoc={undefined} + zoomToScale={emptyFunction} + getScale={returnOne}> + </DocumentView> + </div>)} + {/* <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> */} + + <button className="collectionLinearView-toolbar-button collectionLinearView-round-button" title="Ink" onClick={() => InkingControl.Instance.toggleDisplay()}><FontAwesomeIcon icon="pen-nib" size="sm" /> </button> + <button key="pen" onClick={() => InkingControl.Instance.switchTool(InkTool.Pen)} title="Pen" style={this.selected(InkTool.Pen)}><FontAwesomeIcon icon="pen" size="lg" /></button> + <button key="marker" onClick={() => InkingControl.Instance.switchTool(InkTool.Highlighter)} title="Highlighter" style={this.selected(InkTool.Highlighter)}><FontAwesomeIcon icon="highlighter" size="lg" /></button> + <button key="eraser" onClick={() => InkingControl.Instance.switchTool(InkTool.Eraser)} title="Eraser" style={this.selected(InkTool.Eraser)}><FontAwesomeIcon icon="eraser" size="lg" /></button> + <InkingControl /> + </div> + </div>; + } +}
\ No newline at end of file diff --git a/src/client/views/DictationOverlay.tsx b/src/client/views/DictationOverlay.tsx new file mode 100644 index 000000000..2accf9bfd --- /dev/null +++ b/src/client/views/DictationOverlay.tsx @@ -0,0 +1,71 @@ +import { computed, observable, runInAction } from 'mobx'; +import { observer } from 'mobx-react'; +import "normalize.css"; +import * as React from 'react'; +import { DictationManager } from '../util/DictationManager'; +import "./Main.scss"; +import MainViewModal from './MainViewModal'; + +@observer +export class DictationOverlay extends React.Component { + public static Instance: DictationOverlay; + @observable private _dictationState = DictationManager.placeholder; + @observable private _dictationSuccessState: boolean | undefined = undefined; + @observable private _dictationDisplayState = false; + @observable private _dictationListeningState: DictationManager.Controls.ListeningUIStatus = false; + + public isPointerDown = false; + public overlayTimeout: NodeJS.Timeout | undefined; + public hasActiveModal = false; + + constructor(props: any) { + super(props); + DictationOverlay.Instance = this; + } + + public initiateDictationFade = () => { + let duration = DictationManager.Commands.dictationFadeDuration; + this.overlayTimeout = setTimeout(() => { + this.dictationOverlayVisible = false; + this.dictationSuccess = undefined; + DictationOverlay.Instance.hasActiveModal = false; + setTimeout(() => this.dictatedPhrase = DictationManager.placeholder, 500); + }, duration); + } + public cancelDictationFade = () => { + if (this.overlayTimeout) { + clearTimeout(this.overlayTimeout); + this.overlayTimeout = undefined; + } + } + + @computed public get dictatedPhrase() { return this._dictationState; } + @computed public get dictationSuccess() { return this._dictationSuccessState; } + @computed public get dictationOverlayVisible() { return this._dictationDisplayState; } + @computed public get isListening() { return this._dictationListeningState; } + + public set dictatedPhrase(value: string) { runInAction(() => this._dictationState = value); } + public set dictationSuccess(value: boolean | undefined) { runInAction(() => this._dictationSuccessState = value); } + public set dictationOverlayVisible(value: boolean) { runInAction(() => this._dictationDisplayState = value); } + public set isListening(value: DictationManager.Controls.ListeningUIStatus) { runInAction(() => this._dictationListeningState = value); } + + render() { + 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} + />); + } +}
\ No newline at end of file diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts index 38fce4cf7..f31a29bdc 100644 --- a/src/client/views/GlobalKeyHandler.ts +++ b/src/client/views/GlobalKeyHandler.ts @@ -122,10 +122,10 @@ export default class KeyManager { let preventDefault = true; switch (keyname) { - case "n": - let toggle = MainView.Instance.addMenuToggle.current!; - toggle.checked = !toggle.checked; - break; + // case "n": + // let toggle = MainView.Instance.addMenuToggle.current!; + // toggle.checked = !toggle.checked; + // break; } return { diff --git a/src/client/views/InkingControl.tsx b/src/client/views/InkingControl.tsx index ee8b77050..38734a03d 100644 --- a/src/client/views/InkingControl.tsx +++ b/src/client/views/InkingControl.tsx @@ -18,7 +18,7 @@ library.add(faPen, faHighlighter, faEraser, faBan); @observer export class InkingControl extends React.Component { - static Instance: InkingControl = new InkingControl({}); + @observable static Instance: InkingControl; @observable private _selectedTool: InkTool = InkTool.None; @observable private _selectedColor: string = "rgb(244, 67, 54)"; @observable private _selectedWidth: string = "5"; @@ -26,7 +26,7 @@ export class InkingControl extends React.Component { constructor(props: Readonly<{}>) { super(props); - InkingControl.Instance = this; + runInAction(() => InkingControl.Instance = this); } @action @@ -45,7 +45,6 @@ export class InkingControl extends React.Component { switchColor = action((color: ColorResult): void => { this._selectedColor = color.hex + (color.rgb.a !== undefined ? this.decimalToHexString(Math.round(color.rgb.a * 255)) : "ff"); if (InkingControl.Instance.selectedTool === InkTool.None) { - // if (MainOverlayTextBox.Instance.SetColor(color.hex)) return; let selected = SelectionManager.SelectedDocuments(); let oldColors = selected.map(view => { let targetDoc = view.props.Document.layout instanceof Doc ? view.props.Document.layout : view.props.Document.isTemplate ? view.props.Document : Doc.GetProto(view.props.Document); diff --git a/src/client/views/Main.scss b/src/client/views/Main.scss index 55195b616..0335e12a5 100644 --- a/src/client/views/Main.scss +++ b/src/client/views/Main.scss @@ -21,7 +21,6 @@ div { } - .jsx-parser { width: 100%; height: 100%; @@ -64,228 +63,10 @@ button:hover { cursor: pointer; } -.clear-db-button { - position: absolute; - right: 45%; - bottom: 3%; - font-size: 50%; -} - -.round-button { - width: 36px; - height: 36px; - border-radius: 18px; - font-size: 15px; -} - -.round-button:hover { - transform: scale(1.15); -} - -.add-button { - position: relative; - margin-right: 10px; -} - -.main-undoButtons { - position: absolute; - width: 150px; - right: 0px; -} - -//toolbar stuff -#toolbar { - position: absolute; - right: 8px; - top: 5px; - - .toolbar-button { - display: block; - margin-bottom: 10px; - } -} - -.toolbar-color-picker { - background-color: $light-color; - border-radius: 5px; - padding: 12px; - position: absolute; - bottom: 36px; - left: -3px; - box-shadow: $intermediate-color 0.2vw 0.2vw 0.8vw; -} - -.toolbar-color-button { - border-radius: 11px; - width: 22px; - height: 22px; - cursor: pointer; - text-align: center; // span { - // color: $light-color; - // font-size: 8px; - // user-select: none; - // } - margin-top: -2.55px; - margin-left: -2.55px; -} - -// add nodes menu. Note that the + button is actually an input label, not an actual button. -#add-nodes-menu { - position: absolute; - bottom: 22px; - left: 250px; - - >label { - background: $dark-color; - color: $light-color; - display: inline-block; - border-radius: 18px; - font-size: 25px; - width: 36px; - height: 36px; - margin-right: 10px; - cursor: pointer; - transition: transform 0.2s; - } - - label p { - padding-left: 10.5px; - } - - label:hover { - background: $main-accent; - transform: scale(1.15); - } - - >input { - display: none; - } - - >input:not(:checked)~#add-options-content { - display: none; - } - - >input:checked~label { - transform: rotate(45deg); - transition: transform 0.5s; - cursor: pointer; - } -} - #root { overflow: visible; } -#main-div { - width: 100%; - height: 100%; - position: absolute; - top: 0; - left: 0; - overflow: auto; - z-index: 1; -} -.mainContent { - width:100%; - height:100%; - position:absolute; -} -.mainView-flyoutContainer{ - display:flex; - flex-direction: column; - position: absolute; - width:100%; - height:100%; - border: black 1px solid; - .documentView-node-topmost { - background: lightgrey; - } -} -#mainContent-div { - width: 100%; - height: 100%; - position: absolute; - top: 0; - left: 0; - overflow: hidden; -} - -#add-options-content { - display: flex; - opacity: 1; - margin: 0; - padding: 0; - position: relative; - float: right; - .mainView-docBtn { - position:relative; - margin-right: 10px; - width: 35px; - height: 35px; - } - .collectionFreeFormDocumentView-container { - position: relative; - } -} - -ul#add-options-list { - list-style: none; - padding: 5 0 0 0; - - >li { - display: inline-block; - padding: 0; - } -} -.mainView-logout { - position: absolute; - right: 0; - bottom: 0; - font-size: 8px; -} - -.mainView-libraryFlyout { - height: 100%; - position: absolute; - display: flex; - flex-direction: column; -} - -.expandFlyoutButton { - position: absolute; - top: 30px; - right: 30px; - cursor: pointer; -} - -.mainView-libraryHandle { - width: 20px; - height: 40px; - top: 50%; - border: 1px solid black; - border-radius: 5px; - position: absolute; - z-index: 1; -} - .svg-inline--fa { vertical-align: unset; -} - -.mainView-workspace { - height: 200px; - position: relative; - display: flex; -} - -.mainView-library { - height: 75%; - position: relative; - display: flex; -} - -.mainView-recentlyClosed { - height: 25%; - position: relative; - display: flex; }
\ No newline at end of file diff --git a/src/client/views/MainView.scss b/src/client/views/MainView.scss new file mode 100644 index 000000000..e61494e71 --- /dev/null +++ b/src/client/views/MainView.scss @@ -0,0 +1,97 @@ +@import "globalCssVariables"; +@import "nodeModuleOverrides"; + + +.mainView-tabButtons { + position: relative; + width:100%; +} +// add nodes menu. Note that the + button is actually an input label, not an actual button. +.mainView-docButtons { + position: absolute; + bottom: 20px; + left: 250px; +} + +.mainView-container { + width: 100%; + height: 100%; + position: absolute; + top: 0; + left: 0; + overflow: auto; + z-index: 1; +} +.mainView-mainContent { + width:100%; + height:100%; + position:absolute; +} +.mainView-flyoutContainer{ + display:flex; + flex-direction: column; + position: absolute; + width:100%; + height:100%; + border: black 1px solid; + .documentView-node-topmost { + background: lightgrey; + } +} +.mainView-mainDiv { + width: 100%; + height: 100%; + position: absolute; + top: 0; + left: 0; + overflow: hidden; +} + +.mainView-logout { + position: absolute; + right: 0; + bottom: 0; + font-size: 8px; +} + +.mainView-libraryFlyout { + height: 100%; + position: absolute; + display: flex; + flex-direction: column; +} + +.mainView-expandFlyoutButton { + position: absolute; + top: 30px; + right: 30px; + cursor: pointer; +} + +.mainView-libraryHandle { + width: 20px; + height: 40px; + top: 50%; + border: 1px solid black; + border-radius: 5px; + position: absolute; + z-index: 1; +} + +.mainView-workspace { + height: 200px; + position: relative; + display: flex; +} + +.mainView-library { + height: 75%; + position: relative; + display: flex; +} + +.mainView-recentlyClosed { + height: 25%; + position: relative; + display: flex; +}
\ No newline at end of file diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index aac4af9f6..4367785b6 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -8,32 +8,26 @@ import * as React from 'react'; import Measure from 'react-measure'; 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 { ObjectField } from '../../new_fields/ObjectField'; import { listSpec } from '../../new_fields/Schema'; -import { ScriptField } from '../../new_fields/ScriptField'; -import { BoolCast, Cast, FieldValue, NumCast, PromiseValue, 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, returnFalse, returnOne, returnTrue, Utils } from '../../Utils'; import GoogleAuthenticationManager from '../apis/GoogleAuthenticationManager'; import { DocServer } from '../DocServer'; import { Docs, DocumentOptions } from '../documents/Documents'; -import { DictationManager } from '../util/DictationManager'; -import { DragManager } 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 { ContextMenu } from './ContextMenu'; +import { DictationOverlay } from './DictationOverlay'; import { DocumentDecorations } from './DocumentDecorations'; import KeyManager from './GlobalKeyHandler'; -import { InkingControl } from './InkingControl'; -import "./Main.scss"; -import MainViewModal from './MainViewModal'; +import "./MainView.scss"; import { MainViewNotifs } from './MainViewNotifs'; import { DocumentView } from './nodes/DocumentView'; import { OverlayView } from './OverlayView'; @@ -46,59 +40,17 @@ export class MainView extends React.Component { private _buttonBarHeight = 75; private _flyoutSizeOnDown = 0; private _urlState: HistoryUtil.DocUrl; - private _dropDisposer?: DragManager.DragDropDisposer; - - @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 addMenuToggle = React.createRef<HTMLInputElement>(); @observable public flyoutWidth: number = 250; - public hasActiveModal = false; - public isPointerDown = false; - public overlayTimeout: NodeJS.Timeout | undefined; - - private set mainContainer(doc: Opt<Doc>) { - if (doc) { - !("presentationView" in doc) && (doc.presentationView = new List<Doc>([Docs.Create.TreeDocument([], { title: "Presentation" })])); - this.userDoc ? (this.userDoc.activeWorkspace = doc) : (CurrentUserUtils.GuestWorkspace = doc); - } - } - - @computed private get mainContainer() { return this.userDoc ? FieldValue(Cast(this.userDoc.activeWorkspace, Doc)) : CurrentUserUtils.GuestWorkspace; } @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 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); - } - public cancelDictationFade = () => { - if (this.overlayTimeout) { - clearTimeout(this.overlayTimeout); - this.overlayTimeout = undefined; - } - } - - @computed public get dictatedPhrase() { return this._dictationState; } - @computed public get dictationSuccess() { return this._dictationSuccessState; } - @computed public get dictationOverlayVisible() { return this._dictationDisplayState; } - @computed public get isListening() { return this._dictationListeningState; } - - public set dictatedPhrase(value: string) { runInAction(() => this._dictationState = value); } - public set dictationSuccess(value: boolean | undefined) { runInAction(() => this._dictationSuccessState = value); } - public set dictationOverlayVisible(value: boolean) { runInAction(() => this._dictationDisplayState = value); } - public set isListening(value: DictationManager.Controls.ListeningUIStatus) { runInAction(() => this._dictationListeningState = value); } + public isPointerDown = false; componentWillMount() { var tag = document.createElement('script'); @@ -112,10 +64,8 @@ export class MainView extends React.Component { componentWillUnMount() { window.removeEventListener("keydown", KeyManager.Instance.handle); - //close presentation window.removeEventListener("pointerdown", this.globalPointerDown); window.removeEventListener("pointerup", this.globalPointerUp); - this._dropDisposer && this._dropDisposer(); } constructor(props: Readonly<{}>) { @@ -183,7 +133,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 @@ -202,24 +151,16 @@ export class MainView extends React.Component { ); } 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); - } - }); + 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(); @@ -250,17 +191,17 @@ export class MainView extends React.Component { 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; + + 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(); @@ -280,7 +221,7 @@ 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(); @@ -294,19 +235,6 @@ export class MainView extends React.Component { return true; } - drop = action((e: Event, de: DragManager.DropEvent) => { - (de.data as DragManager.DocumentDragData).draggedDocuments.map(doc => { - if (!doc.onDragStart) { - let dbox = Docs.Create.FontIconDocument({ nativeWidth: 100, nativeHeight: 100, width: 100, height: 100, backgroundColor: StrCast(doc.backgroundColor), title: "Custom", icon: "bolt" }); - dbox.dragFactory = doc; - dbox.removeDropProperties = doc.removeDropProperties instanceof ObjectField ? ObjectField.MakeCopy(doc.removeDropProperties) : undefined; - dbox.onDragStart = ScriptField.MakeFunction('getCopy(this.dragFactory, true)'); - doc = dbox; - } - Doc.AddDocToList(CurrentUserUtils.UserDocument, "docButtons", doc); - }); - }); - onDrop = (e: React.DragEvent<HTMLDivElement>) => { e.preventDefault(); e.stopPropagation(); @@ -323,14 +251,12 @@ export class MainView extends React.Component { getContentsHeight = () => this._panelHeight - this._buttonBarHeight; @computed get dockingContent() { + const mainContainer = this.mainContainer; return <Measure offset onResize={this.onResize}> {({ measureRef }) => - <div ref={measureRef} id="mainContent-div" style={{ - width: `calc(100% - ${this._flyoutTranslate || 0}px`, - transform: `translate(${this._flyoutTranslate || 0}px, 0px)` - }} onDrop={this.onDrop}> - {!this.mainContainer ? (null) : - <DocumentView Document={this.mainContainer} + <div ref={measureRef} className="mainView-mainDiv" onDrop={this.onDrop}> + {!mainContainer ? (null) : + <DocumentView Document={mainContainer} DataDoc={undefined} addDocument={undefined} addDocTab={this.addDocTabFunc} @@ -391,8 +317,7 @@ export class MainView extends React.Component { @action onPointerUp = (e: PointerEvent) => { if (Math.abs(e.clientX - this._flyoutSizeOnDown) < 4) { - if (this.flyoutWidth < 5) this.flyoutWidth = 250; - else this.flyoutWidth = 0; + this.flyoutWidth = this.flyoutWidth < 5 ? 250 : 0; } document.removeEventListener("pointermove", this.onPointerMove); document.removeEventListener("pointerup", this.onPointerUp); @@ -418,7 +343,7 @@ export class MainView extends React.Component { let libraryButtonDoc = Cast(CurrentUserUtils.UserDocument.libraryButtons, Doc) as Doc; libraryButtonDoc.columnWidth = this.flyoutWidth / 3 - 30; return <div className="mainView-flyoutContainer"> - <div style={{ position: "relative", height: `${this._buttonBarHeight}px`, width: "100%" }}> + <div className="mainView-tabButtons" style={{ height: `${this._buttonBarHeight}px` }}> <DocumentView Document={libraryButtonDoc} DataDoc={undefined} @@ -479,8 +404,8 @@ export class MainView extends React.Component { get mainContent() { const sidebar = this.userDoc && this.userDoc.sidebarContainer; return !this.userDoc || !(sidebar instanceof Doc) ? (null) : ( - <div className="mainContent" > - <div onPointerLeave={this.pointerLeaveDragger}> + <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")}` }} onPointerDown={this.onPointerDown} onPointerOver={this.pointerOverDragger}> @@ -508,7 +433,7 @@ 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; @@ -516,126 +441,25 @@ export class MainView extends React.Component { }}><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%" }; - } - - 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); - } - - protected createDropTarget = (ele: HTMLLabelElement) => { //used for stacking and masonry view - this._dropDisposer && this._dropDisposer(); - if (ele) { - this._dropDisposer = DragManager.MakeDropTarget(ele, { handlers: { drop: this.drop.bind(this) } }); - } - } - - /* 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 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] }); - - return < div id="add-nodes-menu" style={{ left: (this._flyoutTranslate ? this.flyoutWidth : 0) + 20, bottom: 20 }} > - + @computed get docButtons() { + return <div className="mainView-docButtons" style={{ left: (this._flyoutTranslate ? this.flyoutWidth : 0) + 20 }} > <MainViewNotifs /> - <input type="checkbox" id="add-menu-toggle" ref={this.addMenuToggle} /> - <label htmlFor="add-menu-toggle" ref={this.createDropTarget} title="Close Menu"><p>+</p></label> - - <div id="add-options-content"> - <button key="undo" 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> - <button key="redo" 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> - - {DocListCast(CurrentUserUtils.UserDocument.docButtons).map(doc => <div className="mainView-docBtn" key={StrCast(doc.title)} > - <DocumentView - Document={doc} - DataDoc={undefined} - addDocument={undefined} - addDocTab={this.addDocTabFunc} - pinToPres={emptyFunction} - removeDocument={(doc: Doc) => Doc.RemoveDocFromList(CurrentUserUtils.UserDocument, "docButtons", doc)} - ruleProvider={undefined} - onClick={undefined} - ScreenToLocalTransform={Transform.Identity} - ContentScaling={() => 35 / NumCast(doc.nativeWidth, 35)} - PanelWidth={() => 35} - PanelHeight={() => 35} - renderDepth={0} - focus={emptyFunction} - backgroundColor={returnEmptyString} - parentActive={returnTrue} - whenActiveChanged={emptyFunction} - bringToFront={emptyFunction} - ContainingCollectionView={undefined} - ContainingCollectionDoc={undefined} - zoomToScale={emptyFunction} - getScale={returnOne}> - </DocumentView> - </div>)} - {/* <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> */} - - <button className="toolbar-button round-button" title="Ink" onClick={() => InkingControl.Instance.toggleDisplay()}><FontAwesomeIcon icon="pen-nib" size="sm" /> </button> - <button key="pen" onClick={() => InkingControl.Instance.switchTool(InkTool.Pen)} title="Pen" style={this.selected(InkTool.Pen)}><FontAwesomeIcon icon="pen" size="lg" /></button> - <button key="marker" onClick={() => InkingControl.Instance.switchTool(InkTool.Highlighter)} title="Highlighter" style={this.selected(InkTool.Highlighter)}><FontAwesomeIcon icon="highlighter" size="lg" /></button> - <button key="eraser" onClick={() => InkingControl.Instance.switchTool(InkTool.Eraser)} title="Eraser" style={this.selected(InkTool.Eraser)}><FontAwesomeIcon icon="eraser" size="lg" /></button> - <InkingControl key="inkControls" /> - </div> - </div >; - } - - @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} - /> - ); + <CollectionLinearView Document={CurrentUserUtils.UserDocument} fieldKey={"docButtons"} /> + </div>; } render() { - return ( - <div id="main-div"> - {this.dictationOverlay} - <SharingManager /> - <GoogleAuthenticationManager /> - <DocumentDecorations /> - {this.mainContent} - <PreviewCursor /> - <ContextMenu /> - {this.nodesMenu()} - <PDFMenu /> - <OverlayView /> - </div > - ); + return (<div className="mainView-container"> + <DictationOverlay /> + <SharingManager /> + <GoogleAuthenticationManager /> + <DocumentDecorations /> + {this.mainContent} + <PreviewCursor /> + <ContextMenu /> + {this.docButtons} + <PDFMenu /> + <OverlayView /> + </div >); } -} +}
\ No newline at end of file diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 98ae7442f..7334de92c 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -26,7 +26,6 @@ import { ContextMenu } from "../ContextMenu"; import { ContextMenuProps } from '../ContextMenuItem'; import { DocComponent } from "../DocComponent"; import { EditableView } from '../EditableView'; -import { MainView } from '../MainView'; import { OverlayView } from '../OverlayView'; import { ScriptBox } from '../ScriptBox'; import { ScriptingRepl } from '../ScriptingRepl'; @@ -39,6 +38,7 @@ import { GooglePhotos } from '../../apis/google_docs/GooglePhotosClientUtils'; import { ImageField } from '../../../new_fields/URLField'; import SharingManager from '../../util/SharingManager'; import { Scripting } from '../../util/Scripting'; +import { DictationOverlay } from '../DictationOverlay'; library.add(fa.faEdit); library.add(fa.faTrash); @@ -423,10 +423,9 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu Doc.GetProto(this.props.Document).transcript = await DictationManager.Controls.listen({ continuous: { indefinite: true }, interimHandler: (results: string) => { - let main = MainView.Instance; - main.dictationSuccess = true; - main.dictatedPhrase = results; - main.isListening = { interim: true }; + DictationOverlay.Instance.dictationSuccess = true; + DictationOverlay.Instance.dictatedPhrase = results; + DictationOverlay.Instance.isListening = { interim: true }; } }); } diff --git a/src/client/views/nodes/ImageBox.scss b/src/client/views/nodes/ImageBox.scss index 97d858f58..2b81c16c0 100644 --- a/src/client/views/nodes/ImageBox.scss +++ b/src/client/views/nodes/ImageBox.scss @@ -9,6 +9,10 @@ pointer-events: none; } +.imageBox-container { + border-radius: inherit; +} + .imageBox-cont-interactive { pointer-events: all; } |