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'; import ResizeObserver from 'resize-observer-polyfill'; import '@dash/components/src/global/globalCssVariables.scss'; import { ClientUtils, returnEmptyFilter, returnFalse, returnTrue, returnZero, setupMoveUpEvents } from '../../ClientUtils'; import { emptyFunction } from '../../Utils'; import { Doc, DocListCast, GetDocFromUrl, Opt, returnEmptyDoclist } from '../../fields/Doc'; import { Id } from '../../fields/FieldSymbols'; import { DocCast, StrCast, toList } 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 { CurrentUserUtils, ToTagName } from '../util/CurrentUserUtils'; 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 { 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 { InkTranscription } from './InkTranscription'; import { LightboxView } from './LightboxView'; import './MainView.scss'; import { ObservableReactComponent } from './ObservableReactComponent'; import { PreviewCursor } from './PreviewCursor'; import { PropertiesView } from './PropertiesView'; import { DashboardStyleProvider, DefaultStyleProvider, returnEmptyDocViewList } 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 { CollectionFreeFormView } from './collections/collectionFreeForm'; import { ImageLabelHandler } from './collections/collectionFreeForm/ImageLabelHandler'; import { MarqueeOptionsMenu } from './collections/collectionFreeForm/MarqueeOptionsMenu'; import { CollectionLinearView } from './collections/collectionLinear'; import { LinkMenu } from './linking/LinkMenu'; import { DocCreatorMenu } from './nodes/DataVizBox/DocCreatorMenu/DocCreatorMenu'; import { SchemaCSVPopUp } from './nodes/DataVizBox/SchemaCSVPopUp'; import { DocButtonState } from './nodes/DocumentLinksButton'; import { DocumentView, DocumentViewInternal } 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 { OpenWhere, OpenWhereMod } from './nodes/OpenWhere'; import { TaskCompletionBox } from './nodes/TaskCompletedBox'; import { DashFieldViewMenu } from './nodes/formattedText/DashFieldView'; import { RichTextMenu } from './nodes/formattedText/RichTextMenu'; import ImageEditorBox from './nodes/imageEditor/ImageEditor'; import { PresBox } from './nodes/trails'; import { AnchorMenu } from './pdf/AnchorMenu'; import { SmartDrawHandler } from './smartdraw/SmartDrawHandler'; import { TopBar } from './topbar/TopBar'; // eslint-disable-next-line @typescript-eslint/no-require-imports 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 _keepContextMenuOpen: boolean = false; @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?: Doc = Doc.MyLeftSidebarPanel; @observable private _leftMenuFlyoutWidth: number = 0; @computed get _hideUI() { return SnappingManager.HideUI || (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 || DocumentView.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, SnappingManager.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 () => DocumentView.Selected().slice(), views => views.length > 1 && document.activeElement instanceof HTMLElement && document.activeElement?.blur() ); reaction( () => Doc.MyDockedBtns?.linearView_isOpen, open => SnappingManager.SetPrintToConsole(!!open) ); 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 && (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); document.addEventListener('dash', (e: Event) => { // event used by chrome plugin to tell Dash which document to focus on const id = GetDocFromUrl((e as Event & { detail: string }).detail); DocServer.GetRefField(id).then(doc => (doc instanceof Doc ? DocumentView.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: object) { super(props); makeObservable(this); DocumentViewInternal.addDocTabFunc = MainView.addDocTabFunc_impl; MainView.Instance = this; DashboardView._urlState = HistoryUtil.parseUrl(window.location) ?? { type: 'doc', docId: '' }; // 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.faMinimize, fa.faArrowsRotate, fa.faFloppyDisk, fa.faRepeat, fa.faArrowsUpDown, fa.faArrowsLeftRight, fa.faWindowMaximize, fa.faGift, fa.faLockOpen, fa.faSort, fa.faArrowUpZA, fa.faArrowDownAZ, 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, 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.faSplotch, fa.faMicrophone, fa.faCircleHalfStroke, 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.faFileExport, 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.faDeleteLeft, fa.faXmarksLines, fa.faCircleXmark, fa.faXmark, fa.faExclamation, fa.faFileAlt, fa.faFileArrowDown, 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.faUserPen, fa.faPenNib, fa.faPhone, fa.faPlay, fa.faPortrait, fa.faRedoAlt, fa.faStamp, fa.faStickyNote, fa.faArrowsAltV, fa.faTimesCircle, fa.faThumbtack, fa.faScissors, 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.faFill, fa.faFillDrip, fa.faLink, fa.faUnlink, fa.faBold, fa.faItalic, fa.faClipboard, fa.faClipboardCheck, fa.faUnderline, fa.faStrikethrough, fa.faSuperscript, fa.faSubscript, fa.faIndent, fa.faEyeDropper, fa.faPaintRoller, fa.faBars, fa.faBarsStaggered, fa.faBrush, fa.faShapes, fa.faEllipsisH, fa.faHandPaper, fa.faMap, fa.faUser, faHireAHelper, fa.faTrashRestore, fa.faUsers, fa.faWrench, fa.faCog, fa.faMap, fa.faBellSlash, fa.faExpandAlt, fa.faArchive, fa.faBezierCurve, fa.faCircle, far.faCircle, fa.faLongArrowAltRight, fa.faPenFancy, fa.faAngleDoubleRight, fa.faAngleDoubleDown, fa.faAngleDoubleLeft, fa.faAngleDoubleUp, faBuffer, 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.faSquarePlus, fa.faReply, 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.faBorderStyle, 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, fa.faPalette, fa.faHourglassHalf, fa.faRobot, fa.faSatellite, fa.faStar, fa.faFilePen, fa.faCloud, fa.faBolt, fa.faLightbulb, fa.faX, ] ); } private longPressTimer: NodeJS.Timeout | undefined; globalPointerClick = action(() => { this.longPressTimer && clearTimeout(this.longPressTimer); SnappingManager.SetLongPress(false); }); globalPointerMove = action((e: PointerEvent) => { if (e.movementX > 3 || e.movementY > 3) this.longPressTimer && clearTimeout(this.longPressTimer); }); globalPointerDown = action((e: PointerEvent) => { SnappingManager.SetLongPress(false); this.longPressTimer = setTimeout( action(() => { SnappingManager.SetLongPress(true); }), 1000 ); DocumentManager.removeOverlayViews(); Doc.linkFollowUnhighlight(); const targets = document.elementsFromPoint(e.x, e.y); const targetClasses = targets.map(target => target.className.toString()); 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(); !targetClasses.includes('marqueeView') && !targetClasses.includes('smart-draw-handler') && SmartDrawHandler.Instance.hideSmartDrawHandler(); !targetClasses.includes('smart-draw-handler') && SmartDrawHandler.Instance.hideRegenerate(); !['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.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); if (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.MyFilesystem && 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[]) => toList(docs).reduce((done, doc) => !!this.headerBarDoc && Doc.AddDocToList(this.headerBarDoc, 'data', doc), true); removeHeaderDoc = (docs: Doc | Doc[]) => toList(docs).reduce((done, doc) => !!this.headerBarDoc && Doc.RemoveDocFromList(this.headerBarDoc, 'data', doc), true); @computed get headerBarDocView() { return !this.headerBarDoc ? null : (
); } @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)`, opacity: DocumentView.LightboxDoc() ? 0 : undefined, pointerEvents: DocumentView.LightboxDoc() ? 'none' : undefined, }}> {!this.mainContainer ? null : this.mainDocView}
); } @action onPropertiesPointerDown = (e: React.PointerEvent) => { setupMoveUpEvents( this, e, action(moveEv => { SnappingManager.SetPropertiesWidth(Math.max(0, this._dashUIWidth - moveEv.clientX)); return !SnappingManager.PropertiesWidth; }), action(() => { SnappingManager.PropertiesWidth < 5 && SnappingManager.SetPropertiesWidth(0); }), action(() => { SnappingManager.SetPropertiesWidth(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 = toList(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._sidebarContent ? null : (
{this.docButtons}
); } @computed get leftMenuPanel() { return !Doc.MyLeftSidebarMenu ? null : (
); } @action selectLeftSidebarButton = (button: Doc) => { const title = StrCast(button.$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._leftMenuFlyoutWidth = this._leftMenuFlyoutWidth || 250; this._sidebarContent && (this._sidebarContent.proto = DocCast(button.target)); SnappingManager.SetLastPressedBtn(button[Id]); } } return true; }; /** * Allows users to add a filter hotkey to the properties panel. Will also update the multitoggle at the top menu and the * icontags tht are displayed on the documents themselves * @param hotKey tite of the new hotkey */ addHotKey = (hotKey: string) => { const filterIcons = DocCast(DocCast(Doc.UserDoc().myContextMenuBtns)?.Filter); if (filterIcons) { const menuDoc = CurrentUserUtils.setupContextMenuBtn(CurrentUserUtils.filterBtnDesc(ToTagName(hotKey), 'question'), filterIcons); Doc.AddToFilterHotKeys(menuDoc); } }; @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 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}
); } closeFlyout = action(() => { SnappingManager.SetLastPressedBtn(''); this._panelContent = 'none'; this._sidebarContent && (this._sidebarContent.proto = undefined); this._leftMenuFlyoutWidth = 0; }); remButtonDoc = (docs: Doc | Doc[]) => toList(docs).reduce((flg: boolean, doc) => flg && !doc.dragOnlyWithinContainer && !!Doc.MyDockedBtns && 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[]) => toList(docs).reduce((flg: boolean, doc) => flg && !!Doc.MyDockedBtns && 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() ?? DocumentView.SelectedDocs().lastElement(); const dragPar = dragged ? CollectionFreeFormView.from(DocumentView.getViews(dragged).lastElement()) : undefined; return !dragPar?.layoutDoc.freeform_snapLines ? null : (
{[ ...SnappingManager.HorizSnapLines.map(l => ( )), ...SnappingManager.VertSnapLines.map(l => ( )), ]}
); } @computed get inkResources() { return ( ); } togglePropertiesFlyout = () => { if (MainView.Instance.propertiesWidth() > 0) { SnappingManager.SetPropertiesWidth(0); } else { SnappingManager.SetPropertiesWidth(300); } }; lightboxMaxBorder = [200, 50]; render() { return (
(ele => { ele.scrollTop = ele.scrollLeft = 0; })(document.getElementById('root')!) } ref={r => { r && new 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 ? : 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.selectLeftSidebarButton(doc); }); // eslint-disable-next-line prefer-arrow-callback ScriptingGlobals.add(function hideUI() { SnappingManager.SetHideUI(!SnappingManager.HideUI); }); // 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');