aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/client/util/DictationManager.ts21
-rw-r--r--src/client/util/SharingManager.tsx5
-rw-r--r--src/client/views/CollectionLinearView.scss74
-rw-r--r--src/client/views/CollectionLinearView.tsx106
-rw-r--r--src/client/views/DictationOverlay.tsx71
-rw-r--r--src/client/views/GlobalKeyHandler.ts8
-rw-r--r--src/client/views/InkingControl.tsx5
-rw-r--r--src/client/views/Main.scss219
-rw-r--r--src/client/views/MainView.scss97
-rw-r--r--src/client/views/MainView.tsx268
-rw-r--r--src/client/views/nodes/DocumentView.tsx9
-rw-r--r--src/client/views/nodes/ImageBox.scss4
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;
}