diff options
Diffstat (limited to 'src/client/views/collections/CollectionDockingView.tsx')
-rw-r--r-- | src/client/views/collections/CollectionDockingView.tsx | 1015 |
1 files changed, 201 insertions, 814 deletions
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 3d8ac2a23..d8e95bcdc 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -1,46 +1,32 @@ 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, Lambda, observable, reaction, runInAction } from "mobx"; +import { action, IReactionDisposer, observable, reaction, runInAction } from "mobx"; import { observer } from "mobx-react"; import * as ReactDOM from 'react-dom'; import * as GoldenLayout from "../../../client/goldenLayout"; -import { DateField } from '../../../fields/DateField'; -import { DataSym, Doc, DocListCast, Field, Opt } from "../../../fields/Doc"; +import { Doc, DocListCast, Opt } from "../../../fields/Doc"; import { Id } from '../../../fields/FieldSymbols'; import { InkTool } from '../../../fields/InkField'; import { List } from '../../../fields/List'; -import { FieldId } from "../../../fields/RefField"; -import { listSpec } from '../../../fields/Schema'; import { Cast, NumCast, StrCast } from "../../../fields/Types"; -import { TraceMobx } from '../../../fields/util'; -import { emptyFunction, emptyPath, returnEmptyFilter, returnFalse, returnOne, returnTrue, returnZero, setupMoveUpEvents, Utils, returnEmptyDoclist } from "../../../Utils"; import { DocServer } from "../../DocServer"; import { Docs } from '../../documents/Documents'; -import { DocumentManager } from '../../util/DocumentManager'; -import { DragManager, dropActionType } from "../../util/DragManager"; +import { CurrentUserUtils } from '../../util/CurrentUserUtils'; +import { DragManager } from "../../util/DragManager"; import { InteractionUtils } from '../../util/InteractionUtils'; import { Scripting } from '../../util/Scripting'; -import { SelectionManager } from '../../util/SelectionManager'; -import { SnappingManager } from '../../util/SnappingManager'; -import { Transform } from '../../util/Transform'; -import { undoBatch } from "../../util/UndoManager"; -import { DocumentView } from "../nodes/DocumentView"; -import { PresBox } from '../nodes/PresBox'; +import { undoBatch, UndoManager } from "../../util/UndoManager"; import "./CollectionDockingView.scss"; -import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormView'; -import { SubCollectionViewProps, CollectionSubView } from "./CollectionSubView"; +import { CollectionSubView, SubCollectionViewProps } from "./CollectionSubView"; import { CollectionViewType } from './CollectionView'; -import { DockingViewButtonSelector } from './ParentDocumentSelector'; +import { TabDocView } from './TabDocView'; import React = require("react"); -import { CurrentUserUtils } from '../../util/CurrentUserUtils'; const _global = (window /* browser */ || global /* node */) as any; @observer export class CollectionDockingView extends CollectionSubView(doc => doc) { - @observable public static Instances: CollectionDockingView[] = []; - @computed public static get Instance() { return CollectionDockingView.Instances[0]; } - public static makeDocumentConfig(document: Doc, width?: number, libraryPath?: Doc[]) { + @observable public static Instance: CollectionDockingView; + public static makeDocumentConfig(document: Doc, panelName?: string, width?: number) { return { type: 'react-component', component: 'DocumentFrameRenderer', @@ -48,332 +34,210 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) { width: width, props: { documentId: document[Id], - libraryPath: libraryPath?.map(d => d[Id]) + panelName // name of tab that can be used to close or replace its contents } }; } - @computed public get initialized() { - return this._goldenLayout !== null; - } - - @observable private _goldenLayout: any = null; + private _reactionDisposer?: IReactionDisposer; private _containerRef = React.createRef<HTMLDivElement>(); - private _flush: boolean = false; + private _flush: UndoManager.Batch | undefined; private _ignoreStateChange = ""; - private _isPointerDown = false; - private _maximizedSrc: Opt<DocumentView>; + public tabMap: Set<any> = new Set(); + public get initialized() { return this._goldenLayout !== null; } + public get HasFullScreen() { return this._goldenLayout._maximisedItem !== null; } + @observable private _goldenLayout: any = null; constructor(props: SubCollectionViewProps) { super(props); - runInAction(() => !CollectionDockingView.Instances ? CollectionDockingView.Instances = [this] : CollectionDockingView.Instances.push(this)); + runInAction(() => CollectionDockingView.Instance = this); //Why is this here? (window as any).React = React; (window as any).ReactDOM = ReactDOM; DragManager.StartWindowDrag = this.StartOtherDrag; } + public StartOtherDrag = (e: any, dragDocs: Doc[]) => { + !this._flush && (this._flush = UndoManager.StartBatch("golden layout drag")); const config = dragDocs.length === 1 ? CollectionDockingView.makeDocumentConfig(dragDocs[0]) : - { - type: 'row', - content: dragDocs.map((doc, i) => CollectionDockingView.makeDocumentConfig(doc)) - }; + { type: 'row', content: dragDocs.map((doc, i) => CollectionDockingView.makeDocumentConfig(doc)) }; const dragSource = this._goldenLayout.createDragSource(document.createElement("div"), config); - dragSource._dragListener.on("dragStop", () => dragSource.destroy()); + //dragSource._dragListener.on("dragStop", dragSource.destroy); dragSource._dragListener.onMouseDown(e); } @undoBatch - @action - public OpenFullScreen(docView: DocumentView, libraryPath?: Doc[]) { - if (docView.props.Document._viewType === CollectionViewType.Docking && docView.props.Document.layoutKey === "layout") { - return CurrentUserUtils.openDashboard(Doc.UserDoc(), docView.props.Document); - } - const document = Doc.MakeAlias(docView.props.Document); - const newItemStackConfig = { - type: 'stack', - content: [CollectionDockingView.makeDocumentConfig(document, undefined, libraryPath)] - }; - const docconfig = this._goldenLayout.root.layoutManager.createContentItem(newItemStackConfig, this._goldenLayout); - this._goldenLayout.root.contentItems[0].addChild(docconfig); - docconfig.callDownwards('_$init'); - this._goldenLayout._$maximiseItem(docconfig); - this._maximizedSrc = docView; - this._ignoreStateChange = JSON.stringify(this._goldenLayout.toConfig()); + public CloseFullScreen = () => { + this._goldenLayout._maximisedItem?.toggleMaximise(); this.stateChanged(); - SelectionManager.DeselectAll(); } - public CloseFullScreen = () => { - const target = this._goldenLayout._maximisedItem; - if (target !== null && this._maximizedSrc) { - this._goldenLayout._maximisedItem.remove(); - SelectionManager.SelectDoc(this._maximizedSrc, false); - this._maximizedSrc = undefined; - this.stateChanged(); + @undoBatch + public static CloseSplit(document: Opt<Doc>, panelName?: string): boolean { + const tab = Array.from(CollectionDockingView.Instance.tabMap.keys()).find((tab) => panelName ? tab.contentItem.config.props.panelName === panelName : tab.DashDoc === document); + if (tab) { + const j = tab.header.parent.contentItems.indexOf(tab.contentItem); + if (j !== -1) { + tab.header.parent.contentItems[j].remove(); + return CollectionDockingView.Instance.layoutChanged(); + } } - } - public HasFullScreen = () => { - return this._goldenLayout._maximisedItem !== null; + return false; } @undoBatch - @action - public static CloseRightSplit(document: Opt<Doc>): boolean { + public static OpenFullScreen(doc: Doc, libraryPath?: Doc[]) { const instance = CollectionDockingView.Instance; - const tryClose = (childItem: any) => { - if (childItem.config?.component === "DocumentFrameRenderer") { - const docView = DocumentManager.Instance.getDocumentViewById(childItem.config.props.documentId); - if (docView && ((!document && docView.Document.isDisplayPanel) || (document && Doc.AreProtosEqual(docView.props.Document, document)))) { - childItem.remove(); - instance.layoutChanged(document); - return true; - } - } - return false; + if (doc._viewType === CollectionViewType.Docking && doc.layoutKey === "layout") { + return CurrentUserUtils.openDashboard(Doc.UserDoc(), doc); + } + const newItemStackConfig = { + type: 'stack', + content: [CollectionDockingView.makeDocumentConfig(Doc.MakeAlias(doc))] }; - const retVal = !instance?._goldenLayout.root.contentItems[0].isRow ? false : - Array.from(instance._goldenLayout.root.contentItems[0].contentItems).some((child: any) => Array.from(child.contentItems).some(tryClose)); - - retVal && instance.stateChanged(); - return retVal; + const docconfig = instance._goldenLayout.root.layoutManager.createContentItem(newItemStackConfig, instance._goldenLayout); + instance._goldenLayout.root.contentItems[0].addChild(docconfig); + docconfig.callDownwards('_$init'); + instance._goldenLayout._$maximiseItem(docconfig); + instance._goldenLayout.emit('stateChanged'); + instance._ignoreStateChange = JSON.stringify(instance._goldenLayout.toConfig()); + instance.stateChanged(); + return true; } - @action - layoutChanged(removed?: Doc) { - this._goldenLayout.root.callDownwards('setSize', [this._goldenLayout.width, this._goldenLayout.height]); - this._goldenLayout.emit('stateChanged'); - this._ignoreStateChange = JSON.stringify(this._goldenLayout.toConfig()); - this.stateChanged(); - } @undoBatch - @action - public static ReplaceRightSplit(document: Doc, libraryPath?: Doc[], addToSplit?: boolean): boolean { - if (!CollectionDockingView.Instance) return false; + public static ReplaceTab(document: Doc, panelName: string, stack: any, addToSplit?: boolean): boolean { const instance = CollectionDockingView.Instance; - let retVal = false; - if (instance._goldenLayout.root.contentItems[0].isRow) { - retVal = Array.from(instance._goldenLayout.root.contentItems[0].contentItems).some((child: any) => { - if (child.contentItems.length === 1 && child.contentItems[0].config.component === "DocumentFrameRenderer" && - DocumentManager.Instance.getDocumentViewById(child.contentItems[0].config.props.documentId)?.Document.isDisplayPanel) { - const newItemStackConfig = CollectionDockingView.makeDocumentConfig(document, undefined, libraryPath); - child.addChild(newItemStackConfig, undefined); - !addToSplit && child.contentItems[0].remove(); - instance.layoutChanged(document); - return true; - } - return Array.from(child.contentItems).filter((tab: any) => tab.config.component === "DocumentFrameRenderer").some((tab: any, j: number) => { - if (DocumentManager.Instance.getDocumentViewById(tab.config.props.documentId)?.Document.isDisplayPanel) { - const newItemStackConfig = CollectionDockingView.makeDocumentConfig(document, undefined, libraryPath); - child.addChild(newItemStackConfig, undefined); - !addToSplit && child.contentItems[j].remove(); - instance.layoutChanged(document); - return true; - } - return false; - }); - }); + if (!instance) return false; + const newConfig = CollectionDockingView.makeDocumentConfig(document, panelName); + if (!panelName && stack) { + const activeContentItemIndex = stack.contentItems.findIndex((item: any) => item.config === stack._activeContentItem.config); + const newContentItem = stack.layoutManager.createContentItem(newConfig, instance._goldenLayout); + stack.addChild(newContentItem.contentItems[0], undefined); + stack.contentItems[activeContentItemIndex].remove(); + return CollectionDockingView.Instance.layoutChanged(); } - if (retVal) { - instance.stateChanged(); + const tab = Array.from(CollectionDockingView.Instance.tabMap.keys()).find((tab) => tab.contentItem.config.props.panelName === panelName); + if (tab) { + tab.header.parent.addChild(newConfig, undefined); + const j = tab.header.parent.contentItems.indexOf(tab.contentItem); + !addToSplit && j !== -1 && tab.header.parent.contentItems[j].remove(); + return CollectionDockingView.Instance.layoutChanged(); } - return retVal; + return CollectionDockingView.AddSplit(document, panelName, stack, panelName); } - // - // Creates a vertical split on the right side of the docking view, and then adds the Document to the right of that split - // - @undoBatch - @action - public static AddRightSplit(document: Doc, libraryPath?: Doc[]) { - if (!CollectionDockingView.Instance) return false; - if (document._viewType === CollectionViewType.Docking) return CurrentUserUtils.openDashboard(Doc.UserDoc(), document); - const instance = CollectionDockingView.Instance; - const newItemStackConfig = { - type: 'stack', - content: [CollectionDockingView.makeDocumentConfig(document, undefined, libraryPath)] - }; - - const newContentItem = instance._goldenLayout.root.layoutManager.createContentItem(newItemStackConfig, instance._goldenLayout); - - if (instance._goldenLayout.root.contentItems.length === 0) { - instance._goldenLayout.root.addChild(newContentItem); - } else if (instance._goldenLayout.root.contentItems[0].isRow) { - instance._goldenLayout.root.contentItems[0].addChild(newContentItem); - } else { - const collayout = instance._goldenLayout.root.contentItems[0]; - const newRow = collayout.layoutManager.createContentItem({ type: "row" }, instance._goldenLayout); - collayout.parent.replaceChild(collayout, newRow); - - newRow.addChild(newContentItem, undefined, true); - newRow.addChild(collayout, 0, true); - collayout.config.width = 50; - newContentItem.config.width = 50; - } - newContentItem.callDownwards('_$init'); - instance.layoutChanged(); - return true; + @undoBatch + public static ToggleSplit(doc: Doc, location: string, stack?: any, panelName?: string) { + return Array.from(CollectionDockingView.Instance.tabMap.keys()).findIndex((tab) => tab.DashDoc === doc) !== -1 ? + CollectionDockingView.CloseSplit(doc) : CollectionDockingView.AddSplit(doc, location, stack, panelName); } - // // Creates a split on any side of the docking view based on the passed input pullSide and then adds the Document to the requested side // @undoBatch - @action - public static AddSplit(document: Doc, pullSide: string, libraryPath?: Doc[]) { - if (!CollectionDockingView.Instance) return false; + public static AddSplit(document: Doc, pullSide: string, stack?: any, panelName?: string) { const instance = CollectionDockingView.Instance; - const newItemStackConfig = { - type: 'stack', - content: [CollectionDockingView.makeDocumentConfig(document, undefined, libraryPath)] - }; + if (!instance) return false; + const docContentConfig = CollectionDockingView.makeDocumentConfig(document, panelName); - const newContentItem = instance._goldenLayout.root.layoutManager.createContentItem(newItemStackConfig, instance._goldenLayout); - - if (instance._goldenLayout.root.contentItems.length === 0) { // if no rows / columns - instance._goldenLayout.root.addChild(newContentItem); - } else if (instance._goldenLayout.root.contentItems[0].isRow) { // if row - if (pullSide === "left") { - instance._goldenLayout.root.contentItems[0].addChild(newContentItem, 0); - } else if (pullSide === "right") { - instance._goldenLayout.root.contentItems[0].addChild(newContentItem); - } else if (pullSide === "top" || pullSide === "bottom") { - // if not going in a row layout, must add already existing content into column - const rowlayout = instance._goldenLayout.root.contentItems[0]; - const newColumn = rowlayout.layoutManager.createContentItem({ type: "column" }, instance._goldenLayout); - rowlayout.parent.replaceChild(rowlayout, newColumn); - if (pullSide === "top") { - newColumn.addChild(rowlayout, undefined, true); - newColumn.addChild(newContentItem, 0, true); - } else if (pullSide === "bottom") { - newColumn.addChild(newContentItem, undefined, true); - newColumn.addChild(rowlayout, 0, true); - } + if (!pullSide && stack) { + stack.addChild(docContentConfig, undefined); + } else { + const newItemStackConfig = { type: 'stack', content: [docContentConfig] }; + const newContentItem = instance._goldenLayout.root.layoutManager.createContentItem(newItemStackConfig, instance._goldenLayout); + if (instance._goldenLayout.root.contentItems.length === 0) { // if no rows / columns + instance._goldenLayout.root.addChild(newContentItem); + } else if (instance._goldenLayout.root.contentItems[0].isRow) { // if row + switch (pullSide) { + default: + case "right": instance._goldenLayout.root.contentItems[0].addChild(newContentItem); break; + case "left": instance._goldenLayout.root.contentItems[0].addChild(newContentItem, 0); break; + case "top": + case "bottom": + // if not going in a row layout, must add already existing content into column + const rowlayout = instance._goldenLayout.root.contentItems[0]; + const newColumn = rowlayout.layoutManager.createContentItem({ type: "column" }, instance._goldenLayout); + rowlayout.parent.replaceChild(rowlayout, newColumn); + if (pullSide === "top") { + newColumn.addChild(rowlayout, undefined, true); + newColumn.addChild(newContentItem, 0, true); + } else if (pullSide === "bottom") { + newColumn.addChild(newContentItem, undefined, true); + newColumn.addChild(rowlayout, 0, true); + } - rowlayout.config.height = 50; - newContentItem.config.height = 50; - } - } else if (instance._goldenLayout.root.contentItems[0].isColumn) { // if column - if (pullSide === "top") { - instance._goldenLayout.root.contentItems[0].addChild(newContentItem, 0); - } else if (pullSide === "bottom") { - instance._goldenLayout.root.contentItems[0].addChild(newContentItem); - } else if (pullSide === "left" || pullSide === "right") { - // if not going in a row layout, must add already existing content into column - const collayout = instance._goldenLayout.root.contentItems[0]; - const newRow = collayout.layoutManager.createContentItem({ type: "row" }, instance._goldenLayout); - collayout.parent.replaceChild(collayout, newRow); - - if (pullSide === "left") { - newRow.addChild(collayout, undefined, true); - newRow.addChild(newContentItem, 0, true); - } else if (pullSide === "right") { - newRow.addChild(newContentItem, undefined, true); - newRow.addChild(collayout, 0, true); + rowlayout.config.height = 50; + newContentItem.config.height = 50; } + } else if (instance._goldenLayout.root.contentItems[0].isColumn) { // if column + switch (pullSide) { + case "top": instance._goldenLayout.root.contentItems[0].addChild(newContentItem, 0); break; + case "bottom": instance._goldenLayout.root.contentItems[0].addChild(newContentItem); break; + case "left": + case "right": + default: + // if not going in a row layout, must add already existing content into column + const collayout = instance._goldenLayout.root.contentItems[0]; + const newRow = collayout.layoutManager.createContentItem({ type: "row" }, instance._goldenLayout); + collayout.parent.replaceChild(collayout, newRow); + + if (pullSide === "left") { + newRow.addChild(collayout, undefined, true); + newRow.addChild(newContentItem, 0, true); + } else { + newRow.addChild(newContentItem, undefined, true); + newRow.addChild(collayout, 0, true); + } - collayout.config.width = 50; - newContentItem.config.width = 50; - } - } - - newContentItem.callDownwards('_$init'); - instance.layoutChanged(); - return true; - } - - - // - // Creates a vertical split on the right side of the docking view, and then adds the Document to that split - // - @undoBatch - @action - public static UseRightSplit(document: Doc, libraryPath?: Doc[], shiftKey?: boolean) { - document.isDisplayPanel = true; - if (shiftKey || !CollectionDockingView.ReplaceRightSplit(document, libraryPath, shiftKey)) { - CollectionDockingView.AddRightSplit(document, libraryPath); - } - } - - @undoBatch - @action - public AddTab = (stack: any, document: Doc, libraryPath?: Doc[]) => { - Doc.GetProto(document).lastOpened = new DateField; - const docContentConfig = CollectionDockingView.makeDocumentConfig(document, undefined, libraryPath); - if (stack === undefined) { - let stack: any = this._goldenLayout.root; - while (!stack.isStack) { - if (stack.contentItems.length) { - stack = stack.contentItems[0]; - } else { - stack.addChild({ type: 'stack', content: [docContentConfig] }); - stack = undefined; - break; + collayout.config.width = 50; + newContentItem.config.width = 50; } } - if (stack) { - stack.addChild(docContentConfig); - } - } else { - stack.addChild(docContentConfig, undefined); + newContentItem.callDownwards('_$init'); } - this.layoutChanged(); - return true; + + return instance.layoutChanged(); } @undoBatch @action - public ReplaceTab = (stack: any, document: Doc, libraryPath?: Doc[]) => { - Doc.GetProto(document).lastOpened = new DateField; - const docContentConfig = CollectionDockingView.makeDocumentConfig(document, undefined, libraryPath); - if (stack === undefined) { - let stack: any = this._goldenLayout.root; - while (!stack.isStack) { - if (stack.contentItems.length) { - stack = stack.contentItems[0]; - } else { - stack.addChild({ type: 'stack', content: [docContentConfig] }); - stack = undefined; - break; - } - } - if (stack) { - stack.addChild(docContentConfig); - } - } else { - stack.addChild(docContentConfig, undefined); - } - this.layoutChanged(); + layoutChanged() { + this._goldenLayout.root.callDownwards('setSize', [this._goldenLayout.width, this._goldenLayout.height]); + this._goldenLayout.emit('stateChanged'); + this._ignoreStateChange = JSON.stringify(this._goldenLayout.toConfig()); + this.stateChanged(); return true; } - setupGoldenLayout() { + async setupGoldenLayout() { const config = StrCast(this.props.Document.dockingConfig); if (config) { - if (!this._goldenLayout) { - runInAction(() => this._goldenLayout = new GoldenLayout(JSON.parse(config))); - } - else { + const matches = config.match(/\"documentId\":\"[a-z0-9-]+\"/g); + const docids = matches?.map(m => m.replace("\"documentId\":\"", "").replace("\"", "")) ?? []; + await Promise.all(docids.map(id => DocServer.GetRefField(id))); + + if (this._goldenLayout) { if (config === JSON.stringify(this._goldenLayout.toConfig())) { return; + } else { + try { + this._goldenLayout.unbind('tabCreated', this.tabCreated); + this._goldenLayout.unbind('tabDestroyed', this.tabDestroyed); + this._goldenLayout.unbind('stackCreated', this.stackCreated); + } catch (e) { } } - try { - this._goldenLayout.unbind('itemDropped', this.itemDropped); - this._goldenLayout.unbind('tabCreated', this.tabCreated); - this._goldenLayout.unbind('tabDestroyed', this.tabDestroyed); - this._goldenLayout.unbind('stackCreated', this.stackCreated); - } catch (e) { } - this._goldenLayout.destroy(); - runInAction(() => this._goldenLayout = new GoldenLayout(JSON.parse(config))); } - this._goldenLayout.on('itemDropped', this.itemDropped); + this.tabMap.clear(); + this._goldenLayout?.destroy(); + runInAction(() => this._goldenLayout = new GoldenLayout(JSON.parse(config))); this._goldenLayout.on('tabCreated', this.tabCreated); this._goldenLayout.on('tabDestroyed', this.tabDestroyed); this._goldenLayout.on('stackCreated', this.stackCreated); - this._goldenLayout.registerComponent('DocumentFrameRenderer', DockedFrameRenderer); + this._goldenLayout.registerComponent('DocumentFrameRenderer', TabDocView); this._goldenLayout.container = this._containerRef.current; if (this._goldenLayout.config.maximisedItemId === '__glMaximised') { try { @@ -385,50 +249,33 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) { this._goldenLayout.init(); } } - reactionDisposer?: Lambda; + componentDidMount: () => void = () => { if (this._containerRef.current) { - const observer = new _global.ResizeObserver(action((entries: any) => { - for (const entry of entries) { - this.onResize(null as any); - } - })); - observer.observe(this._containerRef.current); - this.reactionDisposer = reaction( - () => this.props.Document.dockingConfig, - () => { - if (!this._goldenLayout || this._ignoreStateChange !== JSON.stringify(this._goldenLayout.toConfig())) { - // Because this is in a set timeout, if this component unmounts right after mounting, - // we will leak a GoldenLayout, because we try to destroy it before we ever create it - setTimeout(() => this.setupGoldenLayout(), 1); - DocListCast((Doc.UserDoc().myDashboards as Doc).data).map(d => d.dashboardBrush = false); - this.props.Document.dashboardBrush = true; + new _global.ResizeObserver(this.onResize).observe(this._containerRef.current); + this._reactionDisposer = reaction(() => StrCast(this.props.Document.dockingConfig), + config => { + if (!this._goldenLayout || this._ignoreStateChange !== config) { + this.setupGoldenLayout(); } this._ignoreStateChange = ""; - }, { fireImmediately: true }); - + }); + setTimeout(() => this.setupGoldenLayout(), 0); window.addEventListener('resize', this.onResize); // bcz: would rather add this event to the parent node, but resize events only come from Window } } + componentWillUnmount: () => void = () => { try { - this.props.Document.dashboardBrush = false; - this._goldenLayout.unbind('itemDropped', this.itemDropped); - this._goldenLayout.unbind('tabCreated', this.tabCreated); this._goldenLayout.unbind('stackCreated', this.stackCreated); this._goldenLayout.unbind('tabDestroyed', this.tabDestroyed); - } catch (e) { - - } - this._goldenLayout && this._goldenLayout.destroy(); - runInAction(() => { - CollectionDockingView.Instances.splice(CollectionDockingView.Instances.indexOf(this), 1); - this._goldenLayout = null; - }); + } catch (e) { } + this._goldenLayout?.destroy(); window.removeEventListener('resize', this.onResize); - this.reactionDisposer && this.reactionDisposer(); + this._reactionDisposer?.(); } + @action onResize = (event: any) => { const cur = this._containerRef.current; @@ -437,56 +284,35 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) { } @action - onPointerUp = (e: React.PointerEvent): void => { + onPointerUp = (e: MouseEvent): void => { + window.removeEventListener("pointerup", this.onPointerUp); if (this._flush) { - this._flush = false; setTimeout(() => { CollectionDockingView.Instance._ignoreStateChange = JSON.stringify(CollectionDockingView.Instance._goldenLayout.toConfig()); this.stateChanged(); + this._flush!.end(); + this._flush = undefined; }, 10); } } + @action onPointerDown = (e: React.PointerEvent): void => { - this._isPointerDown = true; - const onPointerUp = action(() => { - window.removeEventListener("pointerup", onPointerUp); - this._isPointerDown = false; - }); - window.addEventListener("pointerup", onPointerUp); - const className = (e.target as any).className; - if (className === "lm_drag_handle" || className === "lm_close" || className === "lm_maximise" || className === "lm_minimise" || className === "lm_close_tab") { - this._flush = true; + window.addEventListener("mouseup", this.onPointerUp); + if (!(e.target as HTMLElement).closest("*.lm_content") && ((e.target as HTMLElement).closest("*.lm_tab") || (e.target as HTMLElement).closest("*.lm_stack"))) { + this._flush = UndoManager.StartBatch("golden layout edit"); } - if (e.nativeEvent.cancelBubble || InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE) || InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || (Doc.GetSelectedTool() === InkTool.Highlighter || Doc.GetSelectedTool() === InkTool.Pen)) { - return; - } else { + if (!e.nativeEvent.cancelBubble && !InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE) && !InteractionUtils.IsType(e, InteractionUtils.PENTYPE) && + Doc.GetSelectedTool() !== InkTool.Highlighter && Doc.GetSelectedTool() !== InkTool.Pen) { e.stopPropagation(); } } - updateDataField = async (json: string) => { - const matches = json.match(/\"documentId\":\"[a-z0-9-]+\"/g); - const docids = matches?.map(m => m.replace("\"documentId\":\"", "").replace("\"", "")); - - const docs = !docids ? [] : (await Promise.all(docids.map(id => DocServer.GetRefField(id)))).filter(f => f).map(f => f as Doc); - const sublists = DocListCast(this.props.Document[this.props.fieldKey]); - const tabs = Cast(sublists[0], Doc, null); - const other = Cast(sublists[1], Doc, null); - const tabdocs = DocListCast(tabs.data); - const otherdocs = DocListCast(other.data); - Doc.GetProto(tabs).data = new List<Doc>(docs); - const otherSet = new Set<Doc>(); - otherdocs.filter(doc => !docs.includes(doc)).forEach(doc => otherSet.add(doc)); - tabdocs.filter(doc => !docs.includes(doc)).forEach(doc => otherSet.add(doc)); - Doc.GetProto(other).data = new List<Doc>(Array.from(otherSet.values())); - } - - public static async Copy(doc: Doc) { + public static Copy(doc: Doc) { let json = StrCast(doc.dockingConfig); const matches = json.match(/\"documentId\":\"[a-z0-9-]+\"/g); const docids = matches?.map(m => m.replace("\"documentId\":\"", "").replace("\"", "")) || []; - const docs = (await Promise.all(docids.map(id => DocServer.GetRefField(id)))).filter(f => f).map(f => f as Doc); + const docs = docids.map(id => DocServer.GetCachedRefField(id)).filter(f => f).map(f => f as Doc); const newtabs = docs.map(doc => { const copy = Doc.MakeAlias(doc); json = json.replace(doc[Id], copy[Id]); @@ -503,511 +329,72 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) { return copy; } - @undoBatch + @action stateChanged = () => { const json = JSON.stringify(this._goldenLayout.toConfig()); - this.props.Document.dockingConfig = json; - this.updateDataField(json); - } - - itemDropped = () => { - CollectionDockingView.Instance._ignoreStateChange = JSON.stringify(CollectionDockingView.Instance._goldenLayout.toConfig()); - this.stateChanged(); - } - - htmlToElement(html: string) { - const template = document.createElement('template'); - html = html.trim(); // Never return a text node of whitespace as the result - template.innerHTML = html; - return template.content.firstChild; - } - - tabCreated = async (tab: any) => { - tab.titleElement[0].Tab = tab; - if (tab.hasOwnProperty("contentItem") && tab.contentItem.config.type !== "stack") { - if (tab.contentItem.config.fixed) { - tab.contentItem.parent.config.fixed = true; - } + const matches = json.match(/\"documentId\":\"[a-z0-9-]+\"/g); + const docids = matches?.map(m => m.replace("\"documentId\":\"", "").replace("\"", "")); + const docs = !docids ? [] : docids.map(id => DocServer.GetCachedRefField(id)).filter(f => f).map(f => f as Doc); - const doc = await DocServer.GetRefField(tab.contentItem.config.props.documentId) as Doc; - if (doc instanceof Doc) { - tab.titleElement[0].onclick = (e: any) => { - if (Date.now() - tab.titleElement[0].lastClick < 1000) tab.titleElement[0].select(); - tab.titleElement[0].lastClick = Date.now(); - tab.titleElement[0].focus(); - }; - tab.titleElement[0].onchange = (e: any) => { - tab.titleElement[0].size = e.currentTarget.value.length + 1; - Doc.GetProto(doc).title = e.currentTarget.value, true; - }; - tab.titleElement[0].size = StrCast(doc.title).length + 1; - tab.titleElement[0].value = doc.title; - tab.titleElement[0].style["max-width"] = "100px"; - const gearSpan = document.createElement("span"); - gearSpan.className = "collectionDockingView-gear"; - gearSpan.style.position = "relative"; - gearSpan.style.paddingLeft = "0px"; - gearSpan.style.paddingRight = "12px"; - const stack = tab.contentItem.parent; - tab.element[0].onpointerdown = (e: any) => { - const view = DocumentManager.Instance.getDocumentView(doc); - view && SelectionManager.SelectDoc(view, false); - }; - // shifts the focus to this tab when another tab is dragged over it - tab.element[0].onmouseenter = (e: any) => { - if (!this._isPointerDown || !SnappingManager.GetIsDragging()) return; - const activeContentItem = tab.header.parent.getActiveContentItem(); - if (tab.contentItem !== activeContentItem) { - tab.header.parent.setActiveContentItem(tab.contentItem); - } - tab.setActive(true); - }; - const onDown = (e: React.PointerEvent) => { - setupMoveUpEvents(this, e, (e) => { - if (!(e as any).defaultPrevented) { - const dragData = new DragManager.DocumentDragData([doc]); - dragData.dropAction = doc.dropAction as dropActionType; - DragManager.StartDocumentDrag([gearSpan], dragData, e.clientX, e.clientY); - return true; - } - return false; - }, returnFalse, emptyFunction); - }; - - tab.selectionDisposer = reaction(() => SelectionManager.SelectedDocuments(), - (sel) => { - const selected = sel.some(v => v.props.Document === doc); - selected && tab.contentItem !== tab.header.parent.getActiveContentItem() && tab.header.parent.setActiveContentItem(tab.contentItem); - } - ); - tab.buttonDisposer = reaction(() => ((view: Opt<DocumentView>) => view ? [view] : [])(DocumentManager.Instance.getDocumentView(doc)), - (views) => { - if (views.length) { - ReactDOM.render(<span title="Drag as document" className="collectionDockingView-dragAsDocument" onPointerDown={onDown} > - <DockingViewButtonSelector views={() => views} Stack={stack} /> - </span>, - gearSpan); - tab.buttonDisposer?.(); - } - }, { fireImmediately: true }); - - tab.reactComponents = [gearSpan]; - tab.element.append(gearSpan); - tab.reactionDisposer = reaction(() => ({ title: doc.title, degree: Doc.IsBrushedDegree(doc) }), ({ title, degree }) => { - tab.titleElement[0].textContent = title, { fireImmediately: true }; - tab.titleElement[0].style.padding = degree ? 0 : 2; - tab.titleElement[0].style.border = `${["gray", "gray", "gray"][degree]} ${["none", "dashed", "solid"][degree]} 2px`; - }); - //TODO why can't this just be doc instead of the id? - tab.titleElement[0].DashDocId = tab.contentItem.config.props.documentId; - } - } - tab.closeElement.off('click') //unbind the current click handler - .click(async function () { - tab.selectionDisposer?.(); - tab.reactionDisposer?.(); - tab.buttonDisposer?.(); - const doc = await DocServer.GetRefField(tab.contentItem.config.props.documentId); - if (doc instanceof Doc) { - const theDoc = doc; - - const recent = await Cast(Doc.UserDoc().myRecentlyClosedDocs, Doc); - if (recent) { - Doc.AddDocToList(recent, "data", doc, undefined, true, true); - } - SelectionManager.DeselectAll(); - } - CollectionDockingView.Instance._ignoreStateChange = JSON.stringify(CollectionDockingView.Instance._goldenLayout.toConfig()); - tab.contentItem.remove(); - CollectionDockingView.Instance._ignoreStateChange = JSON.stringify(CollectionDockingView.Instance._goldenLayout.toConfig()); - }); + this.props.Document.dockingConfig = json; + const sublists = DocListCast(this.props.Document[this.props.fieldKey]); + const tabs = Cast(sublists[0], Doc, null); + const other = Cast(sublists[1], Doc, null); + const tabdocs = DocListCast(tabs.data); + const otherdocs = DocListCast(other.data); + Doc.GetProto(tabs).data = new List<Doc>(docs); + const otherSet = new Set<Doc>(); + otherdocs.filter(doc => !docs.includes(doc)).forEach(doc => otherSet.add(doc)); + tabdocs.filter(doc => !docs.includes(doc)).forEach(doc => otherSet.add(doc)); + Doc.GetProto(other).data = new List<Doc>(Array.from(otherSet.values())); } tabDestroyed = (tab: any) => { - if (tab.reactComponents) { - for (const ele of tab.reactComponents) { - ReactDOM.unmountComponentAtNode(ele); - } - } + this.tabMap.delete(tab); + Object.values(tab._disposers).forEach((disposer: any) => disposer?.()); + tab.reactComponents?.forEach((ele: any) => ReactDOM.unmountComponentAtNode(ele)); } - - stackActiveChanged = () => { - try { - CollectionDockingView.Instance._ignoreStateChange = JSON.stringify(CollectionDockingView.Instance._goldenLayout.toConfig()); - this.stateChanged(); - } catch (e) { } // catch exception thrown when config has not been initialzed yet + tabCreated = (tab: any) => { + tab.contentItem.element[0]?.firstChild?.firstChild?.InitTab?.(tab); // have to explicitly initialize tabs that reuse contents from previous abs (ie, when dragging a tab around a new tab is created for the old content) } stackCreated = (stack: any) => { - stack.layoutManager.on("activeContentItemChanged", this.stackActiveChanged); - //stack.header.controlsContainer.find('.lm_popout').hide(); stack.header.element.on('mousedown', (e: any) => { if (e.target === stack.header.element[0] && e.button === 2) { - const emptyPane = Cast(Doc.UserDoc().emptyPane, Doc, null); + const emptyPane = CurrentUserUtils.EmptyPane; emptyPane["dragFactory-count"] = NumCast(emptyPane["dragFactory-count"]) + 1; - this.AddTab(stack, Docs.Create.FreeformDocument([], { + CollectionDockingView.AddSplit(Docs.Create.FreeformDocument([], { _width: this.props.PanelWidth(), _height: this.props.PanelHeight(), title: `Untitled Tab ${NumCast(emptyPane["dragFactory-count"])}` - })); + }), "", stack); } }); - // starter code for bezel to add new pane - // stack.element.on("touchstart", (e: TouchEvent) => { - // if (e.targetTouches.length === 2) { - // let pt1 = e.targetTouches.item(0); - // let pt2 = e.targetTouches.item(1); - // let threshold = 40 * window.devicePixelRatio; - // if (pt1 && pt2 && InteractionUtils.TwoPointEuclidist(pt1, pt2) < threshold) { - // let edgeThreshold = 30 * window.devicePixelRatio; - // let center = InteractionUtils.CenterPoint([pt1, pt2]); - // let stackRect: DOMRect = stack.element.getBoundingClientRect(); - // let nearLeft = center.X - stackRect.x < edgeThreshold; - // let nearTop = center.Y - stackRect.y < edgeThreshold; - // let nearRight = stackRect.right - center.X < edgeThreshold; - // let nearBottom = stackRect.bottom - center.Y < edgeThreshold; - // let ns = [nearLeft, nearTop, nearRight, nearBottom].filter(n => n); - // if (ns.length === 1) { - - // } - // } - // } - // }); stack.header.controlsContainer.find('.lm_close') //get the close icon .off('click') //unbind the current click handler - .click(action(async () => { + .click(action(() => { //if (confirm('really close this?')) { - stack.remove(); - stack.contentItems.forEach(async (contentItem: any) => { - const doc = await DocServer.GetRefField(contentItem.config.props.documentId); - if (doc instanceof Doc) { - let recent: Doc | undefined; - if (recent = await Cast(Doc.UserDoc().myRecentlyClosedDocs, Doc)) { - Doc.AddDocToList(recent, "data", doc, undefined, true, true); - } - const theDoc = doc; - } - }); - //} + stack.contentItems.forEach((contentItem: any) => Doc.AddDocToList(CurrentUserUtils.MyRecentlyClosed, "data", contentItem.tab.DashDoc, undefined, true, true)); })); stack.header.controlsContainer.find('.lm_popout') //get the close icon .off('click') //unbind the current click handler .click(action(() => { // stack.config.fixed = !stack.config.fixed; // force the stack to have a fixed size - - const emptyPane = Cast(Doc.UserDoc().emptyPane, Doc, null); + const emptyPane = CurrentUserUtils.EmptyPane; emptyPane["dragFactory-count"] = NumCast(emptyPane["dragFactory-count"]) + 1; - this.AddTab(stack, Docs.Create.FreeformDocument([], { + CollectionDockingView.AddSplit(Docs.Create.FreeformDocument([], { _width: this.props.PanelWidth(), _height: this.props.PanelHeight(), title: `Untitled Tab ${NumCast(emptyPane["dragFactory-count"])}` - })); - - // const url = Utils.prepend("/doc/" + stack.contentItems[0].tab.contentItem.config.props.documentId); - // let win = window.open(url, stack.contentItems[0].tab.title, "width=300,height=400"); + }), "", stack); })); } render() { - if (this.props.renderDepth > 0) { - return <div style={{ width: "100%", height: "100%" }}>Nested dashboards can't be rendered</div>; - } - return <div className="collectiondockingview-container" id="menuContainer" - onPointerDown={this.onPointerDown} onPointerUp={this.onPointerUp} ref={this._containerRef} />; - } - -} - -interface DockedFrameProps { - documentId: FieldId; - glContainer: any; - libraryPath: (FieldId[]); - //collectionDockingView: CollectionDockingView -} -@observer -export class DockedFrameRenderer extends React.Component<DockedFrameProps> { - _mainCont: HTMLDivElement | null = null; - @observable private _libraryPath: Doc[] = []; - @observable private _panelWidth = 0; - @observable private _panelHeight = 0; - @observable private _document: Opt<Doc>; - @observable private _isActive: boolean = false; - _tabReaction: IReactionDisposer | undefined; - - get _stack(): any { - return (this.props as any).glContainer.parent.parent; - } - get _tab(): any { - const tab = (this.props as any).glContainer.tab?.element[0] as HTMLElement; - return tab?.getElementsByClassName("lm_title")?.[0]; - } - constructor(props: any) { - super(props); - DocServer.GetRefField(this.props.documentId).then(action((f: Opt<Field>) => this._document = f as Doc)); - this.props.libraryPath && this.setupLibraryPath(); - } - - async setupLibraryPath() { - Promise.all(this.props.libraryPath.map(async docid => { - const d = await DocServer.GetRefField(docid); - return d instanceof Doc ? d : undefined; - })).then(action((list: (Doc | undefined)[]) => this._libraryPath = list.filter(d => d).map(d => d as Doc))); - } - - /** - * Adds a document to the presentation view - **/ - @undoBatch - @action - public static PinDoc(doc: Doc, unpin = false) { - if (unpin) DockedFrameRenderer.UnpinDoc(doc); - else { - //add this new doc to props.Document - const curPres = Cast(Doc.UserDoc().activePresentation, Doc) as Doc; - if (curPres) { - const pinDoc = Doc.MakeAlias(doc); - pinDoc.presentationTargetDoc = doc; - pinDoc.presZoomButton = true; - pinDoc.context = curPres; - Doc.AddDocToList(curPres, "data", pinDoc); - if (curPres.expandBoolean) pinDoc.presExpandInlineButton = true; - if (!DocumentManager.Instance.getDocumentView(curPres)) { - CollectionDockingView.AddRightSplit(curPres); - } - DocumentManager.Instance.jumpToDocument(doc, false, undefined, Cast(doc.context, Doc, null)); - } - } - } - /** - * Adds a document to the presentation view - **/ - @undoBatch - @action - public static UnpinDoc(doc: Doc) { - //add this new doc to props.Document - const curPres = Cast(Doc.UserDoc().activePresentation, Doc) as Doc; - if (curPres) { - const ind = DocListCast(curPres.data).findIndex((val) => Doc.AreProtosEqual(val, doc)); - ind !== -1 && Doc.RemoveDocFromList(curPres, "data", DocListCast(curPres.data)[ind]); - } - } - - componentDidMount() { - const color = () => StrCast(this._document?._backgroundColor, this._document && CollectionDockingView.Instance?.props.backgroundColor?.(this._document, 0) || "white"); - const selected = () => SelectionManager.SelectedDocuments().some(v => Doc.AreProtosEqual(v.props.Document, this._document)); - const updateTabColor = () => this._tab && (this._tab.style.backgroundColor = selected() ? color() : ""); - const observer = new _global.ResizeObserver(action((entries: any) => { - for (const entry of entries) { - this._panelWidth = entry.contentRect.width; - this._panelHeight = entry.contentRect.height; - } - updateTabColor(); - })); - observer.observe(this.props.glContainer._element[0]); - this.props.glContainer.layoutManager.on("activeContentItemChanged", this.onActiveContentItemChanged); - this.props.glContainer.tab?.isActive && this.onActiveContentItemChanged(); - this._tabReaction = reaction(() => ({ views: SelectionManager.SelectedDocuments(), color: color() }), updateTabColor, { fireImmediately: true }); - } - - componentWillUnmount() { - this._tabReaction?.(); - this.props.glContainer.layoutManager.off("activeContentItemChanged", this.onActiveContentItemChanged); - } - - @action.bound - private onActiveContentItemChanged() { - if (this.props.glContainer.tab && this._isActive !== this.props.glContainer.tab.isActive) { - this._isActive = this.props.glContainer.tab.isActive; - this._isActive && setTimeout(() => { - const dv = this._document && DocumentManager.Instance.getFirstDocumentView(this._document); - dv && SelectionManager.SelectDoc(dv, false); - }); - !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. - } - } - - get layoutDoc() { return this._document && Doc.Layout(this._document); } - nativeAspect = () => this.nativeWidth() ? this.nativeWidth() / this.nativeHeight() : 0; - panelWidth = () => this.layoutDoc?.maxWidth ? Math.min(Math.max(NumCast(this.layoutDoc._width), NumCast(this.layoutDoc._nativeWidth)), this._panelWidth) : - (this.nativeAspect() && this.nativeAspect() < this._panelWidth / this._panelHeight ? this._panelHeight * this.nativeAspect() : this._panelWidth) - panelHeight = () => this.nativeAspect() && this.nativeAspect() > this._panelWidth / this._panelHeight ? this._panelWidth / this.nativeAspect() : this._panelHeight; - - nativeWidth = () => !this.layoutDoc!._fitWidth ? NumCast(this.layoutDoc!._nativeWidth) || this._panelWidth : 0; - nativeHeight = () => !this.layoutDoc!._fitWidth ? NumCast(this.layoutDoc!._nativeHeight) || this._panelHeight : 0; - - contentScaling = () => { - const nativeH = this.nativeHeight(); - const nativeW = this.nativeWidth(); - let scaling = 1; - if (!this.layoutDoc?._fitWidth && (!nativeW || !nativeH)) { - scaling = 1; - } else if (NumCast(this.layoutDoc!._nativeWidth) && ((this.layoutDoc?._fitWidth) || - this._panelHeight / NumCast(this.layoutDoc!._nativeHeight) > this._panelWidth / NumCast(this.layoutDoc!._nativeWidth))) { - scaling = this._panelWidth / NumCast(this.layoutDoc!._nativeWidth); - } else if (nativeW && nativeH) { - // if (this.layoutDoc!.type === DocumentType.PDF || this.layoutDoc!.type === DocumentType.WEB) { - // if ((this.layoutDoc?._fitWidth) || - // this._panelHeight / NumCast(this.layoutDoc!._nativeHeight) > this._panelWidth / NumCast(this.layoutDoc!._nativeWidth)) { - // return this._panelWidth / NumCast(this.layoutDoc!._nativeWidth); - // } else { - // return this._panelHeight / NumCast(this.layoutDoc!._nativeHeight); - // } - // } - const wscale = this.panelWidth() / nativeW; - scaling = wscale * nativeH > this._panelHeight ? this._panelHeight / nativeH : wscale; - } else scaling = 1; - return scaling; - } - - ScreenToLocalTransform = () => { - if (this._mainCont && this._mainCont.children) { - const { translateX, translateY } = Utils.GetScreenTransform(this._mainCont.children[0].firstChild as HTMLElement); - const scale = Utils.GetScreenTransform(this._mainCont).scale; - return CollectionDockingView.Instance?.props.ScreenToLocalTransform().translate(-translateX, -translateY).scale(1 / this.contentScaling() / scale); - } - return Transform.Identity(); - } - get previewPanelCenteringOffset() { return this.nativeWidth() ? (this._panelWidth - this.nativeWidth() * this.contentScaling()) / 2 : 0; } - get widthpercent() { return this.nativeWidth() ? `${(this.nativeWidth() * this.contentScaling()) / this._panelWidth * 100}% ` : undefined; } - - addDocTab = (doc: Doc, location: string, libraryPath?: Doc[]) => { - SelectionManager.DeselectAll(); - if (doc._viewType === CollectionViewType.Docking) { - return CurrentUserUtils.openDashboard(Doc.UserDoc(), doc); - } else if (location === "onRight") { - return CollectionDockingView.AddRightSplit(doc, libraryPath); - } else if (location === "close") { - return CollectionDockingView.CloseRightSplit(doc); - } else if (location === "replace") { - CollectionDockingView.UseRightSplit(doc); - return true; - } else {// if (location === "inPlace") { - return CollectionDockingView.Instance.AddTab(this._stack, doc, libraryPath); - } - } - - @computed get renderContentBounds() { - 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 }; - } - @computed get miniLeft() { return 50 + (NumCast(this._document?._panX) - this.renderContentBounds.cx) / this.renderContentBounds.dim * 100 - this.miniWidth / 2; } - @computed get miniTop() { return 50 + (NumCast(this._document?._panY) - this.renderContentBounds.cy) / this.renderContentBounds.dim * 100 - this.miniHeight / 2; } - @computed get miniWidth() { return this.panelWidth() / NumCast(this._document?._viewScale, 1) / this.renderContentBounds.dim * 100; } - @computed get miniHeight() { return this.panelHeight() / NumCast(this._document?._viewScale, 1) / this.renderContentBounds.dim * 100; } - childLayoutTemplate = () => Cast(this._document?.childLayoutTemplate, Doc, null); - returnMiniSize = () => NumCast(this._document?._miniMapSize, 150); - miniDown = (e: React.PointerEvent) => { - setupMoveUpEvents(this, e, action((e: PointerEvent, down: number[], delta: number[]) => { - this._document!._panX = clamp(NumCast(this._document!._panX) + delta[0] / this.returnMiniSize() * this.renderContentBounds.dim, this.renderContentBounds.l, this.renderContentBounds.l + this.renderContentBounds.dim); - this._document!._panY = clamp(NumCast(this._document!._panY) + delta[1] / this.returnMiniSize() * this.renderContentBounds.dim, this.renderContentBounds.t, this.renderContentBounds.t + this.renderContentBounds.dim); - return false; - }), emptyFunction, emptyFunction); - } - getCurrentFrame = (): number => { - const presTargetDoc = Cast(PresBox.Instance.childDocs[PresBox.Instance.itemIndex].presentationTargetDoc, Doc, null); - return Cast(presTargetDoc._currentFrame, "number", null); - } - - renderMiniMap() { - return <div className="miniMap" style={{ - width: this.returnMiniSize(), height: this.returnMiniSize(), background: StrCast(this._document!._backgroundColor, - StrCast(this._document!.backgroundColor, CollectionDockingView.Instance.props.backgroundColor?.(this._document!, 0))), - }}> - <CollectionFreeFormView - Document={this._document!} - LibraryPath={emptyPath} - CollectionView={undefined} - ContainingCollectionView={undefined} - ContainingCollectionDoc={undefined} - ChildLayoutTemplate={this.childLayoutTemplate} // bcz: Ugh .. should probably be rendering a CollectionView or the minimap should be part of the collectionFreeFormView to avoid havin to set stuff like this. - noOverlay={true} // don't render overlay Docs since they won't scale - active={returnTrue} - select={emptyFunction} - dropAction={undefined} - isSelected={returnFalse} - dontRegisterView={true} - annotationsKey={""} - fieldKey={Doc.LayoutFieldKey(this._document!)} - bringToFront={emptyFunction} - rootSelected={returnTrue} - addDocument={returnFalse} - moveDocument={returnFalse} - removeDocument={returnFalse} - ContentScaling={returnOne} - PanelWidth={this.returnMiniSize} - PanelHeight={this.returnMiniSize} - NativeHeight={returnZero} - NativeWidth={returnZero} - ScreenToLocalTransform={this.ScreenToLocalTransform} - renderDepth={0} - whenActiveChanged={emptyFunction} - focus={emptyFunction} - backgroundColor={CollectionDockingView.Instance.props.backgroundColor} - addDocTab={this.addDocTab} - pinToPres={DockedFrameRenderer.PinDoc} - docFilters={CollectionDockingView.Instance.docFilters} - searchFilterDocs={CollectionDockingView.Instance.searchFilterDocs} - fitToBox={true} - /> - <div className="miniOverlay" onPointerDown={this.miniDown} > - <div className="miniThumb" style={{ - width: `${this.miniWidth}% `, - height: `${this.miniHeight}% `, - left: `${this.miniLeft}% `, - top: `${this.miniTop}% `, - }} - /> - </div> + return <div className="collectiondockingview-container" onPointerDown={this.onPointerDown} ref={this._containerRef}> + {this.props.renderDepth > 0 ? "Nested dashboards can't be rendered" : (null)} </div>; } - @computed get docView() { - TraceMobx(); - if (!this._document) return (null); - const document = this._document; - const resolvedDataDoc = !Doc.AreProtosEqual(this._document[DataSym], this._document) ? this._document[DataSym] : undefined; - return <> - <DocumentView key={document[Id]} - LibraryPath={this._libraryPath} - Document={document} - DataDoc={resolvedDataDoc} - bringToFront={emptyFunction} - rootSelected={returnTrue} - addDocument={undefined} - removeDocument={undefined} - ContentScaling={this.contentScaling} - PanelWidth={this.panelWidth} - PanelHeight={this.panelHeight} - NativeHeight={this.nativeHeight} - NativeWidth={this.nativeWidth} - ScreenToLocalTransform={this.ScreenToLocalTransform} - renderDepth={0} - parentActive={returnTrue} - whenActiveChanged={emptyFunction} - focus={emptyFunction} - backgroundColor={CollectionDockingView.Instance.props.backgroundColor} - addDocTab={this.addDocTab} - pinToPres={DockedFrameRenderer.PinDoc} - docFilters={CollectionDockingView.Instance.docFilters} - searchFilterDocs={CollectionDockingView.Instance.searchFilterDocs} - ContainingCollectionView={undefined} - ContainingCollectionDoc={undefined} /> - {document._viewType === CollectionViewType.Freeform && !this._document?.hideMinimap ? this.renderMiniMap() : (null)} - </>; - } - - render() { - return (!this._isActive || !this.layoutDoc) ? (null) : - (<div className="collectionDockingView-content" ref={ref => this._mainCont = ref} - style={{ - transform: `translate(${this.previewPanelCenteringOffset}px, 0px)`, - height: this.layoutDoc && this.layoutDoc._fitWidth ? undefined : "100%", - width: this.widthpercent - }}> - {this.docView} - </div >); - } } -Scripting.addGlobal(function openOnRight(doc: any) { CollectionDockingView.AddRightSplit(doc); }, + +Scripting.addGlobal(function openOnRight(doc: any) { CollectionDockingView.AddSplit(doc, "right"); }, "opens up the inputted document on the right side of the screen", "(doc: any)"); -Scripting.addGlobal(function useRightSplit(doc: any, shiftKey?: boolean) { CollectionDockingView.UseRightSplit(doc, undefined, shiftKey); }); +Scripting.addGlobal(function useRightSplit(doc: any, shiftKey?: boolean) { CollectionDockingView.ReplaceTab(doc, "right", undefined, shiftKey); }); |