aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/MainView.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views/MainView.tsx')
-rw-r--r--src/client/views/MainView.tsx161
1 files changed, 98 insertions, 63 deletions
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index f197f5f29..ea48a72b5 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -11,9 +11,10 @@ import * as ReactDOM from 'react-dom';
import { Doc, DocListCast, Opt } from '../../fields/Doc';
import { List } from '../../fields/List';
import { PrefetchProxy } from '../../fields/Proxy';
+import { ScriptField } from '../../fields/ScriptField';
import { BoolCast, PromiseValue, StrCast } from '../../fields/Types';
import { TraceMobx } from '../../fields/util';
-import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, setupMoveUpEvents, simulateMouseClick, Utils } from '../../Utils';
+import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, returnZero, setupMoveUpEvents, simulateMouseClick, Utils } from '../../Utils';
import { GoogleAuthenticationManager } from '../apis/GoogleAuthenticationManager';
import { DocServer } from '../DocServer';
import { Docs, DocUtils } from '../documents/Documents';
@@ -32,15 +33,16 @@ import { Transform } from '../util/Transform';
import { TimelineMenu } from './animationtimeline/TimelineMenu';
import { CollectionDockingView } from './collections/CollectionDockingView';
import { MarqueeOptionsMenu } from './collections/collectionFreeForm/MarqueeOptionsMenu';
-import { CollectionLinearView } from './collections/CollectionLinearView';
+import { CollectionLinearView } from './collections/collectionLinear';
import { CollectionMenu } from './collections/CollectionMenu';
import { CollectionViewType } from './collections/CollectionView';
import "./collections/TreeView.scss";
+import { ComponentDecorations } from './ComponentDecorations';
import { ContextMenu } from './ContextMenu';
import { DictationOverlay } from './DictationOverlay';
import { DocumentDecorations } from './DocumentDecorations';
import { GestureOverlay } from './GestureOverlay';
-import { MENU_PANEL_WIDTH, SEARCH_PANEL_HEIGHT } from './global/globalCssVariables.scss';
+import { DASHBOARD_SELECTOR_HEIGHT, LEFT_MENU_WIDTH } from './global/globalCssVariables.scss';
import { Colors } from './global/globalEnums';
import { KeyManager } from './GlobalKeyHandler';
import { InkStrokeProperties } from './InkStrokeProperties';
@@ -48,9 +50,11 @@ import { LightboxView } from './LightboxView';
import { LinkMenu } from './linking/LinkMenu';
import "./MainView.scss";
import { AudioBox } from './nodes/AudioBox';
+import { ButtonType } from './nodes/button/FontIconBox';
import { DocumentLinksButton } from './nodes/DocumentLinksButton';
import { DocumentView } from './nodes/DocumentView';
import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox';
+import { RichTextMenu } from './nodes/formattedText/RichTextMenu';
import { LinkDescriptionPopup } from './nodes/LinkDescriptionPopup';
import { LinkDocPreview } from './nodes/LinkDocPreview';
import { RadialMenu } from './nodes/RadialMenu';
@@ -72,21 +76,32 @@ export class MainView extends React.Component {
@observable public LastButton: Opt<Doc>;
@observable private _windowWidth: number = 0;
@observable private _windowHeight: number = 0;
- @observable private _panelWidth: number = 0;
- @observable private _panelHeight: number = 0;
+ @observable private _dashUIWidth: number = 0; // width of entire main dashboard region including left menu buttons and properties panel (but not including the dashboard selector button row)
+ @observable private _dashUIHeight: number = 0; // height of entire main dashboard region including top menu buttons
@observable private _panelContent: string = "none";
@observable private _sidebarContent: any = this.userDoc?.sidebar;
- @observable private _flyoutWidth: number = 0;
+ @observable private _leftMenuFlyoutWidth: number = 0;
- @computed private get topOffset() { return Number(SEARCH_PANEL_HEIGHT.replace("px", "")); } //TODO remove
- @computed private get leftOffset() { return this.menuPanelWidth() - 2; }
+ @computed private get dashboardTabHeight() { return 27; } // 27 comes form lm.config.defaultConfig.dimensions.headerHeight in goldenlayout.js
+ @computed private get topOfDashUI() { return Number(DASHBOARD_SELECTOR_HEIGHT.replace("px", "")); }
+ @computed private get topOfMainDoc() { return this.topOfDashUI + this.topMenuHeight(); }
+ @computed private get topOfMainDocContent() { return this.topOfMainDoc + this.dashboardTabHeight; }
+ @computed private get leftScreenOffsetOfMainDocView() { return this.leftMenuWidth() - 2; }
@computed private get userDoc() { return Doc.UserDoc(); }
@computed private get darkScheme() { return BoolCast(CurrentUserUtils.ActiveDashboard?.darkScheme); }
@computed private get mainContainer() { return this.userDoc ? CurrentUserUtils.ActiveDashboard : CurrentUserUtils.GuestDashboard; }
- @computed public get mainFreeform(): Opt<Doc> { return (docs => (docs && docs.length > 1) ? docs[1] : undefined)(DocListCast(this.mainContainer!.data)); }
-
- menuPanelWidth = () => Number(MENU_PANEL_WIDTH.replace("px", ""));
- propertiesWidth = () => Math.max(0, Math.min(this._panelWidth - 50, CurrentUserUtils.propertiesWidth || 0));
+ @computed public get mainFreeform(): Opt<Doc> { return (docs => (docs?.length > 1) ? docs[1] : undefined)(DocListCast(this.mainContainer!.data)); }
+
+ topMenuHeight = () => 35;
+ topMenuWidth = returnZero; // value is ignored ...
+ leftMenuWidth = () => Number(LEFT_MENU_WIDTH.replace("px", ""));
+ leftMenuHeight = () => this._dashUIHeight;
+ leftMenuFlyoutWidth = () => this._leftMenuFlyoutWidth;
+ leftMenuFlyoutHeight = () => this._dashUIHeight;
+ propertiesWidth = () => Math.max(0, Math.min(this._dashUIWidth - 50, CurrentUserUtils.propertiesWidth || 0));
+ propertiesHeight = () => this._dashUIHeight;
+ mainDocViewWidth = () => this._dashUIWidth - this.propertiesWidth() - this.leftMenuWidth();
+ mainDocViewHeight = () => this._dashUIHeight - this.topMenuHeight();
componentDidMount() {
document.getElementById("root")?.addEventListener("scroll", e => ((ele) => ele.scrollLeft = ele.scrollTop = 0)(document.getElementById("root")!));
@@ -172,7 +187,7 @@ export class MainView extends React.Component {
fa.faArrowAltCircleDown, fa.faArrowAltCircleUp, fa.faArrowAltCircleLeft, fa.faArrowAltCircleRight, fa.faStopCircle, fa.faCheckCircle, fa.faGripVertical,
fa.faSortUp, fa.faSortDown, fa.faTable, fa.faTh, fa.faThList, fa.faProjectDiagram, fa.faSignature, fa.faColumns, fa.faChevronCircleUp, fa.faUpload, fa.faBorderAll,
fa.faBraille, fa.faChalkboard, fa.faPencilAlt, fa.faEyeSlash, fa.faSmile, fa.faIndent, fa.faOutdent, fa.faChartBar, fa.faBan, fa.faPhoneSlash, fa.faGripLines,
- fa.faSave, fa.faBookmark, fa.faMapMarkedAlt);
+ fa.faSave, fa.faBookmark, fa.faList, fa.faListOl, fa.faFolderPlus, fa.faLightbulb, fa.faBookOpen, fa.faMapMarkedAlt);
this.initAuthenticationRouters();
}
@@ -226,22 +241,39 @@ export class MainView extends React.Component {
@action
createNewPresentation = async () => {
- if (!await this.userDoc.myPresentations) {
- this.userDoc.myPresentations = new PrefetchProxy(Docs.Create.TreeDocument([], {
- title: "PRESENTATION TRAILS", childDontRegisterViews: true, _height: 100, _forceActive: true, boxShadow: "0 0", _lockedPosition: true, treeViewOpen: true, system: true
+ if (!await this.userDoc.myTrails) {
+ this.userDoc.myTrails = new PrefetchProxy(Docs.Create.TreeDocument([], {
+ title: "TRAILS", childDontRegisterViews: true, _height: 100, _forceActive: true, boxShadow: "0 0", _lockedPosition: true, treeViewOpen: true, system: true
}));
}
const pres = Docs.Create.PresDocument(new List<Doc>(),
- { title: "Untitled Presentation", _viewType: CollectionViewType.Stacking, _width: 400, _height: 500, targetDropAction: "alias", _chromeHidden: true, boxShadow: "0 0" });
+ { title: "Untitled Trail", _viewType: CollectionViewType.Stacking, _fitWidth: true, _width: 400, _height: 500, targetDropAction: "alias", _chromeHidden: true, boxShadow: "0 0" });
CollectionDockingView.AddSplit(pres, "right");
this.userDoc.activePresentation = pres;
- Doc.AddDocToList(this.userDoc.myPresentations as Doc, "data", pres);
+ Doc.AddDocToList(this.userDoc.myTrails as Doc, "data", pres);
}
- getPWidth = () => this._panelWidth - this.propertiesWidth();
- getPHeight = () => this._panelHeight - (CollectionMenu.Instance?.Pinned ? 35 : 0);
- getContentsHeight = () => this._panelHeight;
- getMenuPanelHeight = () => this._panelHeight + (CollectionMenu.Instance?.Pinned ? 35 : 0);
+ @action
+ createNewFolder = async () => {
+ if (!await this.userDoc.myFilesystem) {
+ this.userDoc.myFileOrphans = Docs.Create.TreeDocument([], { title: "Unfiled", _stayInCollection: true, system: true, isFolder: true });
+ const newFolder = ScriptField.MakeFunction(`createNewFolder()`, { scriptContext: "any" })!;
+ const newFolderButton: Doc = Docs.Create.FontIconDocument({
+ onClick: newFolder, _forceActive: true, toolTip: "New folder", _stayInCollection: true, _hideContextMenu: true, title: "New folder",
+ btnType: ButtonType.ClickButton, _width: 30, _height: 30, buttonText: "New folder", icon: "folder-plus", system: true
+ });
+ this.userDoc.myFilesystem = new PrefetchProxy(Docs.Create.TreeDocument([this.userDoc.myFileOrphans as Doc], {
+ title: "My Documents", _showTitle: "title", buttonMenu: true, buttonMenuDoc: newFolderButton, _height: 100,
+ treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, _forceActive: true, childDropAction: "alias",
+ treeViewTruncateTitleWidth: 150, ignoreClick: true,
+ isFolder: true, treeViewType: "fileSystem", childHideLinkButton: true,
+ _lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "proto", system: true,
+ explainer: "This is your file manager where you can create folders to keep track of documents independently of your dashboard."
+ }));
+ }
+ const folder = Docs.Create.TreeDocument([], { title: "Untitled folder", _stayInCollection: true, isFolder: true });
+ Doc.AddDocToList(this.userDoc.myFilesystem as Doc, "data", folder);
+ }
@computed get mainDocView() {
return <DocumentView key="main"
@@ -257,8 +289,8 @@ export class MainView extends React.Component {
isContentActive={returnTrue}
removeDocument={undefined}
ScreenToLocalTransform={Transform.Identity}
- PanelWidth={this.getPWidth}
- PanelHeight={this.getPHeight}
+ PanelWidth={this.mainDocViewWidth}
+ PanelHeight={this.mainDocViewHeight}
focus={DocUtils.DefaultFocus}
whenChildContentsActiveChanged={emptyFunction}
bringToFront={emptyFunction}
@@ -272,13 +304,10 @@ export class MainView extends React.Component {
}
@computed get dockingContent() {
- return <div key="docking" className={`mainContent-div${this._flyoutWidth ? "-flyout" : ""}`} onDrop={e => { e.stopPropagation(); e.preventDefault(); }}
- // style={{ minWidth: `calc(100% - ${this._flyoutWidth + this.menuPanelWidth() + this.propertiesWidth()}px)`, width: `calc(100% - ${this._flyoutWidth + this.propertiesWidth()}px)` }}>
- // FIXME update with property panel width
+ return <div key="docking" className={`mainView-dockingContent${this._leftMenuFlyoutWidth ? "-flyout" : ""}`} onDrop={e => { e.stopPropagation(); e.preventDefault(); }}
style={{
- minWidth: `calc(100% - ${this._flyoutWidth + this.menuPanelWidth() + this.propertiesWidth()}px)`,
+ minWidth: `calc(100% - ${this._leftMenuFlyoutWidth + this.leftMenuWidth() + this.propertiesWidth()}px)`,
transform: LightboxView.LightboxDoc ? "scale(0.0001)" : undefined,
- //TODO:glr width: `calc(100% - ${this._flyoutWidth + this.menuPanelWidth() + this.propertiesWidth()}px)`
}}>
{!this.mainContainer ? (null) : this.mainDocView}
</div>;
@@ -287,22 +316,21 @@ export class MainView extends React.Component {
@action
onPropertiesPointerDown = (e: React.PointerEvent) => {
setupMoveUpEvents(this, e,
- action(e => (CurrentUserUtils.propertiesWidth = Math.max(0, this._panelWidth - e.clientX)) ? false : false),
+ action(e => (CurrentUserUtils.propertiesWidth = Math.max(0, this._dashUIWidth - e.clientX)) ? false : false),
action(() => CurrentUserUtils.propertiesWidth < 5 && (CurrentUserUtils.propertiesWidth = 0)),
- action(() => CurrentUserUtils.propertiesWidth = this.propertiesWidth() < 15 ? Math.min(this._panelWidth - 50, 250) : 0), false);
+ action(() => CurrentUserUtils.propertiesWidth = this.propertiesWidth() < 15 ? Math.min(this._dashUIWidth - 50, 250) : 0), false);
}
@action
onFlyoutPointerDown = (e: React.PointerEvent) => {
setupMoveUpEvents(this, e,
- action(e => (this._flyoutWidth = Math.max(e.clientX - 58, 0)) ? false : false),
- () => this._flyoutWidth < 5 && this.closeFlyout(),
+ action(e => (this._leftMenuFlyoutWidth = Math.max(e.clientX - 58, 0)) ? false : false),
+ () => this._leftMenuFlyoutWidth < 5 && this.closeFlyout(),
this.closeFlyout);
}
- flyoutWidthFunc = () => this._flyoutWidth;
- sidebarScreenToLocal = () => new Transform(0, -this.topOffset, 1);
- mainContainerXf = () => this.sidebarScreenToLocal().translate(-this.leftOffset, 0);
+ sidebarScreenToLocal = () => new Transform(0, -this.topOfMainDoc, 1);
+ mainContainerXf = () => this.sidebarScreenToLocal().translate(-this.leftScreenOffsetOfMainDocView, 0);
addDocTabFunc = (doc: Doc, where: string): boolean => {
return where === "close" ? CollectionDockingView.CloseSplit(doc) :
doc.dockingConfig ? CurrentUserUtils.openDashboard(Doc.UserDoc(), doc) : CollectionDockingView.AddSplit(doc, "right");
@@ -310,10 +338,10 @@ export class MainView extends React.Component {
@computed get flyout() {
- return !this._flyoutWidth ? <div key="flyout" className={`mainView-libraryFlyout-out`}>
+ return !this._leftMenuFlyoutWidth ? <div key="flyout" className={`mainView-libraryFlyout-out`}>
{this.docButtons}
</div> :
- <div key="libFlyout" className="mainView-libraryFlyout" style={{ minWidth: this._flyoutWidth, width: this._flyoutWidth }} >
+ <div key="libFlyout" className="mainView-libraryFlyout" style={{ minWidth: this._leftMenuFlyoutWidth, width: this._leftMenuFlyoutWidth }} >
<div className="mainView-contentArea" >
<DocumentView
Document={this._sidebarContent.proto || this._sidebarContent}
@@ -327,11 +355,11 @@ export class MainView extends React.Component {
rootSelected={returnTrue}
removeDocument={returnFalse}
ScreenToLocalTransform={this.mainContainerXf}
- PanelWidth={this.flyoutWidthFunc}
- PanelHeight={this.getContentsHeight}
+ PanelWidth={this.leftMenuFlyoutWidth}
+ PanelHeight={this.leftMenuFlyoutHeight}
renderDepth={0}
isContentActive={returnTrue}
- scriptContext={CollectionDockingView.Instance.props.Document}
+ scriptContext={CollectionDockingView.Instance?.props.Document}
focus={DocUtils.DefaultFocus}
whenChildContentsActiveChanged={emptyFunction}
bringToFront={emptyFunction}
@@ -346,8 +374,8 @@ export class MainView extends React.Component {
</div>;
}
- @computed get menuPanel() {
- return <div key="menu" className="mainView-menuPanel">
+ @computed get leftMenuPanel() {
+ return <div key="menu" className="mainView-leftMenuPanel">
<DocumentView
Document={Doc.UserDoc().menuStack as Doc}
DataDoc={undefined}
@@ -357,8 +385,8 @@ export class MainView extends React.Component {
rootSelected={returnTrue}
removeDocument={returnFalse}
ScreenToLocalTransform={this.sidebarScreenToLocal}
- PanelWidth={this.menuPanelWidth}
- PanelHeight={this.getMenuPanelHeight}
+ PanelWidth={this.leftMenuWidth}
+ PanelHeight={this.leftMenuHeight}
renderDepth={0}
docViewPath={returnEmptyDoclist}
focus={DocUtils.DefaultFocus}
@@ -380,7 +408,7 @@ export class MainView extends React.Component {
@action
selectMenu = (button: Doc) => {
const title = StrCast(Doc.GetProto(button).title);
- const willOpen = !this._flyoutWidth || this._panelContent !== title;
+ const willOpen = !this._leftMenuFlyoutWidth || this._panelContent !== title;
this.closeFlyout();
if (willOpen) {
switch (this._panelContent = title) {
@@ -397,38 +425,41 @@ export class MainView extends React.Component {
}
@computed get mainInnerContent() {
- const width = this.propertiesWidth() + this._flyoutWidth + this.menuPanelWidth();
- const transform = this._flyoutWidth ? 'translate(-28px, 0px)' : undefined;
+ const width = this.propertiesWidth() + this._leftMenuFlyoutWidth + this.leftMenuWidth();
+ const transform = this._leftMenuFlyoutWidth ? 'translate(-28px, 0px)' : undefined;
return <>
- {this.menuPanel}
+ {this.leftMenuPanel}
<div key="inner" className={`mainView-innerContent${this.darkScheme ? "-dark" : ""}`}>
{this.flyout}
- <div className="mainView-libraryHandle" style={{ display: !this._flyoutWidth ? "none" : undefined }} onPointerDown={this.onFlyoutPointerDown} >
+ <div className="mainView-libraryHandle" style={{ display: !this._leftMenuFlyoutWidth ? "none" : undefined }} onPointerDown={this.onFlyoutPointerDown} >
<FontAwesomeIcon icon="chevron-left" color={this.darkScheme ? "white" : "black"} style={{ opacity: "50%" }} size="sm" />
</div>
<div className="mainView-innerContainer" style={{ width: `calc(100% - ${width}px)`, transform: transform }}>
- <CollectionMenu />
+ <CollectionMenu panelWidth={this.topMenuWidth} panelHeight={this.topMenuHeight} />
{this.dockingContent}
- <div className="mainView-propertiesDragger" key="props" onPointerDown={this.onPropertiesPointerDown} style={{ right: this._flyoutWidth ? 0 : this.propertiesWidth() - 1 }}>
+ <div className="mainView-propertiesDragger" key="props" onPointerDown={this.onPropertiesPointerDown} style={{ right: this._leftMenuFlyoutWidth ? 0 : this.propertiesWidth() - 1 }}>
<FontAwesomeIcon icon={this.propertiesWidth() < 10 ? "chevron-left" : "chevron-right"} color={this.darkScheme ? Colors.WHITE : Colors.BLACK} size="sm" />
</div>
<div className="properties-container">
- {this.propertiesWidth() < 10 ? (null) : <PropertiesView styleProvider={DefaultStyleProvider} width={this.propertiesWidth()} height={this.getContentsHeight()} />}
+ {this.propertiesWidth() < 10 ? (null) : <PropertiesView styleProvider={DefaultStyleProvider} width={this.propertiesWidth()} height={this.propertiesHeight()} />}
</div>
</div>
</div>
</>;
}
- @computed get mainContent() {
+ @computed get mainDashboardArea() {
return !this.userDoc ? (null) :
- <div className="mainView-mainContent" ref={r => {
- r && new _global.ResizeObserver(action(() => { this._panelWidth = r.getBoundingClientRect().width; this._panelHeight = r.getBoundingClientRect().height; })).observe(r);
+ <div className="mainView-dashboardArea" ref={r => {
+ r && new _global.ResizeObserver(action(() => {
+ this._dashUIWidth = r.getBoundingClientRect().width;
+ this._dashUIHeight = r.getBoundingClientRect().height;
+ })).observe(r);
}} style={{
color: this.darkScheme ? "rgb(205,205,205)" : "black",
- height: `calc(100% - ${this.topOffset}px)`,
+ height: `calc(100% - ${this.topOfDashUI}px)`,
width: "100%",
}} >
{this.mainInnerContent}
@@ -436,16 +467,17 @@ export class MainView extends React.Component {
}
expandFlyout = action((button: Doc) => {
- this._flyoutWidth = (this._flyoutWidth || 250);
+ this._leftMenuFlyoutWidth = (this._leftMenuFlyoutWidth || 250);
this._sidebarContent.proto = button.target as any;
this.LastButton = button;
+ console.log(button.title);
});
closeFlyout = action(() => {
this.LastButton = undefined;
this._panelContent = "none";
this._sidebarContent.proto = undefined;
- this._flyoutWidth = 0;
+ this._leftMenuFlyoutWidth = 0;
});
remButtonDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((flg: boolean, doc) => flg && Doc.RemoveDocFromList(Doc.UserDoc().dockedBtns as Doc, "data", doc), true);
@@ -472,6 +504,7 @@ export class MainView extends React.Component {
rootSelected={returnTrue}
bringToFront={emptyFunction}
select={emptyFunction}
+ isAnyChildContentActive={returnFalse}
isContentActive={returnFalse}
isSelected={returnFalse}
docViewPath={returnEmptyDoclist}
@@ -482,8 +515,8 @@ export class MainView extends React.Component {
pinToPres={emptyFunction}
removeDocument={this.remButtonDoc}
ScreenToLocalTransform={this.buttonBarXf}
- PanelWidth={this.flyoutWidthFunc}
- PanelHeight={this.getContentsHeight}
+ PanelWidth={this.leftMenuFlyoutWidth}
+ PanelHeight={this.leftMenuFlyoutHeight}
renderDepth={0}
focus={DocUtils.DefaultFocus}
whenChildContentsActiveChanged={emptyFunction}
@@ -579,13 +612,14 @@ export class MainView extends React.Component {
<CaptureManager />
<GroupManager />
<GoogleAuthenticationManager />
- <DocumentDecorations boundsLeft={this.leftOffset} boundsTop={this.topOffset} />
+ <DocumentDecorations boundsLeft={this.leftScreenOffsetOfMainDocView} boundsTop={this.topOfMainDoc} PanelWidth={this._windowWidth} PanelHeight={this._windowHeight} />
+ <ComponentDecorations boundsLeft={this.leftScreenOffsetOfMainDocView} boundsTop={this.topOfMainDocContent} />
{this.topbar}
{LinkDescriptionPopup.descriptionPopup ? <LinkDescriptionPopup /> : null}
{DocumentLinksButton.LinkEditorDocView ? <LinkMenu docView={DocumentLinksButton.LinkEditorDocView} changeFlyout={emptyFunction} /> : (null)}
{LinkDocPreview.LinkInfo ? <LinkDocPreview {...LinkDocPreview.LinkInfo} /> : (null)}
<GestureOverlay >
- {this.mainContent}
+ {this.mainDashboardArea}
</GestureOverlay>
<PreviewCursor />
<TaskCompletionBox />
@@ -595,6 +629,7 @@ export class MainView extends React.Component {
<MarqueeOptionsMenu />
<OverlayView />
<TimelineMenu />
+ <RichTextMenu />
{this.snapLines}
<div className="mainView-webRef" ref={this.makeWebRef} />
<LightboxView PanelWidth={this._windowWidth} PanelHeight={this._windowHeight} maxBorder={[200, 50]} />