diff options
Diffstat (limited to 'src/client/views/MainView.tsx')
-rw-r--r-- | src/client/views/MainView.tsx | 161 |
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]} /> |