/* eslint-disable node/no-unpublished-import */ import { library } from '@fortawesome/fontawesome-svg-core'; import { faBuffer, faHireAHelper } from '@fortawesome/free-brands-svg-icons'; import * as far from '@fortawesome/free-regular-svg-icons'; import * as fa from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { action, computed, configure, makeObservable, observable, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; // eslint-disable-next-line import/no-relative-packages import '../../../node_modules/browndash-components/dist/styles/global.min.css'; import { ClientUtils, lightOrDark, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, returnZero, setupMoveUpEvents } from '../../ClientUtils'; import { emptyFunction } from '../../Utils'; import { Doc, DocListCast, Opt } from '../../fields/Doc'; import { DocData } from '../../fields/DocSymbols'; import { DocCast, StrCast } from '../../fields/Types'; import { DocServer } from '../DocServer'; import { GoogleAuthenticationManager } from '../apis/GoogleAuthenticationManager'; import { CollectionViewType, DocumentType } from '../documents/DocumentTypes'; import { Docs } from '../documents/Documents'; import { CalendarManager } from '../util/CalendarManager'; import { CaptureManager } from '../util/CaptureManager'; import { DocumentManager } from '../util/DocumentManager'; import { DragManager } from '../util/DragManager'; import { dropActionType } from '../util/DropActionTypes'; import { GroupManager } from '../util/GroupManager'; import { HistoryUtil } from '../util/History'; import { Hypothesis } from '../util/HypothesisUtils'; import { UPDATE_SERVER_CACHE } from '../util/LinkManager'; import { RTFMarkup } from '../util/RTFMarkup'; import { ScriptingGlobals } from '../util/ScriptingGlobals'; import { SelectionManager } from '../util/SelectionManager'; import { ServerStats } from '../util/ServerStats'; import { SettingsManager } from '../util/SettingsManager'; import { SharingManager } from '../util/SharingManager'; import { SnappingManager } from '../util/SnappingManager'; import { Transform } from '../util/Transform'; import { ReportManager } from '../util/reportManager/ReportManager'; import { ComponentDecorations } from './ComponentDecorations'; import { ContextMenu } from './ContextMenu'; import { DashboardView } from './DashboardView'; import { DictationOverlay } from './DictationOverlay'; import { DocumentDecorations } from './DocumentDecorations'; import { GestureOverlay } from './GestureOverlay'; import { KeyManager } from './GlobalKeyHandler'; import { LightboxView } from './LightboxView'; import './MainView.scss'; import { ObservableReactComponent } from './ObservableReactComponent'; import { OverlayView } from './OverlayView'; import { PreviewCursor } from './PreviewCursor'; import { PropertiesView } from './PropertiesView'; import { DashboardStyleProvider, DefaultStyleProvider } from './StyleProvider'; import { TimelineMenu } from './animationtimeline/TimelineMenu'; import { CollectionDockingView } from './collections/CollectionDockingView'; import { CollectionMenu } from './collections/CollectionMenu'; import { TabDocView } from './collections/TabDocView'; import './collections/TreeView.scss'; import { MarqueeOptionsMenu } from './collections/collectionFreeForm/MarqueeOptionsMenu'; import { CollectionLinearView } from './collections/collectionLinear'; import { LinkMenu } from './linking/LinkMenu'; import { AudioBox } from './nodes/AudioBox'; import { SchemaCSVPopUp } from './nodes/DataVizBox/SchemaCSVPopUp'; import { DocButtonState } from './nodes/DocumentLinksButton'; import { DocumentView, DocumentViewInternal, OpenWhere, OpenWhereMod, returnEmptyDocViewList } from './nodes/DocumentView'; import { ImageEditorData as ImageEditor } from './nodes/ImageBox'; import { LinkDescriptionPopup } from './nodes/LinkDescriptionPopup'; import { LinkDocPreview, LinkInfo } from './nodes/LinkDocPreview'; import { DirectionsAnchorMenu } from './nodes/MapBox/DirectionsAnchorMenu'; import { MapAnchorMenu } from './nodes/MapBox/MapAnchorMenu'; import { TaskCompletionBox } from './nodes/TaskCompletedBox'; import { DashFieldViewMenu } from './nodes/formattedText/DashFieldView'; import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox'; import { RichTextMenu } from './nodes/formattedText/RichTextMenu'; import GenerativeFill from './nodes/generativeFill/GenerativeFill'; import { PresBox } from './nodes/trails'; import { AnchorMenu } from './pdf/AnchorMenu'; import { GPTPopup } from './pdf/GPTPopup/GPTPopup'; import { TopBar } from './topbar/TopBar'; const _global = (window /* browser */ || global) /* node */ as any; const { LEFT_MENU_WIDTH, TOPBAR_HEIGHT } = require('./global/globalCssVariables.module.scss'); // prettier-ignore @observer export class MainView extends ObservableReactComponent<{}> { // eslint-disable-next-line no-use-before-define public static Instance: MainView; public static Live: boolean = false; private _docBtnRef = React.createRef(); @observable private _windowWidth: number = 0; @observable private _windowHeight: 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 = Doc.MyLeftSidebarPanel; @observable private _leftMenuFlyoutWidth: number = 0; @computed get _hideUI() { return this.mainDoc && this.mainDoc._type_collection !== CollectionViewType.Docking; } @computed private get dashboardTabHeight() { return this._hideUI ? 0 : 27; } // 27 comes form lm.config.defaultConfig.dimensions.headerHeight in goldenlayout.js @computed private get topOfDashUI() { return this._hideUI || LightboxView.LightboxDoc ? 0 : Number(TOPBAR_HEIGHT.replace('px', '')); } @computed private get topOfHeaderBarDoc() { return this.topOfDashUI; } @computed private get topOfSidebarDoc() { return this.topOfDashUI + this.topMenuHeight(); } @computed private get topOfMainDoc() { return this.topOfDashUI + this.topMenuHeight() + this.headerBarDocHeight(); } @computed private get topOfMainDocContent() { return this.topOfMainDoc + this.dashboardTabHeight; } @computed private get leftScreenOffsetOfMainDocView() { return this.leftMenuWidth() - 2; } @computed private get userDoc() { return Doc.UserDoc(); } @observable mainDoc: Opt = undefined; @computed private get mainContainer() { if (window.location.pathname.startsWith('/doc/') && ClientUtils.CurrentUserEmail() === 'guest') { DocServer.GetRefField(window.location.pathname.substring('/doc/'.length)).then(main => runInAction(() => { this.mainDoc = main as Doc; }) ); return this.mainDoc; } return this.userDoc ? Doc.ActiveDashboard : Doc.GuestDashboard; } @computed private get headerBarDoc() { return Doc.MyHeaderBar; } @computed public get mainFreeform(): Opt { return (docs => (docs?.length > 1 ? docs[1] : undefined))(DocListCast(this.mainContainer!.data)); } @observable public headerBarHeight: number = 0; headerBarHeightFunc = () => this.headerBarHeight; @action toggleTopBar = () => { if (this.headerBarHeight > 0) { this.headerBarHeight = 0; } else { this.headerBarHeight = 60; } }; headerBarDocWidth = () => this.mainDocViewWidth(); headerBarDocHeight = () => (this._hideUI ? 0 : this.headerBarHeight ?? 0); topMenuHeight = () => (this._hideUI ? 0 : 35); topMenuWidth = returnZero; // value is ignored ... leftMenuWidth = () => (this._hideUI ? 0 : Number(LEFT_MENU_WIDTH.replace('px', ''))); leftMenuHeight = () => this._dashUIHeight; leftMenuFlyoutWidth = () => this._leftMenuFlyoutWidth; leftMenuFlyoutHeight = () => this._dashUIHeight; propertiesWidth = () => Math.max(0, Math.min(this._dashUIWidth - 50, SettingsManager.Instance?.propertiesWidth || 0)); propertiesHeight = () => this._dashUIHeight; mainDocViewWidth = () => this._dashUIWidth - this.propertiesWidth() - this.leftMenuWidth() - this.leftMenuFlyoutWidth(); mainDocViewHeight = () => this._dashUIHeight - this.headerBarDocHeight(); componentDidMount() { // Utils.TraceConsoleLog(); reaction( // when a multi-selection occurs, remove focus from all active elements to allow keyboad input to go only to global key manager to act upon selection () => SelectionManager.Views.slice(), views => views.length > 1 && (document.activeElement as any)?.blur !== undefined && (document.activeElement as any)!.blur() ); const scriptTag = document.createElement('script'); scriptTag.setAttribute('type', 'text/javascript'); scriptTag.setAttribute('src', 'https://www.bing.com/api/maps/mapcontrol?callback=makeMap'); scriptTag.async = true; scriptTag.defer = true; document.body.appendChild(scriptTag); document.getElementById('root')?.addEventListener('scroll', () => (ele => { ele.scrollLeft = ele.scrollTop = 0; })(document.getElementById('root')!) ); const ele = document.getElementById('loader'); const prog = document.getElementById('dash-progress'); if (ele && prog) { // remove from DOM setTimeout(() => { prog.style.transition = '1s'; prog.style.width = '100%'; }, 0); setTimeout(() => { ele.outerHTML = ''; }, 1000); } this._sidebarContent.proto = undefined; if (!MainView.Live) { DocServer.setLivePlaygroundFields([ 'dataTransition', 'viewTransition', 'treeView_Open', 'treeView_ExpandedView', 'carousel_index', 'itemIndex', // for changing slides in presentations 'layout_sidebarWidthPercent', 'layout_currentTimecode', 'layout_timelineHeightPercent', 'layout_hideMinimap', 'layout_showSidebar', 'layout_scrollTop', 'layout_fitWidth', 'layout_curPage', 'presStatus', 'freeform_panX', 'freeform_panY', 'freeform_scale', 'overlayX', 'overlayY', 'text_scrollHeight', 'text_height', 'hidden', // 'type_collection', 'chromeHidden', 'currentFrame', ]); // can play with these fields on someone else's } const tag = document.createElement('script'); tag.src = 'https://www.youtube.com/iframe_api'; const firstScriptTag = document.getElementsByTagName('script')[0]; firstScriptTag.parentNode!.insertBefore(tag, firstScriptTag); window.removeEventListener('keydown', KeyManager.Instance.handleModifiers, true); window.addEventListener('keydown', KeyManager.Instance.handleModifiers, true); window.removeEventListener('keyup', KeyManager.Instance.unhandleModifiers); window.addEventListener('keyup', KeyManager.Instance.unhandleModifiers); window.removeEventListener('keydown', KeyManager.Instance.handle); window.addEventListener('keydown', KeyManager.Instance.handle); window.removeEventListener('keyup', KeyManager.Instance.unhandle); window.addEventListener('keyup', KeyManager.Instance.unhandle); window.addEventListener('paste', KeyManager.Instance.paste as any); document.addEventListener('dash', (e: any) => { // event used by chrome plugin to tell Dash which document to focus on const id = FormattedTextBox.GetDocFromUrl(e.detail); DocServer.GetRefField(id).then(doc => (doc instanceof Doc ? DocumentManager.Instance.showDocument(doc, { willPan: false }) : null)); }); document.addEventListener('linkAnnotationToDash', Hypothesis.linkListener); this.initEventListeners(); } componentWillUnMount() { window.removeEventListener('keyup', KeyManager.Instance.unhandle); window.removeEventListener('keydown', KeyManager.Instance.handle); window.removeEventListener('pointerdown', this.globalPointerDown, true); window.removeEventListener('pointermove', this.globalPointerMove, true); window.removeEventListener('pointerup', this.globalPointerClick, true); window.removeEventListener('paste', KeyManager.Instance.paste as any); document.removeEventListener('linkAnnotationToDash', Hypothesis.linkListener); } constructor(props: any) { super(props); makeObservable(this); DocumentViewInternal.addDocTabFunc = MainView.addDocTabFunc_impl; MainView.Instance = this; DashboardView._urlState = HistoryUtil.parseUrl(window.location) || ({} as any); // causes errors to be generated when modifying an observable outside of an action configure({ enforceActions: 'observed' }); if (window.location.pathname !== '/home') { const pathname = window.location.pathname.substr(1).split('/'); if (pathname.length > 1 && pathname[0] === 'doc') { DocServer.GetRefField(pathname[1]).then( action(field => { if (field instanceof Doc && field._type_collection !== CollectionViewType.Docking) { Doc.GuestTarget = field; } }) ); } } library.add( ...[ fa.faExclamationCircle, fa.faEdit, fa.faArrowDownShortWide, fa.faTrash, fa.faTrashAlt, fa.faShare, fa.faTaxi, fa.faDownload, fa.faPallet, fa.faExpandArrowsAlt, fa.faAmbulance, fa.faLayerGroup, fa.faExternalLinkAlt, fa.faCalendar, fa.faSquare, far.faSquare as any, fa.faConciergeBell, fa.faWindowRestore, fa.faFolder, fa.faFolderOpen, fa.faFolderPlus, fa.faFolderClosed, fa.faBook, fa.faMapPin, fa.faMapMarker, fa.faFingerprint, fa.faCrosshairs, fa.faDesktop, fa.faUnlock, fa.faLock, fa.faLaptopCode, fa.faMale, fa.faCopy, fa.faHome, fa.faHandPointLeft, fa.faHandPointRight, fa.faCompass, fa.faSnowflake, fa.faStar, fa.faMicrophone, fa.faKeyboard, fa.faQuestion, fa.faTasks, fa.faPalette, fa.faAngleLeft, fa.faAngleRight, fa.faBell, fa.faCamera, fa.faExpand, fa.faCaretDown, fa.faCaretLeft, fa.faCaretRight, fa.faCaretSquareDown, fa.faCaretSquareRight, fa.faArrowsAltH, fa.faPlus, fa.faMinus, fa.faTerminal, fa.faToggleOn, fa.faFile, fa.faLocationArrow, fa.faSearch, fa.faFileDownload, fa.faFileUpload, fa.faStop, fa.faCalculator, fa.faWindowMaximize, fa.faIdCard, fa.faAddressCard, fa.faQuestionCircle, fa.faArrowLeft, fa.faArrowRight, fa.faArrowDown, fa.faArrowUp, fa.faBolt, fa.faBullseye, fa.faTurnUp, fa.faTurnDown, fa.faCaretUp, fa.faCat, fa.faCheck, fa.faChevronRight, fa.faChevronLeft, fa.faChevronDown, fa.faChevronUp, fa.faClone, fa.faCloudUploadAlt, fa.faCommentAlt, fa.faCommentDots, fa.faCompressArrowsAlt, fa.faCut, fa.faEllipsisV, fa.faEraser, fa.faExclamation, fa.faFileAlt, fa.faFileAudio, fa.faFileVideo, fa.faFilePdf, fa.faFilm, fa.faFilter, fa.faFont, fa.faGlobeAmericas, fa.faGlobeAsia, fa.faHighlighter, fa.faLongArrowAltRight, fa.faMousePointer, fa.faMusic, fa.faObjectGroup, fa.faArrowsLeftRight, fa.faPause, fa.faPen, fa.faPenNib, fa.faPhone, fa.faPlay, fa.faPortrait, fa.faRedoAlt, fa.faStamp, fa.faStickyNote, fa.faArrowsAltV, fa.faTimesCircle, fa.faThumbtack, fa.faTree, fa.faTv, fa.faUndoAlt, fa.faVideoSlash, fa.faVideo, fa.faAsterisk, fa.faBrain, fa.faImage, fa.faPaintBrush, fa.faTimes, fa.faFlag, fa.faScroll, fa.faEye, fa.faArrowsAlt, fa.faQuoteLeft, fa.faSortAmountDown, fa.faAlignLeft, fa.faAlignCenter, fa.faAlignRight, fa.faHeading, fa.faRulerCombined, fa.faFillDrip, fa.faLink, fa.faUnlink, fa.faBold, fa.faItalic, fa.faClipboard, fa.faUnderline, fa.faStrikethrough, fa.faSuperscript, fa.faSubscript, fa.faIndent, fa.faEyeDropper, fa.faPaintRoller, fa.faBars, fa.faBrush, fa.faShapes, fa.faEllipsisH, fa.faHandPaper, fa.faMap, fa.faUser, faHireAHelper as any, fa.faTrashRestore, fa.faUsers, fa.faWrench, fa.faCog, fa.faMap, fa.faBellSlash, fa.faExpandAlt, fa.faArchive, fa.faBezierCurve, fa.faCircle, far.faCircle as any, fa.faLongArrowAltRight, fa.faPenFancy, fa.faAngleDoubleRight, fa.faAngleDoubleDown, fa.faAngleDoubleLeft, fa.faAngleDoubleUp, faBuffer as any, fa.faExpand, fa.faUndo, fa.faSlidersH, fa.faAngleUp, fa.faAngleDown, fa.faPlayCircle, fa.faClock, fa.faRoute, fa.faRocket, fa.faExchangeAlt, fa.faHashtag, fa.faAlignJustify, fa.faCheckSquare, fa.faListUl, fa.faWindowMinimize, fa.faWindowRestore, fa.faTextWidth, fa.faTextHeight, fa.faClosedCaptioning, fa.faInfoCircle, fa.faTag, fa.faSyncAlt, fa.faPhotoVideo, fa.faArrowAltCircleDown, fa.faArrowAltCircleUp, fa.faArrowAltCircleLeft, fa.faArrowAltCircleRight, fa.faStopCircle, fa.faCheckCircle, fa.faGripVertical, fa.faSortUp, fa.faSortDown, fa.faTable, fa.faTableCells, fa.faTableColumns, fa.faTh, fa.faThList, fa.faProjectDiagram, fa.faSignature, fa.faColumns, fa.faChevronCircleUp, fa.faUpload, fa.faBorderAll, fa.faBraille, fa.faPersonChalkboard, fa.faChalkboard, fa.faPencilAlt, fa.faEyeSlash, fa.faSmile, fa.faIndent, fa.faOutdent, fa.faChartBar, fa.faBan, fa.faPhoneSlash, fa.faGripLines, fa.faSave, fa.faBook, fa.faBookmark, fa.faList, fa.faListOl, fa.faLightbulb, fa.faBookOpen, fa.faMapMarkerAlt, fa.faSearchPlus, fa.faSolarPanel, fa.faVolumeUp, fa.faVolumeDown, fa.faSquareRootAlt, fa.faVolumeMute, fa.faUserCircle, fa.faHeart, fa.faHeartBroken, fa.faHighlighter, fa.faRemoveFormat, fa.faHandPointUp, fa.faXRay, fa.faZ, fa.faArrowsUpToLine, fa.faArrowsDownToLine, ] ); } private longPressTimer: NodeJS.Timeout | undefined; globalPointerClick = action(() => { this.longPressTimer && clearTimeout(this.longPressTimer); DocumentView.LongPress = false; }); globalPointerMove = action((e: PointerEvent) => { if (e.movementX > 3 || e.movementY > 3) this.longPressTimer && clearTimeout(this.longPressTimer); }); globalPointerDown = action((e: PointerEvent) => { DocumentView.LongPress = false; this.longPressTimer = setTimeout( action(() => { DocumentView.LongPress = true; }), 1000 ); DocumentManager.removeOverlayViews(); Doc.linkFollowUnhighlight(); AudioBox.Enabled = true; const targets = document.elementsFromPoint(e.x, e.y); if (targets.length) { let targClass = targets[0].className.toString(); for (let i = 0; i < targets.length - 1; i++) { if (typeof targets[i].className === 'object') targClass = targets[i + 1].className.toString(); else break; } !targClass.includes('contextMenu') && ContextMenu.Instance.closeMenu(); !['timeline-menu-desc', 'timeline-menu-item', 'timeline-menu-input'].includes(targClass) && TimelineMenu.Instance.closeMenu(); } }); initEventListeners = () => { window.addEventListener('beforeunload', UPDATE_SERVER_CACHE); window.addEventListener('drop', e => e.preventDefault(), false); // prevent default behavior of navigating to a new web page window.addEventListener('dragover', e => e.preventDefault(), false); document.addEventListener('pointerdown', this.globalPointerDown, true); document.addEventListener('pointermove', this.globalPointerMove, true); document.addEventListener('pointerup', this.globalPointerClick, true); document.addEventListener( 'click', (e: MouseEvent) => { if (!e.cancelBubble) { const pathstr = (e as any)?.path?.map((p: any) => p.classList?.toString()).join(); if (pathstr?.includes('libraryFlyout')) { SelectionManager.DeselectAll(); } } }, false ); document.oncontextmenu = () => false; }; @action createNewPresentation = () => { const pres = Doc.MakeCopy(Doc.UserDoc().emptyTrail as Doc, true); CollectionDockingView.AddSplit(pres, OpenWhereMod.right); Doc.MyTrails && Doc.AddDocToList(Doc.MyTrails, 'data', pres); // Doc.MyTrails should be created in createDashboard Doc.ActivePresentation = pres; }; @action openPresentation = (pres: Doc) => { if (pres.type === DocumentType.PRES) { CollectionDockingView.AddSplit(pres, OpenWhereMod.right, undefined, PresBox.PanelName); Doc.MyTrails && (Doc.ActivePresentation = pres); Doc.AddDocToList(Doc.MyTrails, 'data', pres); this.closeFlyout(); } }; @action createNewFolder = async () => { const folder = Docs.Create.TreeDocument([], { title: 'Untitled folder', _dragOnlyWithinContainer: true, isFolder: true }); Doc.AddDocToList(Doc.MyFilesystem, 'data', folder); }; waitForDoubleClick = () => (SnappingManager.ExploreMode ? 'never' : undefined); headerBarScreenXf = () => new Transform(-this.leftScreenOffsetOfMainDocView - this.leftMenuFlyoutWidth(), -this.headerBarDocHeight(), 1); mainScreenToLocalXf = () => new Transform(-this.leftScreenOffsetOfMainDocView - this.leftMenuFlyoutWidth(), -this.topOfMainDocContent, 1); addHeaderDoc = (docs: Doc | Doc[]) => (docs instanceof Doc ? [docs] : docs).reduce((done, doc) => Doc.AddDocToList(this.headerBarDoc, 'data', doc), true); removeHeaderDoc = (docs: Doc | Doc[]) => (docs instanceof Doc ? [docs] : docs).reduce((done, doc) => Doc.RemoveDocFromList(this.headerBarDoc, 'data', doc), true); @computed get headerBarDocView() { return (
); } @computed get mainDocView() { const headerBar = this._hideUI || !this.headerBarDocHeight?.() ? null : this.headerBarDocView; return ( <> {headerBar} ); } @computed get dockingContent() { return (
{ e.stopPropagation(); e.preventDefault(); }} style={{ width: `calc(100% - ${this._leftMenuFlyoutWidth + this.leftMenuWidth() + this.propertiesWidth()}px)`, minWidth: `calc(100% - ${this._leftMenuFlyoutWidth + this.leftMenuWidth() + this.propertiesWidth()}px)`, transform: LightboxView.LightboxDoc ? 'scale(0.0001)' : undefined, }}> {!this.mainContainer ? null : this.mainDocView}
); } @action onPropertiesPointerDown = (e: React.PointerEvent) => { setupMoveUpEvents( this, e, action(() => { SettingsManager.Instance.propertiesWidth = Math.max(0, this._dashUIWidth - e.clientX); return !SettingsManager.Instance.propertiesWidth; }), action(() => { SettingsManager.Instance.propertiesWidth < 5 && (SettingsManager.Instance.propertiesWidth = 0); }), action(() => { SettingsManager.Instance.propertiesWidth = this.propertiesWidth() < 15 ? Math.min(this._dashUIWidth - 50, 250) : 0; }), false ); }; @action onFlyoutPointerDown = (e: React.PointerEvent) => { setupMoveUpEvents( this, e, action(ev => { this._leftMenuFlyoutWidth = Math.max(ev.clientX - 58, 0); return false; }), () => this._leftMenuFlyoutWidth < 5 && this.closeFlyout(), this.closeFlyout ); }; sidebarScreenToLocal = () => new Transform(0, -this.topOfSidebarDoc, 1); mainContainerXf = () => this.sidebarScreenToLocal().translate(-this.leftScreenOffsetOfMainDocView, 0); static addDocTabFunc_impl = (docs: Doc | Doc[], location: OpenWhere): boolean => { const doc = (docs instanceof Doc ? [docs] : docs).lastElement(); const whereFields = location.split(':'); const keyValue = whereFields.includes(OpenWhereMod.keyvalue); const whereMods = whereFields.length > 1 ? (whereFields[1] as OpenWhereMod) : OpenWhereMod.none; const panelName = whereFields.length > 1 ? whereFields.lastElement() : ''; if (doc.dockingConfig && !keyValue) return DashboardView.openDashboard(doc); switch (whereFields[0]) { case OpenWhere.lightbox: return LightboxView.Instance.AddDocTab(doc, location); case OpenWhere.close: return CollectionDockingView.CloseSplit(doc, whereMods); case OpenWhere.toggle: return CollectionDockingView.ToggleSplit(doc, whereMods, undefined, TabDocView.DontSelectOnActivate); // bcz: hack! mark the toggle so that it won't be selected on activation- this is needed so that the backlinks menu can toggle views of targets on and off without selecting them case OpenWhere.replace: return CollectionDockingView.ReplaceTab(doc, whereMods, undefined, panelName); case OpenWhere.add:default:return CollectionDockingView.AddSplit(doc, whereMods, undefined, undefined, keyValue); } // prettier-ignore }; @computed get flyout() { return !this._leftMenuFlyoutWidth ? (
{this.docButtons}
) : (
{this.docButtons}
); } @computed get leftMenuPanel() { return (
); } @action selectMenu = (button: Doc) => { const title = StrCast(button[DocData].title); const willOpen = !this._leftMenuFlyoutWidth || this._panelContent !== title; this.closeFlyout(); if (willOpen) { switch ((this._panelContent = title)) { case 'Settings': SettingsManager.Instance.openMgr(); break; case 'Help': break; default: this.expandFlyout(button); } } return true; }; @computed get mainInnerContent() { const leftMenuFlyoutWidth = this._leftMenuFlyoutWidth + this.leftMenuWidth(); const width = this.propertiesWidth() + leftMenuFlyoutWidth; return ( <> {this._hideUI ? null : this.leftMenuPanel}
{this.flyout}
{this.dockingContent} {this._hideUI ? null : (
)}
); } @computed get mainDashboardArea() { return !this.userDoc ? null : (
{ r && new _global.ResizeObserver( action(() => { this._dashUIWidth = r.getBoundingClientRect().width; this._dashUIHeight = r.getBoundingClientRect().height; }) ).observe(r); }} style={{ color: 'black', height: `calc(100% - ${this.topOfDashUI + this.topMenuHeight()}px)`, width: '100%', }}> {this.mainInnerContent}
); } expandFlyout = action((button: Doc) => { // bcz: What's going on here!? --- may be fixed now, so commenting out ... // Chrome(not firefox) seems to have a bug when the flyout expands and there's a zoomed freeform tab. All of the div below the CollectionFreeFormView's main div // generate the wrong value from getClientRectangle() -- specifically they return an 'x' that is the flyout's width greater than it should be. // interactively adjusting the flyout fixes the problem. So does programmatically changing the value after a timeout to something *fractionally* different (ie, 1.5, not 1);) this._leftMenuFlyoutWidth = this._leftMenuFlyoutWidth || 250; // setTimeout(action(() => (this._leftMenuFlyoutWidth += 0.5))); this._sidebarContent.proto = DocCast(button.target); SettingsManager.Instance.LastPressedBtn = button; }); closeFlyout = action(() => { SettingsManager.Instance.LastPressedBtn = undefined; this._panelContent = 'none'; this._sidebarContent.proto = undefined; this._leftMenuFlyoutWidth = 0; }); remButtonDoc = (docs: Doc | Doc[]) => (docs instanceof Doc ? [docs] : docs).reduce((flg: boolean, doc) => flg && !doc.dragOnlyWithinContainer && Doc.RemoveDocFromList(Doc.MyDockedBtns, 'data', doc), true); moveButtonDoc = (docs: Doc | Doc[], targetCol: Doc | undefined, addDocument: (document: Doc | Doc[]) => boolean) => this.remButtonDoc(docs) && addDocument(docs); addButtonDoc = (docs: Doc | Doc[]) => (docs instanceof Doc ? [docs] : docs).reduce((flg: boolean, doc) => flg && Doc.AddDocToList(Doc.MyDockedBtns, 'data', doc), true); buttonBarXf = () => { if (!this._docBtnRef.current) return Transform.Identity(); const { scale, translateX, translateY } = ClientUtils.GetScreenTransform(this._docBtnRef.current); return new Transform(-translateX, -translateY, 1 / scale); }; @computed get docButtons() { return !Doc.MyDockedBtns ? null : (
{['watching', 'recording'].includes(StrCast(this.userDoc?.presentationMode)) ?
{StrCast(this.userDoc?.presentationMode)}
: null}
); } @computed get snapLines() { const dragged = DragManager.docsBeingDragged.lastElement() ?? SelectionManager.Docs.lastElement(); const dragPar = dragged ? DocumentManager.Instance.getDocumentView(dragged)?.CollectionFreeFormView : undefined; return !dragPar?.layoutDoc.freeform_snapLines ? null : (
{[ ...SnappingManager.HorizSnapLines.map((l, i) => ( // eslint-disable-next-line react/no-array-index-key )), ...SnappingManager.VertSnapLines.map((l, i) => ( // eslint-disable-next-line react/no-array-index-key )), ]}
); } @computed get inkResources() { return ( ); } lightboxMaxBorder = [200, 50]; render() { return (
(ele => { ele.scrollTop = ele.scrollLeft = 0; })(document.getElementById('root')!) } ref={r => { r && new _global.ResizeObserver( action(() => { this._windowWidth = r.getBoundingClientRect().width; this._windowHeight = r.getBoundingClientRect().height; }) ).observe(r); }}> {this.inkResources} {this._hideUI ? null : } {DocButtonState.Instance.LinkEditorDocView ? ( { DocButtonState.Instance.LinkEditorDocView = undefined; })} docView={DocButtonState.Instance.LinkEditorDocView} /> ) : null} {LinkInfo.Instance?.LinkInfo ? ( // eslint-disable-next-line react/jsx-props-no-spreading ) : null} {((page: string) => { // prettier-ignore switch (page) { case 'home': return ; case 'dashboard': default: return (<>
{this.mainDashboardArea} ); } })(Doc.ActivePage)} {/* */} {this.snapLines} {/* */}
); } } // eslint-disable-next-line prefer-arrow-callback ScriptingGlobals.add(function selectMainMenu(doc: Doc) { MainView.Instance.selectMenu(doc); }); // eslint-disable-next-line prefer-arrow-callback ScriptingGlobals.add(function createNewPresentation() { return MainView.Instance.createNewPresentation(); }, 'creates a new presentation when called'); // eslint-disable-next-line prefer-arrow-callback ScriptingGlobals.add(function openPresentation(pres: Doc) { return MainView.Instance.openPresentation(pres); }, 'creates a new presentation when called'); // eslint-disable-next-line prefer-arrow-callback ScriptingGlobals.add(function createNewFolder() { return MainView.Instance.createNewFolder(); }, 'creates a new folder in myFiles when called');