import { IconProp } from '@fortawesome/fontawesome-svg-core'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Tooltip } from '@material-ui/core'; import 'golden-layout/src/css/goldenlayout-base.css'; import 'golden-layout/src/css/goldenlayout-dark-theme.css'; import { clamp } from 'lodash'; import { action, computed, IReactionDisposer, observable, reaction } from "mobx"; import { observer } from "mobx-react"; import * as ReactDOM from 'react-dom'; import { DataSym, Doc, HeightSym, Opt, WidthSym } from "../../../fields/Doc"; import { Id } from '../../../fields/FieldSymbols'; import { List } from '../../../fields/List'; import { FieldId } from "../../../fields/RefField"; import { BoolCast, Cast, NumCast, StrCast } from "../../../fields/Types"; import { emptyFunction, lightOrDark, returnEmptyDoclist, returnFalse, returnTrue, setupMoveUpEvents, simulateMouseClick, Utils } from "../../../Utils"; import { DocServer } from "../../DocServer"; import { DocUtils } from '../../documents/Documents'; import { DocumentType } from '../../documents/DocumentTypes'; import { CurrentUserUtils } from '../../util/CurrentUserUtils'; import { DocumentManager } from '../../util/DocumentManager'; import { DragManager, dropActionType } from "../../util/DragManager"; import { SelectionManager } from '../../util/SelectionManager'; import { SnappingManager } from '../../util/SnappingManager'; import { Transform } from '../../util/Transform'; import { undoBatch, UndoManager } from "../../util/UndoManager"; import { Colors, Shadows } from '../global/globalEnums'; import { LightboxView } from '../LightboxView'; import { MainView } from '../MainView'; import { DocFocusOptions, DocumentView, DocumentViewProps } from "../nodes/DocumentView"; import { DashFieldView } from '../nodes/formattedText/DashFieldView'; import { PinProps, PresBox, PresMovement } from '../nodes/trails'; import { DefaultStyleProvider, StyleProp } from '../StyleProvider'; import { CollectionDockingView } from './CollectionDockingView'; import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormView'; import { CollectionView, CollectionViewType } from './CollectionView'; import "./TabDocView.scss"; import React = require("react"); import { listSpec } from '../../../fields/Schema'; import { ScriptField } from '../../../fields/ScriptField'; const _global = (window /* browser */ || global /* node */) as any; interface TabDocViewProps { documentId: FieldId; glContainer: any; } @observer export class TabDocView extends React.Component { _mainCont: HTMLDivElement | null = null; _tabReaction: IReactionDisposer | undefined; @observable _activated: boolean = false; @observable _panelWidth = 0; @observable _panelHeight = 0; @observable _isActive: boolean = false; @observable _document: Doc | undefined; @observable _view: DocumentView | undefined; @computed get layoutDoc() { return this._document && Doc.Layout(this._document); } @computed get tabColor() { return StrCast(this._document?._backgroundColor, StrCast(this._document?.backgroundColor, DefaultStyleProvider(this._document, undefined, StyleProp.BackgroundColor))); } @computed get tabTextColor() { return this._document?.type === DocumentType.PRES ? "black" : StrCast(this._document?._color, StrCast(this._document?.color, DefaultStyleProvider(this._document, undefined, StyleProp.Color))); } // @computed get renderBounds() { // const bounds = this._document ? Cast(this._document._renderContentBounds, listSpec("number"), [0, 0, this.returnMiniSize(), this.returnMiniSize()]) : [0, 0, 0, 0]; // const xbounds = bounds[2] - bounds[0]; // const ybounds = bounds[3] - bounds[1]; // const dim = Math.max(xbounds, ybounds); // return { l: bounds[0] + xbounds / 2 - dim / 2, t: bounds[1] + ybounds / 2 - dim / 2, cx: bounds[0] + xbounds / 2, cy: bounds[1] + ybounds / 2, dim }; // } get stack() { return (this.props as any).glContainer.parent.parent; } get tab() { return (this.props as any).glContainer.tab; } get view() { return this._view; } _lastTab: any; _lastView: DocumentView | undefined; @action init = (tab: any, doc: Opt) => { if (tab.contentItem === tab.header.parent.getActiveContentItem()) this._activated = true; if (tab.DashDoc !== doc && doc && tab.hasOwnProperty("contentItem") && tab.contentItem.config.type !== "stack") { tab._disposers = {} as { [name: string]: IReactionDisposer }; tab.contentItem.config.fixed && (tab.contentItem.parent.config.fixed = true); tab.DashDoc = doc; const iconType: IconProp = Doc.toIcon(doc); // setup the title element and set its size according to the # of chars in the title. Show the full title when clicked. const titleEle = tab.titleElement[0]; const iconWrap = document.createElement("div"); const closeWrap = document.createElement("div"); titleEle.size = StrCast(doc.title).length + 3; titleEle.value = doc.title; titleEle.onkeydown = (e: KeyboardEvent) => { e.stopPropagation(); }; titleEle.onchange = undoBatch(action((e: any) => { titleEle.size = e.currentTarget.value.length + 3; Doc.GetProto(doc).title = e.currentTarget.value; })); if (tab.element[0].children[1].children.length === 1) { iconWrap.className = "lm_iconWrap lm_moreInfo"; const dragBtnDown = (e: React.PointerEvent) => { setupMoveUpEvents(this, e, e => !e.defaultPrevented && DragManager.StartDocumentDrag([iconWrap], new DragManager.DocumentDragData([doc], doc.dropAction as dropActionType), e.clientX, e.clientY), returnFalse, action(e => { if (this.view) { SelectionManager.SelectView(this.view, false); let child = this.view.ContentDiv!.children[0]; while (child.children.length) { const next = Array.from(child.children).find(c => c.className?.toString().includes("SVGAnimatedString") || typeof (c.className) === "string"); if (next?.className?.toString().includes(DocumentView.ROOT_DIV)) break; if (next?.className?.toString().includes(DashFieldView.name)) break; if (next) child = next; else break; } simulateMouseClick(child, e.clientX, e.clientY + 30, e.screenX, e.screenY + 30); } else { this._activated = true; setTimeout(() =>this.view && SelectionManager.SelectView(this.view, false)); } })); }; const docIcon = ; const closeIcon = ; ReactDOM.render(docIcon, iconWrap); ReactDOM.render(closeIcon, closeWrap); tab.reactComponents = [iconWrap, closeWrap]; tab.element[0].prepend(iconWrap); tab._disposers.layerDisposer = reaction(() => ({ layer: tab.DashDoc.activeLayer, color: this.tabColor }), ({ layer, color }) => { // console.log("TabDocView: " + this.tabColor); // console.log("lightOrDark: " + lightOrDark(this.tabColor)); const textColor = lightOrDark(this.tabColor); //not working with StyleProp.Color titleEle.style.color = textColor; titleEle.style.backgroundColor = "transparent"; iconWrap.style.color = textColor; closeWrap.style.color = textColor; tab.element[0].style.background = !layer ? color : "dimgrey"; }, { fireImmediately: true }); } // shifts the focus to this tab when another tab is dragged over it tab.element[0].onmouseenter = (e: MouseEvent) => { if (SnappingManager.GetIsDragging() && tab.contentItem !== tab.header.parent.getActiveContentItem()) { tab.header.parent.setActiveContentItem(tab.contentItem); tab.setActive(true); } }; // select the tab document when the tab is directly clicked and activate the tab whenver the tab document is selected titleEle.onpointerdown = action((e: any) => { if (e.target.className !== "lm_iconWrap") { if (this.view) SelectionManager.SelectView(this.view, false); else this._activated = true; if (Date.now() - titleEle.lastClick < 1000) titleEle.select(); titleEle.lastClick = Date.now(); (document.activeElement !== titleEle) && titleEle.focus(); } }); tab._disposers.selectionDisposer = reaction(() => SelectionManager.Views().some(v => v.topMost && v.props.Document === doc), action((selected) => { if (selected) this._activated = true; const toggle = tab.element[0].children[1].children[0] as HTMLInputElement; selected && tab.contentItem !== tab.header.parent.getActiveContentItem() && UndoManager.RunInBatch(() => tab.header.parent.setActiveContentItem(tab.contentItem), "tab switch"); // toggle.style.fontWeight = selected ? "bold" : ""; // toggle.style.textTransform = selected ? "uppercase" : ""; })); // highlight the tab when the tab document is brushed in any part of the UI tab._disposers.reactionDisposer = reaction(() => ({ title: doc.title, degree: Doc.IsBrushedDegree(doc) }), ({ title, degree }) => { //titleEle.value = title; // titleEle.style.padding = degree ? 0 : 2; // titleEle.style.border = `${["gray", "gray", "gray"][degree]} ${["none", "dashed", "solid"][degree]} 2px`; }, { fireImmediately: true }); // clean up the tab when it is closed tab.closeElement.off('click') //unbind the current click handler .click(function () { Object.values(tab._disposers).forEach((disposer: any) => disposer?.()); SelectionManager.DeselectAll(); UndoManager.RunInBatch(() => tab.contentItem.remove(), "delete tab"); }); } } /** * Adds a document to the presentation view **/ @action public static PinDoc(docs: Doc|Doc[], pinProps?: PinProps) { const docList = ((docs instanceof Doc) ? [docs]: docs); const batch = UndoManager.StartBatch("pinning doc"); // all docs will be added to the ActivePresentation as stored on CurrentUserUtils const curPres = CurrentUserUtils.ActivePresentation; curPres && docList.forEach(doc => { // Edge Case 1: Cannot pin document to itself if (doc === curPres) { alert("Cannot pin presentation document to itself"); return; } const pinDoc = Doc.MakeAlias(doc); pinDoc.presentationTargetDoc = doc; pinDoc.title = doc.title + " - Slide"; pinDoc.data = new List(); // the children of the alias' layout are the presentation slide children. the alias' data field might be children of a collection, PDF data, etc -- in any case we don't want the tree view to "see" this data pinDoc.presMovement = PresMovement.Zoom; pinDoc.groupWithUp = false; pinDoc.context = curPres; // these should potentially all be props passed down by the CollectionTreeView to the TreeView elements. That way the PresBox could configure all of its children at render time pinDoc.treeViewRenderAsBulletHeader = true; // forces a tree view to render the document next to the bullet in the header area pinDoc.treeViewHeaderWidth = "100%"; // forces the header to grow to be the same size as its largest sibling. pinDoc.treeViewChildrenOnRoot = true; // tree view will look for hierarchical children on the root doc, not the data doc. pinDoc.treeViewFieldKey = "data"; // tree view will treat the 'data' field as the field where the hierarchical children are located instead of using the document's layout string field pinDoc.treeViewExpandedView = "data";// in case the data doc has an expandedView set, this will mask that field and use the 'data' field when expanding the tree view pinDoc.treeViewGrowsHorizontally = true;// the document expands horizontally when displayed as a tree view header pinDoc.treeViewHideHeaderIfTemplate = true; // this will force the document to render itself as the tree view header const presArray: Doc[] = PresBox.Instance?.sortArray(); const size: number = PresBox.Instance?._selectedArray.size; const presSelected: Doc | undefined = presArray && size ? presArray[size - 1] : undefined; const duration = NumCast(doc[`${Doc.LayoutFieldKey(pinDoc)}-duration`], null); // If pinWithView option set then update scale and x / y props of slide if (pinProps?.pinWithView) { const viewProps = pinProps.pinWithView; pinDoc.presPinView = true; pinDoc.presPinViewX = viewProps.bounds.left + viewProps.bounds.width / 2; pinDoc.presPinViewY = viewProps.bounds.top + viewProps.bounds.height / 2; pinDoc.presPinViewScale = viewProps.scale; pinDoc.contentBounds = new List([viewProps.bounds.left, viewProps.bounds.top, viewProps.bounds.left+viewProps.bounds.width, viewProps.bounds.top+viewProps.bounds.height]); } if (pinProps?.pinDocView) { const scrollable = [DocumentType.PDF, DocumentType.RTF, DocumentType.WEB].includes(pinDoc.type as any) || pinDoc._viewType === CollectionViewType.Stacking; const pannable: boolean = ((pinDoc.type === DocumentType.COL && doc._viewType === CollectionViewType.Freeform) || doc.type === DocumentType.IMG); if (scrollable) { const scroll = doc._scrollTop; pinDoc.presPinView = true; pinDoc.presPinViewScroll = scroll; } else if ([DocumentType.AUDIO, DocumentType.VID].includes(doc.type as any)) { pinDoc.presPinView = true; pinDoc.presStartTime = doc._currentTimecode; pinDoc.presEndTime = NumCast(doc._currentTimecode) + 0.1; } else if (pannable) { pinDoc.presPinView = true; pinDoc.presPinViewX = pinDoc._panX; pinDoc.presPinViewY = pinDoc._panY; pinDoc.presPinViewScale = pinDoc._viewScale; const pw = NumCast(pinProps.panelWidth); const ph = NumCast(pinProps.panelHeight); const ps = NumCast(pinDoc._viewScale); if (pw && ph && ps) { pinDoc.contentBounds = new List([ NumCast(pinDoc.panX)-pw/2/ps, NumCast(pinDoc.panY)-ph/2/ps, NumCast(pinDoc.panX)+pw/2/ps, NumCast(pinDoc.panY)+ph/2/ps]); } } else if (doc.type === DocumentType.COMPARISON) { const width = doc._clipWidth; pinDoc.presPinClipWidth = width; pinDoc.presPinView = true; } } pinDoc.onClick = ScriptField.MakeFunction("navigateToDoc(self.presentationTargetDoc, self)"); Doc.AddDocToList(curPres, "data", pinDoc, presSelected); if (!pinProps?.audioRange && duration !== undefined) { pinDoc.mediaStart = "manual"; pinDoc.mediaStop = "manual"; pinDoc.presStartTime = NumCast(doc.clipStart); pinDoc.presEndTime = NumCast(doc.clipEnd, duration); } //save position if (pinProps?.setPosition || pinDoc.isInkMask) { pinDoc.setPosition = true; pinDoc.y = doc.y; pinDoc.x = doc.x; pinDoc.presHideAfter = true; pinDoc.presHideBefore = true; pinDoc.title = doc.title + " (move)"; pinDoc.presMovement = PresMovement.None; } if (curPres.expandBoolean) pinDoc.presExpandInlineButton = true; PresBox.Instance?._selectedArray.clear(); pinDoc && PresBox.Instance?._selectedArray.set(pinDoc, undefined); //Update selected array }); if (!Array.from(CollectionDockingView.Instance.tabMap).map(d => d.DashDoc).includes(curPres)) { const docs = Cast(CurrentUserUtils.MyOverlayDocs.data, listSpec(Doc), []); if (docs.includes(curPres)) docs.splice(docs.indexOf(curPres), 1); CollectionDockingView.AddSplit(curPres, "right"); setTimeout(() => DocumentManager.Instance.jumpToDocument(docList.lastElement(), false, undefined, []), 100); // keeps the pinned doc in view since the sidebar shifts things } setTimeout(batch.end, 500); // need to wait until dockingview (goldenlayout) updates all its structurs } componentDidMount() { new _global.ResizeObserver(action((entries: any) => { for (const entry of entries) { this._panelWidth = entry.contentRect.width; this._panelHeight = entry.contentRect.height; } })).observe(this.props.glContainer._element[0]); this.props.glContainer.layoutManager.on("activeContentItemChanged", this.onActiveContentItemChanged); this.props.glContainer.tab?.isActive && this.onActiveContentItemChanged(undefined); // this._tabReaction = reaction(() => ({ selected: this.active(), title: this.tab?.titleElement[0] }), // ({ selected, title }) => title && (title.style.backgroundColor = selected ? "white" : ""), // { fireImmediately: true }); } componentDidUpdate() { this._view && DocumentManager.Instance.AddView(this._view); } componentWillUnmount() { this._tabReaction?.(); this._view && DocumentManager.Instance.RemoveView(this._view); this.props.glContainer.layoutManager.off("activeContentItemChanged", this.onActiveContentItemChanged); } @action.bound private onActiveContentItemChanged(contentItem: any) { if (!contentItem || (this.stack === contentItem.parent && ((contentItem?.tab === this.tab && !this._isActive) || (contentItem?.tab !== this.tab && this._isActive)))) { this._activated = this._isActive = !contentItem || contentItem?.tab === this.tab; !this._isActive && this._document && Doc.UnBrushDoc(this._document); // bcz: bad -- trying to simulate a pointer leave event when a new tab is opened up on top of an existing one. } } // adds a tab to the layout based on the locaiton parameter which can be: // close[:{left,right,top,bottom}] - e.g., "close" will close the tab, "close:left" will close the left tab, // add[:{left,right,top,bottom}] - e.g., "add" will add a tab to the current stack, "add:right" will add a tab on the right // replace[:{left,right,top,bottom,}] - e.g., "replace" will replace the current stack contents, // "replace:right" - will replace the stack on the right named "right" if it exists, or create a stack on the right with that name, // "replace:monkeys" - will replace any tab that has the label 'monkeys', or a tab with that label will be created by default on the right // inPlace - will add the document to any collection along the path from the document to the docking view that has a field isInPlaceContainer. if none is found, inPlace adds a tab to current stack addDocTab = (doc: Doc, location: string) => { SelectionManager.DeselectAll(); const locationFields = doc._viewType === CollectionViewType.Docking ? ["dashboard"] : location.split(":"); const locationParams = locationFields.length > 1 ? locationFields[1] : ""; switch (locationFields[0]) { case "dashboard": return CurrentUserUtils.openDashboard(doc); case "close": return CollectionDockingView.CloseSplit(doc, locationParams); case "fullScreen": return CollectionDockingView.OpenFullScreen(doc); case "replace": return CollectionDockingView.ReplaceTab(doc, locationParams, this.stack); // case "lightbox": { // // TabDocView.PinDoc(doc, { hidePresBox: true }); // return LightboxView.AddDocTab(doc, location, undefined, this.addDocTab); // } case "lightbox": return LightboxView.AddDocTab(doc, location, undefined, this.addDocTab); case "toggle": return CollectionDockingView.ToggleSplit(doc, locationParams, this.stack); case "inPlace": case "add": default: return CollectionDockingView.AddSplit(doc, locationParams, this.stack); } } remDocTab = (doc: Doc | Doc[]) => { if (doc === this._document) { SelectionManager.DeselectAll(); CollectionDockingView.CloseSplit(this._document); return true; } return false; } getCurrentFrame = () => { return NumCast(Cast(PresBox.Instance.childDocs[PresBox.Instance.itemIndex].presentationTargetDoc, Doc, null)._currentFrame); } @action focusFunc = (doc: Doc, options?: DocFocusOptions) => { const shrinkwrap = options?.originalTarget === this._document && this.view?.ComponentView?.shrinkWrap; if (shrinkwrap && this._document) { const focusSpeed = 1000; shrinkwrap(); this._document._viewTransition = `transform ${focusSpeed}ms`; setTimeout(action(() => { this._document!._viewTransition = undefined; options?.afterFocus?.(false); }), focusSpeed); } else { options?.afterFocus?.(false); } if (!this.tab.header.parent._activeContentItem || this.tab.header.parent._activeContentItem !== this.tab.contentItem) { this.tab.header.parent.setActiveContentItem(this.tab.contentItem); // glr: Panning does not work when this is set - (this line is for trying to make a tab that is not topmost become topmost) } } active = () => this._isActive; @observable _forceInvalidateScreenToLocal = 0; ScreenToLocalTransform = () => { this._forceInvalidateScreenToLocal; const { translateX, translateY } = Utils.GetScreenTransform(this._mainCont?.children?.[0] as HTMLElement); return CollectionDockingView.Instance?.props.ScreenToLocalTransform().translate(-translateX, -translateY); } PanelWidth = () => this._panelWidth; PanelHeight = () => this._panelHeight; miniMapColor = () => this.tabColor; tabView = () => this._view; disableMinimap = () => !this._document || (this._document.layout !== CollectionView.LayoutString(Doc.LayoutFieldKey(this._document)) || this._document?._viewType !== CollectionViewType.Freeform); hideMinimap = () => this.disableMinimap() || BoolCast(this._document?.hideMinimap); @computed get docView() { return !this._activated || !this._document || this._document._viewType === CollectionViewType.Docking ? (null) : <> { this._lastView && DocumentManager.Instance.RemoveView(this._lastView); this._view = r; this._lastView = this._view; })} renderDepth={0} Document={this._document} DataDoc={!Doc.AreProtosEqual(this._document[DataSym], this._document) ? this._document[DataSym] : undefined} ContainingCollectionView={undefined} ContainingCollectionDoc={undefined} onBrowseClick={MainView.Instance.exploreMode} isContentActive={returnTrue} PanelWidth={this.PanelWidth} PanelHeight={this.PanelHeight} styleProvider={DefaultStyleProvider} docFilters={CollectionDockingView.Instance.childDocFilters} docRangeFilters={CollectionDockingView.Instance.childDocRangeFilters} searchFilterDocs={CollectionDockingView.Instance.searchFilterDocs} addDocument={undefined} removeDocument={this.remDocTab} addDocTab={this.addDocTab} ScreenToLocalTransform={this.ScreenToLocalTransform} dontCenter={"y"} rootSelected={returnTrue} whenChildContentsActiveChanged={emptyFunction} focus={this.focusFunc} docViewPath={returnEmptyDoclist} bringToFront={emptyFunction} pinToPres={TabDocView.PinDoc} /> {this._document.hideMinimap ? "Open minimap" : "Close minimap"}}>
e.stopPropagation()} onClick={action(e => { e.stopPropagation(); this._document!.hideMinimap = !this._document!.hideMinimap; })} >
; } render() { return (
{ if (this._mainCont = ref) { if (this._lastTab) { this._view && DocumentManager.Instance.RemoveView(this._view); } this._lastTab = this.tab; (this._mainCont as any).InitTab = (tab: any) => this.init(tab, this._document); DocServer.GetRefField(this.props.documentId).then(action(doc => doc instanceof Doc && (this._document = doc) && this.tab && this.init(this.tab, this._document))); new _global.ResizeObserver(action((entries: any) => this._forceInvalidateScreenToLocal++)).observe(ref); } }} > {this.docView}
); } } interface TabMinimapViewProps { document: Doc; hideMinimap: () => boolean; tabView: () => DocumentView | undefined; addDocTab: (doc: Doc, where: string) => boolean; PanelWidth: () => number; PanelHeight: () => number; background: () => string; } @observer export class TabMinimapView extends React.Component { static miniStyleProvider = (doc: Opt, props: Opt, property: string): any => { if (doc) { switch (property.split(":")[0]) { default: return DefaultStyleProvider(doc, props, property); case StyleProp.PointerEvents: return "none"; case StyleProp.DocContents: const background = ((type: DocumentType) => { switch (type) { case DocumentType.PDF: return "pink"; case DocumentType.AUDIO: return "lightgreen"; case DocumentType.WEB: return "brown"; case DocumentType.IMG: return "blue"; case DocumentType.MAP: return "orange"; case DocumentType.VID: return "purple"; case DocumentType.RTF: return "yellow"; case DocumentType.COL: return undefined; default: return "gray"; } })(doc.type as DocumentType); return !background ? undefined :
; } } } @computed get renderBounds() { const compView = this.props.tabView()?.ComponentView as CollectionFreeFormView; const bounds = compView?.freeformData?.(true)?.bounds; if (!bounds) return undefined; const xbounds = bounds.r - bounds.x; const ybounds = bounds.b - bounds.y; const dim = Math.max(xbounds, ybounds); return { l: bounds.x + xbounds / 2 - dim / 2, t: bounds.y + ybounds / 2 - dim / 2, cx: bounds.x + xbounds / 2, cy: bounds.y + ybounds / 2, dim }; } childLayoutTemplate = () => Cast(this.props.document.childLayoutTemplate, Doc, null); returnMiniSize = () => NumCast(this.props.document._miniMapSize, 150); miniDown = (e: React.PointerEvent) => { const doc = this.props.document; const renderBounds = this.renderBounds ?? { l: 0, r: 0, t: 0, b: 0, dim: 1 }; const miniSize = this.returnMiniSize(); doc && setupMoveUpEvents(this, e, action((e: PointerEvent, down: number[], delta: number[]) => { doc._panX = clamp(NumCast(doc._panX) + delta[0] / miniSize * renderBounds.dim, renderBounds.l, renderBounds.l + renderBounds.dim); doc._panY = clamp(NumCast(doc._panY) + delta[1] / miniSize * renderBounds.dim, renderBounds.t, renderBounds.t + renderBounds.dim); return false; }), emptyFunction, emptyFunction); } render() { if (!this.renderBounds) return (null); const miniWidth = this.props.PanelWidth() / NumCast(this.props.document._viewScale, 1) / this.renderBounds.dim * 100; const miniHeight = this.props.PanelHeight() / NumCast(this.props.document._viewScale, 1) / this.renderBounds.dim * 100; const miniLeft = 50 + (NumCast(this.props.document._panX) - this.renderBounds.cx) / this.renderBounds.dim * 100 - miniWidth / 2; const miniTop = 50 + (NumCast(this.props.document._panY) - this.renderBounds.cy) / this.renderBounds.dim * 100 - miniHeight / 2; const miniSize = this.returnMiniSize(); return this.props.hideMinimap() ? (null) :
; } }