From 1536f8d6e5f44fd14954550e9bd670561727b417 Mon Sep 17 00:00:00 2001 From: bobzel Date: Fri, 28 Aug 2020 15:45:07 -0400 Subject: fixed undo for collection tabs. fixed treeview to support freezeChildren to prevent children from being removed from the tree. Made _isBackground a layoutfield. fixed formattedTextBox exceptions on undo from a tab. --- src/client/util/CurrentUserUtils.ts | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) (limited to 'src/client/util/CurrentUserUtils.ts') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 2c9c870aa..c60403701 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -26,6 +26,8 @@ import { LinkManager } from "./LinkManager"; import { Id } from "../../fields/FieldSymbols"; import { HistoryUtil } from "./History"; import { CollectionDockingView } from "../views/collections/CollectionDockingView"; +import { SelectionManager } from "./SelectionManager"; +import { DocumentManager } from "./DocumentManager"; export class CurrentUserUtils { private static curr_id: string; @@ -1018,12 +1020,9 @@ export class CurrentUserUtils { } public static snapshotDashboard = (userDoc: Doc) => { - const activeDashboard = Cast(userDoc.activeDashboard, Doc, null); - CollectionDockingView.Copy(activeDashboard).then(copy => { - Doc.AddDocToList(Cast(userDoc.myDashboards, Doc, null), "data", copy); - // bcz: strangely, we need a timeout to prevent exceptions/issues initializing GoldenLayout (the rendering engine for Main Container) - setTimeout(() => CurrentUserUtils.openDashboard(userDoc, copy), 0); - }); + const copy = CollectionDockingView.Copy(Cast(userDoc.activeDashboard, Doc, null)); + Doc.AddDocToList(Cast(userDoc.myDashboards, Doc, null), "data", copy); + CurrentUserUtils.openDashboard(userDoc, copy); } public static createNewDashboard = async (userDoc: Doc, id?: string) => { @@ -1059,6 +1058,14 @@ export class CurrentUserUtils { } } +Scripting.addGlobal(function openDragFactory(dragFactory: Doc) { + const copy = Doc.copyDragFactory(dragFactory); + if (copy) { + CollectionDockingView.AddRightSplit(copy); + const view = DocumentManager.Instance.getFirstDocumentView(copy); + view && SelectionManager.SelectDoc(view, false); + } +}); Scripting.addGlobal(function snapshotDashboard() { CurrentUserUtils.snapshotDashboard(Doc.UserDoc()); }, "creates a snapshot copy of a dashboard"); Scripting.addGlobal(function createNewDashboard() { return CurrentUserUtils.createNewDashboard(Doc.UserDoc()); }, -- cgit v1.2.3-70-g09d2 From 7b8924d203c6367edbf7e6ffbe2e8f2e6f24b859 Mon Sep 17 00:00:00 2001 From: bobzel Date: Sat, 29 Aug 2020 00:53:11 -0400 Subject: added some link follow options to link Editor. Big overhaul of dockingView to make things undoable and cleaner --- src/client/documents/Documents.ts | 2 +- src/client/util/CurrentUserUtils.ts | 12 +- src/client/util/DragManager.ts | 3 +- src/client/views/GlobalKeyHandler.ts | 4 +- .../views/collections/CollectionDockingView.tsx | 487 ++++++++------------- .../views/collections/CollectionTreeView.tsx | 2 +- src/client/views/linking/LinkEditor.tsx | 8 + src/client/views/search/SearchBox.tsx | 2 +- src/fields/Doc.ts | 4 - src/mobile/MobileInterface.tsx | 4 +- 10 files changed, 215 insertions(+), 313 deletions(-) (limited to 'src/client/util/CurrentUserUtils.ts') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 18ff993fe..9186cea87 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -848,7 +848,7 @@ export namespace Docs { { type: type, content: [ - ...configs.map(config => CollectionDockingView.makeDocumentConfig(config.doc, config.initialWidth, config.path)) + ...configs.map(config => CollectionDockingView.makeDocumentConfig(config.doc, config.initialWidth)) ] } ] diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index c60403701..7937072da 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -1025,7 +1025,7 @@ export class CurrentUserUtils { CurrentUserUtils.openDashboard(userDoc, copy); } - public static createNewDashboard = async (userDoc: Doc, id?: string) => { + public static createNewDashboard = (userDoc: Doc, id?: string) => { const myPresentations = userDoc.myPresentations as Doc; const presentation = Doc.MakeCopy(userDoc.emptyPresentation as Doc, true); const dashboards = Cast(userDoc.myDashboards, Doc) as Doc; @@ -1051,11 +1051,13 @@ export class CurrentUserUtils { dashboardDoc.contextMenuLabels = new List(["Toggle Theme Colors", "Toggle Comic Mode", "Snapshot Dashboard", "Create Dashboard"]); Doc.AddDocToList(dashboards, "data", dashboardDoc); - // bcz: strangely, we need a timeout to prevent exceptions/issues initializing GoldenLayout (the rendering engine for Main Container) - setTimeout(() => { - CurrentUserUtils.openDashboard(userDoc, dashboardDoc); - }, 0); + CurrentUserUtils.openDashboard(userDoc, dashboardDoc); } + + public static get ActivePresentation() { return Cast(Doc.UserDoc().activePresentation, Doc, null); } + public static get MyRecentlyClosed() { return Cast(Doc.UserDoc().myRecentlyClosedDocs, Doc, null); } + public static get MyDashboards() { return Cast(Doc.UserDoc().myDashboards, Doc, null); } + public static get EmptyPane() { return Cast(Doc.UserDoc().emptyPane, Doc, null); } } Scripting.addGlobal(function openDragFactory(dragFactory: Doc) { diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 0cca61841..8bf6faf03 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -117,10 +117,11 @@ export namespace DragManager { } export class DocumentDragData { - constructor(dragDoc: Doc[]) { + constructor(dragDoc: Doc[], dropAction?: dropActionType) { this.draggedDocuments = dragDoc; this.droppedDocuments = []; this.offset = [0, 0]; + this.dropAction = dropAction; } draggedDocuments: Doc[]; droppedDocuments: Doc[]; diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts index 84b3d64fd..1cbbfd67b 100644 --- a/src/client/views/GlobalKeyHandler.ts +++ b/src/client/views/GlobalKeyHandler.ts @@ -99,7 +99,7 @@ export class KeyManager { if (main.isPointerDown) { DragManager.AbortDrag(); } else { - if (CollectionDockingView.Instance.HasFullScreen()) { + if (CollectionDockingView.Instance.HasFullScreen) { CollectionDockingView.Instance.CloseFullScreen(); } else { doDeselect = !ContextMenu.Instance.closeMenu(); @@ -253,7 +253,7 @@ export class KeyManager { break; case "o": const target = SelectionManager.SelectedDocuments()[0]; - target && CollectionDockingView.Instance && CollectionDockingView.Instance.OpenFullScreen(target); + target && CollectionDockingView.OpenFullScreen(target.props.Document); break; case "r": preventDefault = false; diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 816f784ff..201cada09 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -37,9 +37,8 @@ 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, width?: number) { return { type: 'react-component', component: 'DocumentFrameRenderer', @@ -47,79 +46,46 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) { width: width, props: { documentId: document[Id], - libraryPath: libraryPath?.map(d => d[Id]) } }; } - @computed public get initialized() { - return this._goldenLayout !== null; - } - - @observable private _goldenLayout: any = null; + private _reactionDisposer?: IReactionDisposer; private _containerRef = React.createRef(); private _flush: UndoManager.Batch | undefined; private _ignoreStateChange = ""; - private _isPointerDown = false; - private _maximizedSrc: Opt; + public tabMap: Set = 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[]) => { - console.log("START drag batch"); !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()); - this.stateChanged(); - SelectionManager.DeselectAll(); - } - @undoBatch public CloseFullScreen = () => { const target = this._goldenLayout._maximisedItem; - if (target !== null && this._maximizedSrc) { - this._goldenLayout._maximisedItem.remove(); - SelectionManager.SelectDoc(this._maximizedSrc, false); - this._maximizedSrc = undefined; + if (target) { + target.remove(); this.stateChanged(); } } - public HasFullScreen = () => { - return this._goldenLayout._maximisedItem !== null; - } - @undoBatch @action public static CloseRightSplit(document: Opt): boolean { @@ -136,17 +102,29 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) { const retVal = !CollectionDockingView.Instance?._goldenLayout.root.contentItems[0].isRow ? false : Array.from(CollectionDockingView.Instance._goldenLayout.root.contentItems[0].contentItems).some((child: any) => Array.from(child.contentItems).some(tryClose)); - retVal && CollectionDockingView.Instance.layoutChanged(document); + retVal && document && CollectionDockingView.Instance.layoutChanged(); return retVal; } @undoBatch @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(); + public static OpenFullScreen(doc: Doc, libraryPath?: Doc[]) { + const instance = CollectionDockingView.Instance; + if (doc._viewType === CollectionViewType.Docking && doc.layoutKey === "layout") { + return CurrentUserUtils.openDashboard(Doc.UserDoc(), doc); + } + const newItemStackConfig = { + type: 'stack', + content: [CollectionDockingView.makeDocumentConfig(Doc.MakeAlias(doc), undefined)] + }; + 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; } public static ReplaceRightSplit(document: Doc, libraryPath?: Doc[], addToSplit?: boolean): boolean { @@ -156,14 +134,16 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) { 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); + const newItemStackConfig = CollectionDockingView.makeDocumentConfig(document, undefined); + runInAction(() => document.isDisplayPanel = true); child.addChild(newItemStackConfig, undefined); !addToSplit && child.contentItems[0].remove(); 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); + const newItemStackConfig = CollectionDockingView.makeDocumentConfig(document, undefined); + runInAction(() => document.isDisplayPanel = true); child.addChild(newItemStackConfig, undefined); !addToSplit && child.contentItems[j].remove(); return true; @@ -171,7 +151,7 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) { return false; }); }); - retVal && instance.layoutChanged(document); + retVal && instance.layoutChanged(); return retVal; } @@ -183,9 +163,9 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) { public static AddRightSplit(document: Doc, dontSelect: boolean = false, isDisplayPanel: Opt = undefined) { if (!CollectionDockingView.Instance) return false; - const ind = CollectionDockingView.Instance._tabMap.findIndex((val) => val.doc === document); + const ind = Array.from(CollectionDockingView.Instance.tabMap.keys()).findIndex((tab) => tab.DashDoc === document); if (ind !== -1) { - const tab = CollectionDockingView.Instance._tabMap[ind].tab; + const tab = Array.from(CollectionDockingView.Instance.tabMap.keys())[ind]; const activeContentItem = tab.header.parent.getActiveContentItem(); if (tab.contentItem !== activeContentItem) { tab.header.parent.setActiveContentItem(tab.contentItem); @@ -198,7 +178,7 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) { const instance = CollectionDockingView.Instance; const newItemStackConfig = { type: 'stack', - content: [CollectionDockingView.makeDocumentConfig(document, undefined, [])] + content: [CollectionDockingView.makeDocumentConfig(document, undefined)] }; const newContentItem = instance._goldenLayout.root.layoutManager.createContentItem(newItemStackConfig, instance._goldenLayout); @@ -238,7 +218,7 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) { const instance = CollectionDockingView.Instance; const newItemStackConfig = { type: 'stack', - content: [CollectionDockingView.makeDocumentConfig(document, undefined, libraryPath)] + content: [CollectionDockingView.makeDocumentConfig(document, undefined)] }; const newContentItem = instance._goldenLayout.root.layoutManager.createContentItem(newItemStackConfig, instance._goldenLayout); @@ -299,16 +279,20 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) { // // Creates a vertical split on the right side of the docking view, and then adds the Document to that split // + @undoBatch public static UseRightSplit(document: Doc, libraryPath?: Doc[], shiftKey?: boolean) { if (shiftKey || !CollectionDockingView.ReplaceRightSplit(document, libraryPath, shiftKey)) { - CollectionDockingView.AddRightSplit(document, false, true); + return CollectionDockingView.AddRightSplit(document, false, true); } + return false; } - public AddTab = (stack: any, document: Doc, libraryPath?: Doc[]) => { - const docContentConfig = CollectionDockingView.makeDocumentConfig(document, undefined, libraryPath); + @undoBatch + public static AddTab(stack: any, document: Doc, libraryPath?: Doc[]) { + const instance = CollectionDockingView.Instance; + const docContentConfig = CollectionDockingView.makeDocumentConfig(document, undefined); if (stack === undefined) { - let stack: any = this._goldenLayout.root; + stack = instance._goldenLayout.root; while (!stack.isStack) { if (stack.contentItems.length) { stack = stack.contentItems[0]; @@ -318,41 +302,21 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) { break; } } - if (stack) { - stack.addChild(docContentConfig); - } - } else { - stack.addChild(docContentConfig, undefined); } - this.layoutChanged(); + stack?.addChild(docContentConfig, undefined); + instance.layoutChanged(); return true; } - public ReplaceTab = (stack: any, document: Doc, libraryPath?: Doc[]) => { - 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(); - return true; + @undoBatch + @action + 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(); } - _tabMap: { tab: any, doc: Doc }[] = []; - async setupGoldenLayout() { const config = StrCast(this.props.Document.dockingConfig); if (config) { @@ -360,27 +324,23 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) { const docids = matches?.map(m => m.replace("\"documentId\":\"", "").replace("\"", "")) ?? []; await Promise.all(docids.map(id => DocServer.GetRefField(id))); - if (!this._goldenLayout) { - runInAction(() => this._goldenLayout = new GoldenLayout(JSON.parse(config))); - } - else { + if (this._goldenLayout) { if (config === JSON.stringify(this._goldenLayout.toConfig())) { return; + } else { + try { + this._goldenLayout.unbind('tabDestroyed', this.tabDestroyed); + this._goldenLayout.unbind('stackCreated', this.stackCreated); + } catch (e) { } } - try { - 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('tabCreated', this.tabCreated); + this.tabMap.clear(); + this._goldenLayout?.destroy(); + runInAction(() => this._goldenLayout = new GoldenLayout(JSON.parse(config))); this._goldenLayout.on('tabDestroyed', this.tabDestroyed); this._goldenLayout.on('stackCreated', this.stackCreated); this._goldenLayout.registerComponent('DocumentFrameRenderer', DockedFrameRenderer); this._goldenLayout.container = this._containerRef.current; - //this._goldenLayout.on("stateChanged", () => console.log("STATE CHANGED")); if (this._goldenLayout.config.maximisedItemId === '__glMaximised') { try { this._goldenLayout.config.root.getItemsById(this._goldenLayout.config.maximisedItemId)[0].toggleMaximise(); @@ -391,17 +351,15 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) { this._goldenLayout.init(); } } - reactionDisposer?: Lambda; + componentDidMount: () => void = () => { if (this._containerRef.current) { - const observer = new _global.ResizeObserver(this.onResize); - observer.observe(this._containerRef.current); - this.reactionDisposer = reaction( - () => StrCast(this.props.Document.dockingConfig), + 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(); - DocListCast((Doc.UserDoc().myDashboards as Doc).data).map(d => d.dashboardBrush = false); + DocListCast(CurrentUserUtils.MyDashboards.data).map(d => d.dashboardBrush = false); this.props.Document.dashboardBrush = true; } this._ignoreStateChange = ""; @@ -410,24 +368,19 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) { 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('tabCreated', this.tabCreated); this._goldenLayout.unbind('stackCreated', this.stackCreated); this._goldenLayout.unbind('tabDestroyed', this.tabDestroyed); - } catch (e) { - - } + } catch (e) { } this._goldenLayout?.destroy(); - runInAction(() => { - CollectionDockingView.Instances.splice(CollectionDockingView.Instances.indexOf(this), 1); - this._goldenLayout = null; - }); window.removeEventListener('resize', this.onResize); - this.reactionDisposer?.(); + this._reactionDisposer?.(); } + @action onResize = (event: any) => { const cur = this._containerRef.current; @@ -438,31 +391,29 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) { @action onPointerUp = (e: MouseEvent): void => { window.removeEventListener("pointerup", this.onPointerUp); - this._isPointerDown = false; if (this._flush) { setTimeout(() => { CollectionDockingView.Instance._ignoreStateChange = JSON.stringify(CollectionDockingView.Instance._goldenLayout.toConfig()); this.stateChanged(); - console.log("END BATCH Up"); this._flush!.end(); this._flush = undefined; }, 10); } } + @action onPointerDown = (e: React.PointerEvent): void => { 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"))) { - console.log("START BATCH dwn"); 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(); } } + public static Copy(doc: Doc) { let json = StrCast(doc.dockingConfig); const matches = json.match(/\"documentId\":\"[a-z0-9-]+\"/g); @@ -504,124 +455,18 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) { Doc.GetProto(other).data = new List(Array.from(otherSet.values())); } - tabCreated = (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 doc = DocServer.GetCachedRefField(tab.contentItem.config.props.documentId) as Doc; - if (doc instanceof Doc) { - this._tabMap.push({ tab: tab, doc: 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) => { - if (e.target.className !== "lm_close_tab") { - 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) => view ? [view] : [])(DocumentManager.Instance.getDocumentView(doc)), - (views) => { - if (views.length) { - ReactDOM.render( - views} Stack={stack} /> - , - 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].value = title; - tab.titleElement[0].style.padding = degree ? 0 : 2; - tab.titleElement[0].style.border = `${["gray", "gray", "gray"][degree]} ${["none", "dashed", "solid"][degree]} 2px`; - }, { fireImmediately: true }); - //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(function () { - tab.selectionDisposer?.(); - tab.reactionDisposer?.(); - tab.buttonDisposer?.(); - const doc = DocServer.GetCachedRefField(tab.contentItem.config.props.documentId); - if (doc instanceof Doc) { - const recent = Cast(Doc.UserDoc().myRecentlyClosedDocs, Doc, null); - 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()); - }); - } - tabDestroyed = (tab: any) => { - const ind = this._tabMap.findIndex((val) => val.tab === tab); - ind !== -1 && this._tabMap.splice(ind, 1); - 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 && Array.from(tab.reactComponents).forEach((ele: any) => ReactDOM.unmountComponentAtNode(ele)); } - stackCreated = (stack: any) => { - //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.AddTab(stack, Docs.Create.FreeformDocument([], { _width: this.props.PanelWidth(), _height: this.props.PanelHeight(), title: `Untitled Tab ${NumCast(emptyPane["dragFactory-count"])}` })); } @@ -632,59 +477,125 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) { .click(action(() => { //if (confirm('really close this?')) { stack.remove(); - stack.contentItems.forEach((contentItem: any) => { - const doc = Cast(DocServer.GetCachedRefField(contentItem.config.props.documentId), Doc, null); - if (doc instanceof Doc) { - const recent = Cast(Doc.UserDoc().myRecentlyClosedDocs, Doc, null); - recent && Doc.AddDocToList(recent, "data", doc, undefined, true, true); - } - }); + 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.AddTab(stack, Docs.Create.FreeformDocument([], { _width: this.props.PanelWidth(), _height: this.props.PanelHeight(), title: `Untitled Tab ${NumCast(emptyPane["dragFactory-count"])}` })); })); } render() { - if (this.props.renderDepth > 0) { - return
Nested dashboards can't be rendered
; - } - return
; + return
+ {this.props.renderDepth > 0 ? "Nested dashboards can't be rendered" : (null)} +
; } } interface DockedFrameProps { documentId: FieldId; glContainer: any; - libraryPath: (FieldId[]); - //collectionDockingView: CollectionDockingView } @observer export class DockedFrameRenderer extends React.Component { _mainCont: HTMLDivElement | null = null; + _tabReaction: IReactionDisposer | undefined; @observable private _panelWidth = 0; @observable private _panelHeight = 0; - @observable private _document: Opt; @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]; - } + get stack(): any { return (this.props as any).glContainer.parent.parent; } + get tab() { return (this.props as any).glContainer.tab; } + get tabTitle(): any { return (this.tab?.element[0] as HTMLElement).getElementsByClassName("lm_title")?.[0]; } + get view() { return this._document && DocumentManager.Instance.getDocumentView(this._document); } + @observable _document: Doc | undefined; + constructor(props: any) { super(props); - this._document = Cast(DocServer.GetCachedRefField(this.props.documentId), Doc, null); + setTimeout(() => DocServer.GetRefField(this.tab.contentItem.config.props.documentId).then(doc => this.init(doc as Doc)), 0); + } + + @action + init = (doc: Doc) => { + const tab = this.tab; + tab._disposers = {} as { [name: string]: IReactionDisposer }; + tab.DashDoc = this._document = doc; + if (tab.hasOwnProperty("contentItem") && tab.contentItem.config.type !== "stack") { + tab.contentItem.config.fixed && (tab.contentItem.parent.config.fixed = true); + + CollectionDockingView.Instance.tabMap.add(tab); + 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; + }; + tab.titleElement[0].size = StrCast(this._document.title).length + 1; + tab.titleElement[0].value = this._document.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) => { + e.target.className !== "lm_close_tab" && this.view && SelectionManager.SelectDoc(this.view, false); + }; + // 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); + }; + const onDown = (e: React.PointerEvent) => { + setupMoveUpEvents(this, e, (e) => { + !e.defaultPrevented && DragManager.StartDocumentDrag([gearSpan], new DragManager.DocumentDragData([doc], doc.dropAction as dropActionType), e.clientX, e.clientY); + return !e.defaultPrevented; + }, returnFalse, emptyFunction); + }; + + tab._disposers.selectionDisposer = reaction(() => SelectionManager.SelectedDocuments().some(v => v.props.Document === doc), + (selected) => { + selected && tab.contentItem !== tab.header.parent.getActiveContentItem() && tab.header.parent.setActiveContentItem(tab.contentItem); + } + ); + tab._disposers.buttonDisposer = reaction(() => this.view, + (view) => { + if (view) { + ReactDOM.render( + [view]} Stack={stack} /> + , + gearSpan); + tab._disposers.buttonDisposer?.(); + } + }, { fireImmediately: true }); + + tab.reactComponents = [gearSpan]; + tab.element.append(gearSpan); + tab._disposers.reactionDisposer = reaction(() => ({ title: doc.title, degree: Doc.IsBrushedDegree(doc) }), ({ title, degree }) => { + tab.titleElement[0].value = title; + tab.titleElement[0].style.padding = degree ? 0 : 2; + tab.titleElement[0].style.border = `${["gray", "gray", "gray"][degree]} ${["none", "dashed", "solid"][degree]} 2px`; + }, { fireImmediately: true }); + } + tab.closeElement.off('click') //unbind the current click handler + .click(function () { + Object.values(tab._disposers).forEach((disposer: any) => disposer?.()); + Doc.AddDocToList(CurrentUserUtils.MyRecentlyClosed, "data", doc, undefined, true, true); + SelectionManager.DeselectAll(); + tab.contentItem.remove(); + }); } /** * Adds a document to the presentation view @@ -695,7 +606,7 @@ export class DockedFrameRenderer extends React.Component { if (unpin) DockedFrameRenderer.UnpinDoc(doc); else { //add this new doc to props.Document - const curPres = Cast(Doc.UserDoc().activePresentation, Doc) as Doc; + const curPres = CurrentUserUtils.ActivePresentation; if (curPres) { const pinDoc = Doc.MakeAlias(doc); pinDoc.presentationTargetDoc = doc; @@ -717,7 +628,7 @@ export class DockedFrameRenderer extends React.Component { @action public static UnpinDoc(doc: Doc) { //add this new doc to props.Document - const curPres = Cast(Doc.UserDoc().activePresentation, Doc) as Doc; + const curPres = CurrentUserUtils.ActivePresentation; if (curPres) { const ind = DocListCast(curPres.data).findIndex((val) => Doc.AreProtosEqual(val, doc)); ind !== -1 && Doc.RemoveDocFromList(curPres, "data", DocListCast(curPres.data)[ind]); @@ -727,7 +638,7 @@ export class DockedFrameRenderer extends React.Component { componentDidMount() { const color = () => StrCast(this._document?._backgroundColor, this._document && CollectionDockingView.Instance?.props.backgroundColor?.(this._document, 0) || "white"); const selected = () => SelectionManager.SelectedDocuments().some(v => v.props.Document === this._document); - const updateTabColor = () => this._tab && (this._tab.style.backgroundColor = selected() ? color() : ""); + const updateTabColor = () => this.tabTitle && (this.tabTitle.style.backgroundColor = selected() ? color() : ""); const observer = new _global.ResizeObserver(action((entries: any) => { for (const entry of entries) { this._panelWidth = entry.contentRect.width; @@ -750,10 +661,7 @@ export class DockedFrameRenderer extends React.Component { 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 && setTimeout(() => this.view && SelectionManager.SelectDoc(this.view, false), 0); (CollectionDockingView.Instance as any)._goldenLayout.isInitialised && CollectionDockingView.Instance.stateChanged(); !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. } @@ -764,7 +672,6 @@ export class DockedFrameRenderer extends React.Component { 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; @@ -778,17 +685,9 @@ export class DockedFrameRenderer extends React.Component { 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; } @@ -805,17 +704,14 @@ export class DockedFrameRenderer extends React.Component { 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); - } 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); + if (doc._viewType === CollectionViewType.Docking) return CurrentUserUtils.openDashboard(Doc.UserDoc(), doc); + switch (location) { + case "onRight": return CollectionDockingView.AddRightSplit(doc); + case "close": return CollectionDockingView.CloseRightSplit(doc); + case "replace": return CollectionDockingView.UseRightSplit(doc); + case "fullScreen": return CollectionDockingView.OpenFullScreen(doc); + case "inPlace": + default: return CollectionDockingView.AddTab(this.stack, doc); } } @@ -833,7 +729,7 @@ export class DockedFrameRenderer extends React.Component { 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 && 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; @@ -899,12 +795,11 @@ export class DockedFrameRenderer extends React.Component { @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 <> - { searchFilterDocs={CollectionDockingView.Instance.searchFilterDocs} ContainingCollectionView={undefined} ContainingCollectionDoc={undefined} /> - {document._viewType === CollectionViewType.Freeform && !this._document?.hideMinimap ? this.renderMiniMap() : (null)} + {this._document._viewType === CollectionViewType.Freeform && !this._document?.hideMinimap ? this.renderMiniMap() : (null)} ; } diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index cdfa67769..fffbe65a3 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -738,7 +738,7 @@ export class CollectionTreeView extends CollectionSubView { // need to test if propagation has stopped because GoldenLayout forces a parallel react hierarchy to be created for its top-level layout - if (!e.isPropagationStopped() && this.doc === Doc.UserDoc().myDashboards) { + if (!e.isPropagationStopped() && this.doc === CurrentUserUtils.MyDashboards) { ContextMenu.Instance.addItem({ description: "Create Dashboard", event: () => CurrentUserUtils.createNewDashboard(Doc.UserDoc()), icon: "plus" }); ContextMenu.Instance.addItem({ description: "Delete Dashboard", event: () => this.remove(this.doc), icon: "minus" }); e.stopPropagation(); diff --git a/src/client/views/linking/LinkEditor.tsx b/src/client/views/linking/LinkEditor.tsx index 11a905fb6..8e391f995 100644 --- a/src/client/views/linking/LinkEditor.tsx +++ b/src/client/views/linking/LinkEditor.tsx @@ -379,6 +379,14 @@ export class LinkEditor extends React.Component { onPointerDown={() => this.changeFollowBehavior("onRight")}> Always open in a new pane
+
this.changeFollowBehavior("replace")}> + Always replace right tab +
+
this.changeFollowBehavior("fullScreen")}> + Always open full screen +
this.changeFollowBehavior("inTab")}> Always open in a new tab diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx index a24761195..d7a8ed404 100644 --- a/src/client/views/search/SearchBox.tsx +++ b/src/client/views/search/SearchBox.tsx @@ -492,7 +492,7 @@ export class SearchBox extends ViewBoxBaseComponent
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index cb4ca9a25..1401dd6a1 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -939,19 +939,16 @@ export namespace Doc { } export function IsBrushed(doc: Doc) { - return false; return computedFn(function IsBrushed(doc: Doc) { return brushManager.BrushedDoc.has(doc) || brushManager.BrushedDoc.has(Doc.GetProto(doc)); })(doc); } // don't bother memoizing (caching) the result if called from a non-reactive context. (plus this avoids a warning message) export function IsBrushedDegreeUnmemoized(doc: Doc) { - return 0; if (!doc || GetEffectiveAcl(doc) === AclPrivate || GetEffectiveAcl(Doc.GetProto(doc)) === AclPrivate) return 0; return brushManager.BrushedDoc.has(doc) ? 2 : brushManager.BrushedDoc.has(Doc.GetProto(doc)) ? 1 : 0; } export function IsBrushedDegree(doc: Doc) { - return 0; return computedFn(function IsBrushDegree(doc: Doc) { return Doc.IsBrushedDegreeUnmemoized(doc); })(doc); @@ -980,7 +977,6 @@ export namespace Doc { let _lastDate = 0; export function linkFollowHighlight(destDoc: Doc, dataAndDisplayDocs = true) { - return; linkFollowUnhighlight(); Doc.HighlightDoc(destDoc, dataAndDisplayDocs); document.removeEventListener("pointerdown", linkFollowUnhighlight); diff --git a/src/mobile/MobileInterface.tsx b/src/mobile/MobileInterface.tsx index 841862f49..d4eb76ecd 100644 --- a/src/mobile/MobileInterface.tsx +++ b/src/mobile/MobileInterface.tsx @@ -324,7 +324,7 @@ export class MobileInterface extends React.Component { ); } // stores dashboards documents as 'dashboards' variable - let dashboards = Cast(Doc.UserDoc().myDashboards, Doc) as Doc; + let dashboards = CurrentUserUtils.MyDashboards; if (this.dashboards) { dashboards = this.dashboards; } @@ -394,7 +394,7 @@ export class MobileInterface extends React.Component { */ @action createNewDashboard = async (id?: string) => { - const scens = Cast(Doc.UserDoc().myDashboards, Doc) as Doc; + const scens = CurrentUserUtils.MyDashboards; const dashboardCount = DocListCast(scens.data).length + 1; const freeformOptions: DocumentOptions = { x: 0, -- cgit v1.2.3-70-g09d2 From 4f3c8cfab12f1c5fff27faa0998a286d210148d2 Mon Sep 17 00:00:00 2001 From: bobzel Date: Sat, 29 Aug 2020 16:39:17 -0400 Subject: major restructure of how addDocTabs works. now you can add/replace with options for left/right/top/bottom and specific panel names --- src/client/documents/Documents.ts | 2 +- src/client/util/CurrentUserUtils.ts | 2 +- src/client/util/DictationManager.ts | 4 +- src/client/util/DocumentManager.ts | 4 +- src/client/views/DocumentButtonBar.tsx | 2 +- src/client/views/DocumentDecorations.tsx | 2 +- src/client/views/GlobalKeyHandler.ts | 4 +- src/client/views/MainView.tsx | 8 +- src/client/views/PropertiesButtons.tsx | 4 +- src/client/views/PropertiesDocContextSelector.tsx | 2 +- src/client/views/PropertiesView.tsx | 2 +- src/client/views/animationtimeline/Keyframe.tsx | 2 +- .../views/collections/CollectionDockingView.tsx | 298 ++++++++------------- .../views/collections/CollectionSchemaCells.tsx | 2 +- .../CollectionSchemaMovableTableHOC.tsx | 2 +- .../views/collections/CollectionTreeView.tsx | 2 +- src/client/views/collections/CollectionView.tsx | 8 +- src/client/views/collections/SchemaTable.tsx | 2 +- .../collections/collectionFreeForm/MarqueeView.tsx | 4 +- src/client/views/linking/LinkEditor.tsx | 20 +- src/client/views/linking/LinkMenuItem.tsx | 2 +- src/client/views/nodes/DocumentView.tsx | 16 +- src/client/views/nodes/FontIconBox.tsx | 2 +- src/client/views/nodes/KeyValueBox.tsx | 2 +- src/client/views/nodes/KeyValuePair.tsx | 2 +- src/client/views/nodes/LinkAnchorBox.tsx | 6 +- src/client/views/nodes/LinkDocPreview.tsx | 6 +- src/client/views/nodes/PresBox.tsx | 6 +- src/client/views/nodes/VideoBox.tsx | 2 +- .../views/nodes/formattedText/DashFieldView.tsx | 4 +- .../views/nodes/formattedText/FormattedTextBox.tsx | 12 +- .../formattedText/FormattedTextBoxComment.tsx | 16 +- .../formattedText/ProsemirrorExampleTransfer.ts | 2 +- .../views/nodes/formattedText/RichTextMenu.tsx | 4 +- .../views/nodes/formattedText/RichTextRules.ts | 2 +- src/client/views/nodes/formattedText/nodes_rts.ts | 4 +- src/client/views/pdf/Annotation.tsx | 2 +- src/client/views/pdf/PDFViewer.tsx | 2 +- src/fields/RichTextUtils.ts | 2 +- 39 files changed, 202 insertions(+), 268 deletions(-) (limited to 'src/client/util/CurrentUserUtils.ts') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 2ca1b95d7..262086735 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -847,7 +847,7 @@ export namespace Docs { { type: type, content: [ - ...configs.map(config => CollectionDockingView.makeDocumentConfig(config.doc, false, config.initialWidth)) + ...configs.map(config => CollectionDockingView.makeDocumentConfig(config.doc, undefined, config.initialWidth)) ] } ] diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 7937072da..20f8fe9c1 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -1063,7 +1063,7 @@ export class CurrentUserUtils { Scripting.addGlobal(function openDragFactory(dragFactory: Doc) { const copy = Doc.copyDragFactory(dragFactory); if (copy) { - CollectionDockingView.AddRightSplit(copy); + CollectionDockingView.AddSplit(copy, "right"); const view = DocumentManager.Instance.getFirstDocumentView(copy); view && SelectionManager.SelectDoc(view, false); } diff --git a/src/client/util/DictationManager.ts b/src/client/util/DictationManager.ts index e66c15fcb..231e1fa8d 100644 --- a/src/client/util/DictationManager.ts +++ b/src/client/util/DictationManager.ts @@ -323,7 +323,7 @@ export namespace DictationManager { ["open fields", { action: (target: DocumentView) => { const kvp = Docs.Create.KVPDocument(target.props.Document, { _width: 300, _height: 300 }); - target.props.addDocTab(kvp, "onRight"); + target.props.addDocTab(kvp, "add:right"); } }], @@ -337,7 +337,7 @@ export namespace DictationManager { const proseMirrorState = `{"doc":{"type":"doc","content":[{"type":"ordered_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"type":"text","text":"${prompt}"}]}]}]}]},"selection":{"type":"text","anchor":${anchor},"head":${head}}}`; proto.data = new RichTextField(proseMirrorState); proto.backgroundColor = "#eeffff"; - target.props.addDocTab(newBox, "onRight"); + target.props.addDocTab(newBox, "add:right"); } }] diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index fb54fbefc..93c37dbd1 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -124,7 +124,7 @@ export class DocumentManager { } static addRightSplit = (doc: Doc, finished?: () => void) => { - CollectionDockingView.AddRightSplit(doc); + CollectionDockingView.AddSplit(doc, "right"); finished?.(); } public jumpToDocument = async ( @@ -231,7 +231,7 @@ export class DocumentManager { containerDoc._currentTimecode = targetTimecode; const targetContext = await target?.context as Doc; const targetNavContext = !Doc.AreProtosEqual(targetContext, currentContext) ? targetContext : undefined; - DocumentManager.Instance.jumpToDocument(target, zoom, (doc, finished) => createViewFunc(doc, StrCast(linkDoc.followLinkLocation, "onRight"), finished), targetNavContext, linkDoc, undefined, doc, finished); + DocumentManager.Instance.jumpToDocument(target, zoom, (doc, finished) => createViewFunc(doc, StrCast(linkDoc.followLinkLocation, "add:right"), finished), targetNavContext, linkDoc, undefined, doc, finished); } else { finished?.(); } diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx index dd223390a..764a2547c 100644 --- a/src/client/views/DocumentButtonBar.tsx +++ b/src/client/views/DocumentButtonBar.tsx @@ -156,7 +156,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV googleDoc = Docs.Create.WebDocument(googleDocUrl, options); dataDoc.googleDoc = googleDoc; } - CollectionDockingView.AddRightSplit(googleDoc); + CollectionDockingView.AddSplit(googleDoc, "right"); } else if (e.altKey) { e.preventDefault(); window.open(googleDocUrl); diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 6c64a3714..c7a24133c 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -188,7 +188,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> const alias = Doc.MakeAlias(selectedDocs[0].props.Document); alias.context = undefined; //CollectionDockingView.Instance?.OpenFullScreen(selectedDocs[0]); - CollectionDockingView.AddRightSplit(alias); + CollectionDockingView.AddSplit(alias, "right"); } } SelectionManager.DeselectAll(); diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts index 2794a9f4e..db3e3d0dd 100644 --- a/src/client/views/GlobalKeyHandler.ts +++ b/src/client/views/GlobalKeyHandler.ts @@ -210,7 +210,7 @@ export class KeyManager { return { stopPropagation: false, preventDefault: false }; } } - MainView.Instance.mainFreeform && CollectionDockingView.AddRightSplit(MainView.Instance.mainFreeform); + MainView.Instance.mainFreeform && CollectionDockingView.AddSplit(MainView.Instance.mainFreeform, "right"); break; case "arrowleft": if (document.activeElement) { @@ -218,7 +218,7 @@ export class KeyManager { return { stopPropagation: false, preventDefault: false }; } } - MainView.Instance.mainFreeform && CollectionDockingView.CloseRightSplit(MainView.Instance.mainFreeform); + MainView.Instance.mainFreeform && CollectionDockingView.CloseSplit(MainView.Instance.mainFreeform); break; case "backspace": if (document.activeElement) { diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 6a960f4d5..7b0c6fa9f 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -236,7 +236,7 @@ export class MainView extends React.Component { reaction(() => CollectionDockingView.Instance && CollectionDockingView.Instance.initialized, initialized => initialized && received && DocServer.GetRefField(received).then(docField => { if (docField instanceof Doc && docField._viewType !== CollectionViewType.Docking) { - CollectionDockingView.AddRightSplit(docField); + CollectionDockingView.AddSplit(docField, "right"); } }), ); @@ -260,7 +260,7 @@ export class MainView extends React.Component { } const pres = Docs.Create.PresDocument(new List(), { title: "Untitled Presentation", _viewType: CollectionViewType.Stacking, _width: 400, _height: 500, targetDropAction: "alias", _chromeStatus: "replaced", boxShadow: "0 0", system: true }); - CollectionDockingView.AddRightSplit(pres); + CollectionDockingView.AddSplit(pres, "right"); Doc.UserDoc().activePresentation = pres; const myPresentations = Doc.UserDoc().myPresentations as Doc; Doc.AddDocToList(myPresentations, "data", pres); @@ -382,9 +382,9 @@ export class MainView extends React.Component { @computed get topOffset() { return (CollectionMenu.Instance?.Pinned ? 35 : 0) + Number(SEARCH_PANEL_HEIGHT.replace("px", "")); } flyoutWidthFunc = () => this.flyoutWidth; addDocTabFunc = (doc: Doc, where: string, libraryPath?: Doc[]): boolean => { - return where === "close" ? CollectionDockingView.CloseRightSplit(doc) : + return where === "close" ? CollectionDockingView.CloseSplit(doc) : doc.dockingConfig ? CurrentUserUtils.openDashboard(Doc.UserDoc(), doc) : - CollectionDockingView.AddRightSplit(doc); + CollectionDockingView.AddSplit(doc, "right"); } sidebarScreenToLocal = () => new Transform(0, (CollectionMenu.Instance.Pinned ? -35 : 0) - Number(SEARCH_PANEL_HEIGHT.replace("px", "")), 1); mainContainerXf = () => this.sidebarScreenToLocal().translate(-58, 0); diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx index e95b28d6c..6ac6571d3 100644 --- a/src/client/views/PropertiesButtons.tsx +++ b/src/client/views/PropertiesButtons.tsx @@ -160,7 +160,7 @@ export class PropertiesButtons extends React.Component<{}, {}> { googleDoc = Docs.Create.WebDocument(googleDocUrl, options); dataDoc.googleDoc = googleDoc; } - CollectionDockingView.AddRightSplit(googleDoc); + CollectionDockingView.AddRight(googleDoc, "right"); } else if (e.altKey) { e.preventDefault(); window.open(googleDocUrl); @@ -343,7 +343,7 @@ export class PropertiesButtons extends React.Component<{}, {}> { dv.toggleFollowLink("inPlace", true, false); } else if (value === "linkOnRight") { dv.noOnClick(); - dv.toggleFollowLink("onRight", false, false); + dv.toggleFollowLink("add:right", false, false); } }); } diff --git a/src/client/views/PropertiesDocContextSelector.tsx b/src/client/views/PropertiesDocContextSelector.tsx index b382298f3..427748fe7 100644 --- a/src/client/views/PropertiesDocContextSelector.tsx +++ b/src/client/views/PropertiesDocContextSelector.tsx @@ -44,7 +44,7 @@ export class PropertiesDocContextSelector extends React.Component { } @computed get contexts() { - return !this.selectedDoc ? (null) : CollectionDockingView.AddRightSplit(doc)} />; + return !this.selectedDoc ? (null) : CollectionDockingView.AddSplit(doc, "right")} />; } previewBackground = () => "lightgrey"; diff --git a/src/client/views/animationtimeline/Keyframe.tsx b/src/client/views/animationtimeline/Keyframe.tsx index 4fb362ab1..e84022366 100644 --- a/src/client/views/animationtimeline/Keyframe.tsx +++ b/src/client/views/animationtimeline/Keyframe.tsx @@ -331,7 +331,7 @@ export class Keyframe extends React.Component { }), TimelineMenu.Instance.addItem("button", "Show Data", action(() => { const kvp = Docs.Create.KVPDocument(kf, { _width: 300, _height: 300 }); - CollectionDockingView.AddRightSplit(kvp); + CollectionDockingView.AddSplit(kvp, "right"); })), TimelineMenu.Instance.addItem("button", "Delete", action(() => { (this.regiondata.keyframes as List).splice(this.keyframes.indexOf(kf), 1); diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 8301d3df8..62c865436 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -1,6 +1,6 @@ import 'golden-layout/src/css/goldenlayout-base.css'; import 'golden-layout/src/css/goldenlayout-dark-theme.css'; -import { clamp } from 'lodash'; +import { clamp, pull } from 'lodash'; import { action, computed, IReactionDisposer, Lambda, observable, reaction, runInAction } from "mobx"; import { observer } from "mobx-react"; import * as ReactDOM from 'react-dom'; @@ -38,7 +38,7 @@ const _global = (window /* browser */ || global /* node */) as any; @observer export class CollectionDockingView extends CollectionSubView(doc => doc) { @observable public static Instance: CollectionDockingView; - public static makeDocumentConfig(document: Doc, isDisplayPanel?: boolean, width?: number) { + public static makeDocumentConfig(document: Doc, panelName?: string, width?: number) { return { type: 'react-component', component: 'DocumentFrameRenderer', @@ -46,7 +46,7 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) { width: width, props: { documentId: document[Id], - isDisplayPanel // flag for whether a tab should be considered a placeholder that has its contents replaced with new content + panelName // name of tab that can be used to close or replace its contents } }; } @@ -85,26 +85,20 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) { } @undoBatch - @action - public static CloseRightSplit(document: Opt): boolean { - const tryClose = (childItem: any) => { - if (childItem.config?.component === "DocumentFrameRenderer") { - if ((!document && childItem.config.props.isDisplayPanel) || (document && childItem.config.props.documentId === document[Id])) { - childItem.remove(); - return true; - } + public static CloseSplit(document: Opt, 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(); } - return false; - }; - const retVal = !CollectionDockingView.Instance?._goldenLayout.root.contentItems[0].isRow ? false : - Array.from(CollectionDockingView.Instance._goldenLayout.root.contentItems[0].contentItems).some((child: any) => Array.from(child.contentItems).some(tryClose)); + } - retVal && document && CollectionDockingView.Instance.layoutChanged(); - return retVal; + return false; } @undoBatch - @action public static OpenFullScreen(doc: Doc, libraryPath?: Doc[]) { const instance = CollectionDockingView.Instance; if (doc._viewType === CollectionViewType.Docking && doc.layoutKey === "layout") { @@ -124,176 +118,94 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) { return true; } - public static ReplaceRightSplit(document: Doc, libraryPath?: Doc[], addToSplit?: boolean): boolean { - if (!CollectionDockingView.Instance) return false; - const instance = CollectionDockingView.Instance; - const retVal = !instance._goldenLayout.root.contentItems[0].isRow ? false : - Array.from(instance._goldenLayout.root.contentItems[0].contentItems).some((child: any) => { - if (child.contentItems.length === 1 && child.contentItems[0].config.component === "DocumentFrameRenderer" && - child.contentItems[0].config.isDisplayPanel) { - const newItemStackConfig = CollectionDockingView.makeDocumentConfig(document, true); - child.addChild(newItemStackConfig, undefined); - !addToSplit && child.contentItems[0].remove(); - return true; - } - return Array.from(child.contentItems).filter((tab: any) => tab.config.component === "DocumentFrameRenderer").some((tab: any, j: number) => { - if (tab.config.props.isDisplayPanel) { - const newItemStackConfig = CollectionDockingView.makeDocumentConfig(document, true); - child.addChild(newItemStackConfig, undefined); - !addToSplit && child.contentItems[j].remove(); - return true; - } - return false; - }); - }); - retVal && instance.layoutChanged(); - return retVal; - } - - // - // 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, isDisplayPanel: Opt = undefined) { - if (!CollectionDockingView.Instance) return false; - - const ind = Array.from(CollectionDockingView.Instance.tabMap.keys()).findIndex((tab) => tab.DashDoc === document); - if (ind !== -1) { - const tab = Array.from(CollectionDockingView.Instance.tabMap.keys())[ind]; - const activeContentItem = tab.header.parent.getActiveContentItem(); - if (tab.contentItem !== activeContentItem) { - tab.header.parent.setActiveContentItem(tab.contentItem); - } - tab.setActive(true); - } else { - if (document._viewType === CollectionViewType.Docking) return CurrentUserUtils.openDashboard(Doc.UserDoc(), document); - const instance = CollectionDockingView.Instance; - const newItemStackConfig = { - type: 'stack', - content: [CollectionDockingView.makeDocumentConfig(document, isDisplayPanel)] - }; - - 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(); + public static ReplaceTab(document: Doc, panelName: string, stack: any, addToSplit?: boolean): boolean { + const instance = CollectionDockingView.Instance; + 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(); } - return true; + 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 CollectionDockingView.AddSplit(document, panelName, 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 // - @action - public static AddSplit(document: Doc, pullSide: string, libraryPath?: Doc[]) { - if (!CollectionDockingView.Instance) return false; + @undoBatch + public static AddSplit(document: Doc, pullSide: string, stack?: any, panelName?: string) { const instance = CollectionDockingView.Instance; - const newItemStackConfig = { - type: 'stack', - content: [CollectionDockingView.makeDocumentConfig(document, undefined)] - }; + 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; } - - 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); + } 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; } - - collayout.config.width = 50; - newContentItem.config.width = 50; } + newContentItem.callDownwards('_$init'); } - 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 - public static UseRightSplit(document: Doc, libraryPath?: Doc[], shiftKey?: boolean) { - if (shiftKey || !CollectionDockingView.ReplaceRightSplit(document, libraryPath, shiftKey)) { - return CollectionDockingView.AddRightSplit(document, true); - } - return false; - } - - @undoBatch - public static AddTab(stack: any, document: Doc, libraryPath?: Doc[]) { - const instance = CollectionDockingView.Instance; - const docContentConfig = CollectionDockingView.makeDocumentConfig(document, undefined); - if (stack === undefined) { - stack = instance._goldenLayout.root; - while (!stack.isStack) { - if (stack.contentItems.length) { - stack = stack.contentItems[0]; - } else { - stack.addChild({ type: 'stack', content: [docContentConfig] }); - stack = undefined; - break; - } - } - } - stack?.addChild(docContentConfig, undefined); - instance.layoutChanged(); - return true; + return instance.layoutChanged(); } @undoBatch @@ -303,6 +215,7 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) { this._goldenLayout.emit('stateChanged'); this._ignoreStateChange = JSON.stringify(this._goldenLayout.toConfig()); this.stateChanged(); + return true; } async setupGoldenLayout() { @@ -450,7 +363,7 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) { tab.reactComponents?.forEach((ele: any) => ReactDOM.unmountComponentAtNode(ele)); } 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) + 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) => { @@ -458,9 +371,9 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) { if (e.target === stack.header.element[0] && e.button === 2) { const emptyPane = CurrentUserUtils.EmptyPane; emptyPane["dragFactory-count"] = NumCast(emptyPane["dragFactory-count"]) + 1; - CollectionDockingView.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); } }); @@ -477,9 +390,9 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) { // stack.config.fixed = !stack.config.fixed; // force the stack to have a fixed size const emptyPane = CurrentUserUtils.EmptyPane; emptyPane["dragFactory-count"] = NumCast(emptyPane["dragFactory-count"]) + 1; - CollectionDockingView.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); })); } @@ -601,7 +514,7 @@ export class DockedFrameRenderer extends React.Component { Doc.AddDocToList(curPres, "data", pinDoc); if (curPres.expandBoolean) pinDoc.presExpandInlineButton = true; if (!DocumentManager.Instance.getDocumentView(curPres)) { - CollectionDockingView.AddRightSplit(curPres); + CollectionDockingView.AddSplit(curPres, "right"); } DocumentManager.Instance.jumpToDocument(doc, false, undefined, Cast(doc.context, Doc, null)); } @@ -688,17 +601,26 @@ export class DockedFrameRenderer extends React.Component { 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; } + // 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, libraryPath?: Doc[]) => { SelectionManager.DeselectAll(); if (doc._viewType === CollectionViewType.Docking) return CurrentUserUtils.openDashboard(Doc.UserDoc(), doc); - switch (location) { - case "onRight": return Array.from(CollectionDockingView.Instance.tabMap.keys()).findIndex((tab) => tab.DashDoc === doc) !== -1 ? - CollectionDockingView.CloseRightSplit(doc) : CollectionDockingView.AddRightSplit(doc); - case "close": return CollectionDockingView.CloseRightSplit(doc); - case "replace": return CollectionDockingView.UseRightSplit(doc); + const locationFields = location.split(":"); + const locationParams = locationFields.length > 1 ? locationFields[1] : ""; + switch (locationFields[0]) { + case "close": return CollectionDockingView.CloseSplit(doc, locationParams); case "fullScreen": return CollectionDockingView.OpenFullScreen(doc); + case "replace": return CollectionDockingView.ReplaceTab(doc, locationParams, this.stack); case "inPlace": - default: return CollectionDockingView.AddTab(this.stack, doc); + case "add": + default: return Array.from(CollectionDockingView.Instance.tabMap.keys()).findIndex((tab) => tab.DashDoc === doc) !== -1 ? + CollectionDockingView.CloseSplit(doc) : CollectionDockingView.AddSplit(doc, locationParams, this.stack); } } @@ -830,6 +752,6 @@ export class DockedFrameRenderer extends React.Component {
); } } -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); }); diff --git a/src/client/views/collections/CollectionSchemaCells.tsx b/src/client/views/collections/CollectionSchemaCells.tsx index 18ca29aaa..0d3a951cf 100644 --- a/src/client/views/collections/CollectionSchemaCells.tsx +++ b/src/client/views/collections/CollectionSchemaCells.tsx @@ -370,7 +370,7 @@ export class CollectionSchemaDocCell extends CollectionSchemaCell { })} />
-
this._doc && this.props.addDocTab(this._doc, "onRight")} className="collectionSchemaView-cellContents-docButton"> +
this._doc && this.props.addDocTab(this._doc, "add:right")} className="collectionSchemaView-cellContents-docButton">
; diff --git a/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx b/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx index a3475b481..383a9312f 100644 --- a/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx +++ b/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx @@ -246,7 +246,7 @@ export class MovableRow extends React.Component {
this.props.removeDoc(this.props.rowInfo.original))}>
-
this.props.addDocTab(this.props.rowInfo.original, "onRight")}>
+
this.props.addDocTab(this.props.rowInfo.original, "add:right")}>
{children} diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index fffbe65a3..80a6afba7 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -117,7 +117,7 @@ class TreeView extends React.Component { Doc.ComputeContentBounds(DocListCast(this.props.document[this.fieldKey])); } - @undoBatch openRight = () => this.props.addDocTab(this.doc, "onRight"); + @undoBatch openRight = () => this.props.addDocTab(this.doc, "add:right"); @undoBatch move = (doc: Doc | Doc[], target: Doc | undefined, addDoc: (doc: Doc | Doc[]) => boolean) => { return this.doc !== target && this.props.removeDoc?.(doc) === true && addDoc(doc); } diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 1da0e601a..64ad3abbf 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -305,7 +305,7 @@ export class CollectionView extends Touchable { const newRendition = Doc.MakeAlias(this.props.Document); newRendition._viewType = vtype; - this.props.addDocTab(newRendition, "onRight"); + this.props.addDocTab(newRendition, "add:right"); return newRendition; }, false); @@ -313,10 +313,10 @@ export class CollectionView extends Touchable this.props.Document.forceActive = !this.props.Document.forceActive, icon: "project-diagram" }) : null; if (this.props.Document.childLayout instanceof Doc) { - optionItems.push({ description: "View Child Layout", event: () => this.props.addDocTab(this.props.Document.childLayout as Doc, "onRight"), icon: "project-diagram" }); + optionItems.push({ description: "View Child Layout", event: () => this.props.addDocTab(this.props.Document.childLayout as Doc, "add:right"), icon: "project-diagram" }); } if (this.props.Document.childClickedOpenTemplateView instanceof Doc) { - optionItems.push({ description: "View Child Detailed Layout", event: () => this.props.addDocTab(this.props.Document.childClickedOpenTemplateView as Doc, "onRight"), icon: "project-diagram" }); + optionItems.push({ description: "View Child Detailed Layout", event: () => this.props.addDocTab(this.props.Document.childClickedOpenTemplateView as Doc, "add:right"), icon: "project-diagram" }); } !Doc.UserDoc().noviceMode && optionItems.push({ description: `${this.props.Document.isInPlaceContainer ? "Unset" : "Set"} inPlace Container`, event: () => this.props.Document.isInPlaceContainer = !this.props.Document.isInPlaceContainer, icon: "project-diagram" }); @@ -331,7 +331,7 @@ export class CollectionView extends Touchable { const alias = Doc.MakeAlias(this.props.Document); DocUtils.makeCustomViewClicked(alias, undefined, func.key); - this.props.addDocTab(alias, "onRight"); + this.props.addDocTab(alias, "add:right"); } })); DocListCast(Cast(Doc.UserDoc()["clickFuncs-child"], Doc, null).data).forEach(childClick => diff --git a/src/client/views/collections/SchemaTable.tsx b/src/client/views/collections/SchemaTable.tsx index 2fe00a245..1fb7aa04a 100644 --- a/src/client/views/collections/SchemaTable.tsx +++ b/src/client/views/collections/SchemaTable.tsx @@ -545,7 +545,7 @@ export class SchemaTable extends React.Component { } onOpenClick = () => { - this._showDoc && this.props.addDocTab(this._showDoc, "onRight"); + this._showDoc && this.props.addDocTab(this._showDoc, "add:right"); } getPreviewTransform = (): Transform => { diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index 759d142ea..1aa30fc02 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -76,7 +76,7 @@ export class MarqueeView extends React.Component this.props.addDocTab( - Docs.Create.WebDocument(`https://bing.com/search?q=${str}`, { _width: 200, x, y, _nativeHeight: 962, _nativeWidth: 850, isAnnotating: false, title: "bing", UseCors: true }), "onRight")); + Docs.Create.WebDocument(`https://bing.com/search?q=${str}`, { _width: 200, x, y, _nativeHeight: 962, _nativeWidth: 850, isAnnotating: false, title: "bing", UseCors: true }), "add:right")); cm.displayMenu(this._downX, this._downY); e.stopPropagation(); @@ -400,7 +400,7 @@ export class MarqueeView extends React.Component { Default
this.changeFollowBehavior("onRight")}> - Always open in a new pane + onPointerDown={() => this.changeFollowBehavior("add:left")}> + Always open in new left pane
this.changeFollowBehavior("replace")}> + onPointerDown={() => this.changeFollowBehavior("add:right")}> + Always open in new right pane +
+
this.changeFollowBehavior("replace:right")}> Always replace right tab
+
this.changeFollowBehavior("replace:left")}> + Always replace left tab +
this.changeFollowBehavior("fullScreen")}> Always open full screen
this.changeFollowBehavior("inTab")}> + onPointerDown={() => this.changeFollowBehavior("add")}> Always open in a new tab
+
this.changeFollowBehavior("replace")}> + Replace Tab +
{this.props.linkDoc.linksToAnnotation ?
this.changeFollowBehavior("openExternal")}> diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx index a77122456..05b6b584d 100644 --- a/src/client/views/linking/LinkMenuItem.tsx +++ b/src/client/views/linking/LinkMenuItem.tsx @@ -167,7 +167,7 @@ export class LinkMenuItem extends React.Component { }); } } else { - DocumentManager.Instance.FollowLink(this.props.linkDoc, this.props.sourceDoc, doc => this.props.addDocTab(doc, "onRight"), false); + DocumentManager.Instance.FollowLink(this.props.linkDoc, this.props.sourceDoc, doc => this.props.addDocTab(doc, "add:right"), false); } linkDoc.linksToAnnotation && Hypothesis.scrollToAnnotation(StrCast(this.props.linkDoc.annotationId), this.props.destinationDoc); diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 2d03c5154..48a1688a6 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -180,10 +180,10 @@ export class DocumentView extends DocComponent(Docu const pt = me.touchEvent.touches[me.touchEvent.touches.length - 1]; RadialMenu.Instance.openMenu(pt.pageX - 15, pt.pageY - 15); - // RadialMenu.Instance.addItem({ description: "Open Fields", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { _width: 300, _height: 300 }), "onRight"), icon: "map-pin", selected: -1 }); + // RadialMenu.Instance.addItem({ description: "Open Fields", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { _width: 300, _height: 300 }), "add:right"), icon: "map-pin", selected: -1 }); const effectiveAcl = GetEffectiveAcl(this.props.Document[DataSym]); (effectiveAcl === AclEdit || effectiveAcl === AclAdmin) && RadialMenu.Instance.addItem({ description: "Delete", event: () => { this.props.ContainingCollectionView?.removeDocument(this.props.Document), RadialMenu.Instance.closeMenu(); }, icon: "external-link-square-alt", selected: -1 }); - // RadialMenu.Instance.addItem({ description: "Open in a new tab", event: () => this.props.addDocTab(this.props.Document, "onRight"), icon: "trash", selected: -1 }); + // RadialMenu.Instance.addItem({ description: "Open in a new tab", event: () => this.props.addDocTab(this.props.Document, "add:right"), icon: "trash", selected: -1 }); RadialMenu.Instance.addItem({ description: "Pin", event: () => this.props.pinToPres(this.props.Document), icon: "map-pin", selected: -1 }); RadialMenu.Instance.addItem({ description: "Open", event: () => MobileInterface.Instance.handleClick(this.props.Document), icon: "trash", selected: -1 }); @@ -323,7 +323,7 @@ export class DocumentView extends DocComponent(Docu fullScreenDoc = Doc.MakeAlias(this.props.Document); fullScreenDoc.layoutKey = "layout_fullScreen"; } - this.props.addDocTab(fullScreenDoc, "inTab"); + this.props.addDocTab(fullScreenDoc, "add"); }, "double tap"); SelectionManager.DeselectAll(); } @@ -351,7 +351,7 @@ export class DocumentView extends DocComponent(Docu this._timeout = setTimeout(() => { this._timeout = undefined; clickFunc(); }, 500); } else clickFunc(); } else if (this.Document["onClick-rawScript"] && !StrCast(Doc.LayoutField(this.layoutDoc))?.includes("ScriptingBox")) {// bcz: hack? don't edit a script if you're clicking on a scripting box itself - this.props.addDocTab(DocUtils.makeCustomViewClicked(Doc.MakeAlias(this.props.Document), undefined, "onClick"), "onRight"); + this.props.addDocTab(DocUtils.makeCustomViewClicked(Doc.MakeAlias(this.props.Document), undefined, "onClick"), "add:right"); } else if (this.allLinks && this.Document.isLinkButton && !e.shiftKey && !e.ctrlKey) { this.allLinks.length && this.followLinkClick(e.altKey, e.ctrlKey, e.shiftKey); } else { @@ -748,7 +748,7 @@ export class DocumentView extends DocComponent(Docu const templateDoc = Cast(this.props.Document[StrCast(this.props.Document.layoutKey)], Doc, null); const appearance = cm.findByDescription("UI Controls..."); const appearanceItems: ContextMenuProps[] = appearance && "subitems" in appearance ? appearance.subitems : []; - templateDoc && appearanceItems.push({ description: "Open Template ", event: () => this.props.addDocTab(templateDoc, "onRight"), icon: "eye" }); + templateDoc && appearanceItems.push({ description: "Open Template ", event: () => this.props.addDocTab(templateDoc, "add:right"), icon: "eye" }); //DocListCast(this.Document.links).length && appearanceItems.splice(0, 0, { description: `${this.layoutDoc.hideLinkButton ? "Show" : "Hide"} Link Button`, event: action(() => this.layoutDoc.hideLinkButton = !this.layoutDoc.hideLinkButton), icon: "eye" }); !appearance && cm.addItem({ description: "UI Controls...", subitems: appearanceItems, icon: "compass" }); @@ -766,7 +766,7 @@ export class DocumentView extends DocComponent(Docu onClicks.push({ description: "Toggle Detail", event: () => this.Document.onClick = ScriptField.MakeScript(`toggleDetail(self, "${this.Document.layoutKey}")`), icon: "concierge-bell" }); onClicks.push({ description: this.Document.ignoreClick ? "Select" : "Do Nothing", event: () => this.Document.ignoreClick = !this.Document.ignoreClick, icon: this.Document.ignoreClick ? "unlock" : "lock" }); onClicks.push({ description: this.Document.isLinkButton ? "Remove Follow Behavior" : "Follow Link in Place", event: () => this.toggleFollowLink("inPlace", true, false), icon: "link" }); - !this.Document.isLinkButton && onClicks.push({ description: "Follow Link on Right", event: () => this.toggleFollowLink("onRight", false, false), icon: "link" }); + !this.Document.isLinkButton && onClicks.push({ description: "Follow Link on Right", event: () => this.toggleFollowLink("add:right", false, false), icon: "link" }); onClicks.push({ description: this.Document.isLinkButton || this.onClickHandler ? "Remove Click Behavior" : "Follow Link", event: () => this.toggleFollowLink(undefined, false, false), icon: "link" }); onClicks.push({ description: (this.Document.isPushpin ? "Remove" : "Make") + " Pushpin", event: () => this.toggleFollowLink(undefined, false, true), icon: "map-pin" }); onClicks.push({ description: "Edit onClick Script", event: () => UndoManager.RunInBatch(() => DocUtils.makeCustomViewClicked(this.props.Document, undefined, "onClick"), "edit onClick"), icon: "terminal" }); @@ -810,8 +810,8 @@ export class DocumentView extends DocComponent(Docu const help = cm.findByDescription("Help..."); const helpItems: ContextMenuProps[] = help && "subitems" in help ? help.subitems : []; - !Doc.UserDoc().novice && helpItems.push({ description: "Show Fields ", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { _width: 300, _height: 300 }), "onRight"), icon: "layer-group" }); - helpItems.push({ description: "Text Shortcuts Ctrl+/", event: () => this.props.addDocTab(Docs.Create.PdfDocument(Utils.prepend("/assets/cheat-sheet.pdf"), { _width: 300, _height: 300 }), "onRight"), icon: "keyboard" }); + !Doc.UserDoc().novice && helpItems.push({ description: "Show Fields ", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { _width: 300, _height: 300 }), "add:right"), icon: "layer-group" }); + helpItems.push({ description: "Text Shortcuts Ctrl+/", event: () => this.props.addDocTab(Docs.Create.PdfDocument(Utils.prepend("/assets/cheat-sheet.pdf"), { _width: 300, _height: 300 }), "add:right"), icon: "keyboard" }); helpItems.push({ description: "Print Document in Console", event: () => console.log(this.props.Document), icon: "hand-point-right" }); cm.addItem({ description: "Help...", noexpand: true, subitems: helpItems, icon: "question" }); diff --git a/src/client/views/nodes/FontIconBox.tsx b/src/client/views/nodes/FontIconBox.tsx index bc5abb0a4..87142babd 100644 --- a/src/client/views/nodes/FontIconBox.tsx +++ b/src/client/views/nodes/FontIconBox.tsx @@ -39,7 +39,7 @@ export class FontIconBox extends DocComponent( showTemplate = (): void => { const dragFactory = Cast(this.layoutDoc.dragFactory, Doc, null); - dragFactory && this.props.addDocTab(dragFactory, "onRight"); + dragFactory && this.props.addDocTab(dragFactory, "add:right"); } dragAsTemplate = (): void => { this.layoutDoc.onDragStart = ScriptField.MakeFunction('getCopy(this.dragFactory, true)'); diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx index b732f5f83..c5ff42a1a 100644 --- a/src/client/views/nodes/KeyValueBox.tsx +++ b/src/client/views/nodes/KeyValueBox.tsx @@ -230,7 +230,7 @@ export class KeyValueBox extends React.Component { openItems.push({ description: "Default Perspective", event: () => { this.props.addDocTab(this.props.Document, "close"); - this.props.addDocTab(this.fieldDocToLayout, "onRight"); + this.props.addDocTab(this.fieldDocToLayout, "add:right"); }, icon: "image" }); !open && cm.addItem({ description: "Change Perspective...", subitems: openItems, icon: "external-link-alt" }); diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx index 6dd85b7f5..74d10d087 100644 --- a/src/client/views/nodes/KeyValuePair.tsx +++ b/src/client/views/nodes/KeyValuePair.tsx @@ -46,7 +46,7 @@ export class KeyValuePair extends React.Component { if (value instanceof Doc) { e.stopPropagation(); e.preventDefault(); - ContextMenu.Instance.addItem({ description: "Open Fields", event: () => this.props.addDocTab(Docs.Create.KVPDocument(value, { _width: 300, _height: 300 }), "onRight"), icon: "layer-group" }); + ContextMenu.Instance.addItem({ description: "Open Fields", event: () => this.props.addDocTab(Docs.Create.KVPDocument(value, { _width: 300, _height: 300 }), "add:right"), icon: "layer-group" }); ContextMenu.Instance.displayMenu(e.clientX, e.clientY); } } diff --git a/src/client/views/nodes/LinkAnchorBox.tsx b/src/client/views/nodes/LinkAnchorBox.tsx index 8e5ee09ff..10b6aa02e 100644 --- a/src/client/views/nodes/LinkAnchorBox.tsx +++ b/src/client/views/nodes/LinkAnchorBox.tsx @@ -73,7 +73,7 @@ export class LinkAnchorBox extends ViewBoxBaseComponent { - DocumentManager.Instance.FollowLink(this.rootDoc, anchorContainerDoc, document => this.props.addDocTab(document, StrCast(this.layoutDoc.linkOpenLocation, e.altKey ? "inTab" : "onRight")), false); + DocumentManager.Instance.FollowLink(this.rootDoc, anchorContainerDoc, document => this.props.addDocTab(document, StrCast(this.layoutDoc.linkOpenLocation, "add:right")), false); this._editing = false; }), 300 - (Date.now() - this._lastTap)); } @@ -87,14 +87,14 @@ export class LinkAnchorBox extends ViewBoxBaseComponent { - this.props.addDocTab(this.rootDoc, "onRight"); + this.props.addDocTab(this.rootDoc, "add:right"); } openLinkTargetOnRight = (e: React.MouseEvent) => { const alias = Doc.MakeAlias(Cast(this.layoutDoc[this.fieldKey], Doc, null)); alias.isLinkButton = undefined; alias._isBackground = undefined; alias.layoutKey = "layout"; - this.props.addDocTab(alias, "onRight"); + this.props.addDocTab(alias, "add:right"); } @action openLinkEditor = action((e: React.MouseEvent) => { diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx index f154c5f56..e174a95af 100644 --- a/src/client/views/nodes/LinkDocPreview.tsx +++ b/src/client/views/nodes/LinkDocPreview.tsx @@ -40,7 +40,7 @@ export class LinkDocPreview extends React.Component { async followDefault() { DocumentLinksButton.EditLink = undefined; LinkDocPreview.LinkInfo = undefined; - this._targetDoc ? DocumentManager.Instance.FollowLink(this.props.linkDoc, this._targetDoc, doc => this.props.addDocTab(doc, "onRight"), false) : null; + this._targetDoc ? DocumentManager.Instance.FollowLink(this.props.linkDoc, this._targetDoc, doc => this.props.addDocTab(doc, "add:right"), false) : null; } componentDidUpdate() { this.updatePreview(); } @@ -69,9 +69,9 @@ export class LinkDocPreview extends React.Component { pointerDown = (e: React.PointerEvent) => { if (this.props.linkDoc && this.props.linkSrc) { DocumentManager.Instance.FollowLink(this.props.linkDoc, this.props.linkSrc, - (doc: Doc, followLinkLocation: string) => this.props.addDocTab(doc, e.ctrlKey ? "inTab" : followLinkLocation)); + (doc: Doc, followLinkLocation: string) => this.props.addDocTab(doc, e.ctrlKey ? "add" : followLinkLocation)); } else if (this.props.href) { - this.props.addDocTab(Docs.Create.WebDocument(this.props.href, { title: this.props.href, _width: 200, _height: 400, UseCors: true }), "onRight"); + this.props.addDocTab(Docs.Create.WebDocument(this.props.href, { title: this.props.href, _width: 200, _height: 400, UseCors: true }), "add:right"); } } width = () => Math.min(225, NumCast(this._targetDoc?.[WidthSym](), 225)); diff --git a/src/client/views/nodes/PresBox.tsx b/src/client/views/nodes/PresBox.tsx index f60c521ad..a03c45a53 100644 --- a/src/client/views/nodes/PresBox.tsx +++ b/src/client/views/nodes/PresBox.tsx @@ -238,7 +238,7 @@ export class PresBox extends ViewBoxBaseComponent // If openDocument is selected then it should open the document for the user if (activeItem.openDocument) { - this.props.addDocTab(activeItem, "replace"); + this.props.addDocTab(activeItem, "replace:right"); } else //docToJump stayed same meaning, it was not in the group or was the last element in the group if (activeItem.zoomProgressivize && this.rootDoc.presStatus !== 'edit') { @@ -443,7 +443,7 @@ export class PresBox extends ViewBoxBaseComponent if (this.layoutDoc.inOverlay) { this.layoutDoc.presStatus = 'edit'; Doc.RemoveDocFromList((Doc.UserDoc().myOverlayDocs as Doc), undefined, this.rootDoc); - CollectionDockingView.AddRightSplit(this.rootDoc); + CollectionDockingView.AddSplit(this.rootDoc, "right"); this.layoutDoc.inOverlay = false; } else if (this.layoutDoc.context && docView) { this.layoutDoc.presStatus = 'manual'; @@ -1115,7 +1115,7 @@ export class PresBox extends ViewBoxBaseComponent DockedFrameRenderer.PinDoc(doc, false); this.gotoDocument(this.childDocs.length, this.itemIndex); } else { - this.props.addDocTab(doc, "onRight"); + this.props.addDocTab(doc, "add:right"); } } } diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index 578061344..d50a10bdd 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -82,7 +82,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent { e.stopPropagation(); const collview = await DocUtils.addFieldEnumerations(this._textBoxDoc, this._fieldKey, [{ title: this._fieldKey }]); - collview instanceof Doc && this.props.tbox.props.addDocTab(collview, "onRight"); + collview instanceof Doc && this.props.tbox.props.addDocTab(collview, "add:right"); } @@ -191,7 +191,7 @@ export class DashFieldViewInternal extends React.Component c.heading).indexOf(this._fieldKey) === -1 && list.push(new SchemaHeaderField(this._fieldKey, "#f1efeb")); list.map(c => c.heading).indexOf("text") === -1 && list.push(new SchemaHeaderField("text", "#f1efeb")); alias._pivotField = this._fieldKey.startsWith("#") ? "#" : this._fieldKey; - this.props.tbox.props.addDocTab(alias, "onRight"); + this.props.tbox.props.addDocTab(alias, "add:right"); } } diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 356e36b76..c49e38f72 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -222,7 +222,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp const id = Utils.GenerateDeterministicGuid(this.dataDoc[Id] + key); const allLinks = [{ href: Utils.prepend("/doc/" + id), title: value, targetId: id }]; - const link = this._editorView.state.schema.marks.linkAnchor.create({ allLinks, location: "onRight", title: value }); + const link = this._editorView.state.schema.marks.linkAnchor.create({ allLinks, location: "add:right", title: value }); const mval = this._editorView.state.schema.marks.metadataVal.create(); const offset = (tx.selection.to === range!.end - 1 ? -1 : 0); tx = tx.addMark(textEndSelection - value.length + offset, textEndSelection, link).addMark(textEndSelection - value.length + offset, textEndSelection, mval); @@ -465,7 +465,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp const linkDoc = data.linkDocument!; const anchor1Title = linkDoc.anchor1 instanceof Doc ? StrCast(linkDoc.anchor1.title) : "-untitled-"; const anchor1Id = linkDoc.anchor1 instanceof Doc ? linkDoc.anchor1[Id] : ""; - this.makeLinkToSelection(linkDoc[Id], anchor1Title, "onRight", anchor1Id); + this.makeLinkToSelection(linkDoc[Id], anchor1Title, "add:right", anchor1Id); } getNodeEndpoints(context: Node, node: Node): { from: number, to: number } | null { @@ -811,7 +811,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp if (linkDoc) { const anchor2Title = linkDoc.anchor2 instanceof Doc ? StrCast(linkDoc.anchor2.title) : "-untitled-"; const anchor2Id = linkDoc.anchor2 instanceof Doc ? linkDoc.anchor2[Id] : ""; - this.makeLinkToSelection(linkDoc[Id], anchor2Title, "onRight", anchor2Id); + this.makeLinkToSelection(linkDoc[Id], anchor2Title, "add:right", anchor2Id); } }, { fireImmediately: true } @@ -1096,7 +1096,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp const marks = [...node.marks]; const linkIndex = marks.findIndex(mark => mark.type.name === "link"); const allLinks = [{ href: Utils.prepend(`/doc/${linkId}`), title, linkId }]; - const link = view.state.schema.mark(view.state.schema.marks.linkAnchor, { allLinks, location: "onRight", title, docref: true }); + const link = view.state.schema.mark(view.state.schema.marks.linkAnchor, { allLinks, location: "add:right", title, docref: true }); marks.splice(linkIndex === -1 ? 0 : linkIndex, 1, link); return node.mark(marks); } @@ -1243,10 +1243,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp FormattedTextBoxComment.Hide(); if (FormattedTextBoxComment.linkDoc) { if (FormattedTextBoxComment.linkDoc.type !== DocumentType.LINK) { - this.props.addDocTab(FormattedTextBoxComment.linkDoc, e.ctrlKey ? "inTab" : "onRight"); + this.props.addDocTab(FormattedTextBoxComment.linkDoc, e.ctrlKey ? "add" : "add:right"); } else { DocumentManager.Instance.FollowLink(FormattedTextBoxComment.linkDoc, this.props.Document, - (doc: Doc, followLinkLocation: string) => this.props.addDocTab(doc, e.ctrlKey ? "inTab" : followLinkLocation)); + (doc: Doc, followLinkLocation: string) => this.props.addDocTab(doc, e.ctrlKey ? "add" : followLinkLocation)); } } diff --git a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx index 55d225adc..f41f03620 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx @@ -103,7 +103,7 @@ export class FormattedTextBoxComment { this.deleteLink(); } else if (FormattedTextBoxComment._followRef && FormattedTextBoxComment._followRef.contains(e.target as any)) { if (FormattedTextBoxComment.linkDoc.type !== DocumentType.LINK) { - textBox.props.addDocTab(FormattedTextBoxComment.linkDoc, e.ctrlKey ? "inTab" : "onRight"); + textBox.props.addDocTab(FormattedTextBoxComment.linkDoc, e.ctrlKey ? "add" : "add:right"); } else { const anchor = FieldValue(Doc.AreProtosEqual(FieldValue(Cast(FormattedTextBoxComment.linkDoc.anchor1, Doc)), textBox.dataDoc) ? Cast(FormattedTextBoxComment.linkDoc.anchor2, Doc) : (Cast(FormattedTextBoxComment.linkDoc.anchor1, Doc)) @@ -112,28 +112,28 @@ export class FormattedTextBoxComment { if (FormattedTextBoxComment.linkDoc.follow) { if (FormattedTextBoxComment.linkDoc.follow === "Default") { - DocumentManager.Instance.FollowLink(FormattedTextBoxComment.linkDoc, textBox.props.Document, doc => textBox.props.addDocTab(doc, "onRight"), false); + DocumentManager.Instance.FollowLink(FormattedTextBoxComment.linkDoc, textBox.props.Document, doc => textBox.props.addDocTab(doc, "add:right"), false); } else if (FormattedTextBoxComment.linkDoc.follow === "Always open in right tab") { - if (target) { textBox.props.addDocTab(target, "onRight"); } + if (target) { textBox.props.addDocTab(target, "add:right"); } } else if (FormattedTextBoxComment.linkDoc.follow === "Always open in new tab") { - if (target) { textBox.props.addDocTab(target, "inTab"); } + if (target) { textBox.props.addDocTab(target, "add"); } } } else { - DocumentManager.Instance.FollowLink(FormattedTextBoxComment.linkDoc, textBox.props.Document, doc => textBox.props.addDocTab(doc, "onRight"), false); + DocumentManager.Instance.FollowLink(FormattedTextBoxComment.linkDoc, textBox.props.Document, doc => textBox.props.addDocTab(doc, "add:right"), false); } } } else { if (FormattedTextBoxComment.linkDoc.type !== DocumentType.LINK) { - textBox.props.addDocTab(FormattedTextBoxComment.linkDoc, e.ctrlKey ? "inTab" : "onRight"); + textBox.props.addDocTab(FormattedTextBoxComment.linkDoc, e.ctrlKey ? "add" : "add:right"); } else { DocumentManager.Instance.FollowLink(FormattedTextBoxComment.linkDoc, textBox.props.Document, - (doc: Doc, followLinkLocation: string) => textBox.props.addDocTab(doc, e.ctrlKey ? "inTab" : followLinkLocation)); + (doc: Doc, followLinkLocation: string) => textBox.props.addDocTab(doc, e.ctrlKey ? "add" : followLinkLocation)); } } } } else if (textBox && (FormattedTextBoxComment.tooltipText as any).href) { - textBox.props.addDocTab(Docs.Create.WebDocument((FormattedTextBoxComment.tooltipText as any).href, { title: (FormattedTextBoxComment.tooltipText as any).href, _width: 200, _height: 400, UseCors: true }), "onRight"); + textBox.props.addDocTab(Docs.Create.WebDocument((FormattedTextBoxComment.tooltipText as any).href, { title: (FormattedTextBoxComment.tooltipText as any).href, _width: 200, _height: 400, UseCors: true }), "add:right"); } keep && textBox && FormattedTextBoxComment.start !== undefined && textBox.adoptAnnotation( FormattedTextBoxComment.start, FormattedTextBoxComment.end, FormattedTextBoxComment.mark); diff --git a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts index a65eea50f..c6bacc1a8 100644 --- a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts +++ b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts @@ -104,7 +104,7 @@ export function buildKeymap>(schema: S, props: any, mapKey //Command to create a new Tab with a PDF of all the command shortcuts bind("Mod-/", (state: EditorState, dispatch: (tx: Transaction) => void) => { const newDoc = Docs.Create.PdfDocument(Utils.prepend("/assets/cheat-sheet.pdf"), { _fitWidth: true, _width: 300, _height: 300 }); - props.addDocTab(newDoc, "onRight"); + props.addDocTab(newDoc, "add:right"); }); //Commands to modify BlockType diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx index 399f72608..307238ea1 100644 --- a/src/client/views/nodes/formattedText/RichTextMenu.tsx +++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx @@ -810,7 +810,7 @@ export class RichTextMenu extends AntimodeMenu {

Linked to:

- +
; @@ -857,7 +857,7 @@ export class RichTextMenu extends AntimodeMenu { // TODO: should check for valid URL @undoBatch makeLinkToURL = (target: string, lcoation: string) => { - ((this.view as any)?.TextView as FormattedTextBox).makeLinkToSelection("", target, "onRight", "", target); + ((this.view as any)?.TextView as FormattedTextBox).makeLinkToSelection("", target, "onRadd:rightight", "", target); } @undoBatch diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts index 7e632a0ee..3fadfe842 100644 --- a/src/client/views/nodes/formattedText/RichTextRules.ts +++ b/src/client/views/nodes/formattedText/RichTextRules.ts @@ -279,7 +279,7 @@ export class RichTextRules { DocUtils.Publish(target, docid, returnFalse, returnFalse); DocUtils.MakeLink({ doc: this.Document }, { doc: target }, "portal to"); }); - const link = state.schema.marks.linkAnchor.create({ href: Utils.prepend("/doc/" + docid), location: "onRight", title: docid, targetId: docid }); + const link = state.schema.marks.linkAnchor.create({ href: Utils.prepend("/doc/" + docid), location: "add:right", title: docid, targetId: docid }); return state.tr.deleteRange(end - 1, end).deleteRange(start, start + 2).addMark(start, end - 3, link); } return state.tr; diff --git a/src/client/views/nodes/formattedText/nodes_rts.ts b/src/client/views/nodes/formattedText/nodes_rts.ts index 1616500f6..64f7d27e5 100644 --- a/src/client/views/nodes/formattedText/nodes_rts.ts +++ b/src/client/views/nodes/formattedText/nodes_rts.ts @@ -148,7 +148,7 @@ export const nodes: { [index: string]: NodeSpec } = { alt: { default: null }, title: { default: null }, float: { default: "left" }, - location: { default: "onRight" }, + location: { default: "add:right" }, docid: { default: "" } }, group: "inline", @@ -177,7 +177,7 @@ export const nodes: { [index: string]: NodeSpec } = { height: { default: 100 }, title: { default: null }, float: { default: "right" }, - location: { default: "onRight" }, + location: { default: "add:right" }, hidden: { default: false }, fieldKey: { default: "" }, docid: { default: "" }, diff --git a/src/client/views/pdf/Annotation.tsx b/src/client/views/pdf/Annotation.tsx index 98638ecc2..a33068a18 100644 --- a/src/client/views/pdf/Annotation.tsx +++ b/src/client/views/pdf/Annotation.tsx @@ -99,7 +99,7 @@ class RegionAnnotation extends React.Component { else if (e.button === 0) { const annoGroup = await Cast(this.props.document.group, Doc); if (annoGroup) { - DocumentManager.Instance.FollowLink(undefined, annoGroup, (doc, followLinkLocation) => this.props.addDocTab(doc, e.ctrlKey ? "inTab" : followLinkLocation), false, undefined); + DocumentManager.Instance.FollowLink(undefined, annoGroup, (doc, followLinkLocation) => this.props.addDocTab(doc, e.ctrlKey ? "add" : followLinkLocation), false, undefined); e.stopPropagation(); } } diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index ed33ac822..d89e66ec5 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -604,7 +604,7 @@ export class PDFViewer extends ViewBoxAnnotatableComponent { -- cgit v1.2.3-70-g09d2 From 442572a66aaa6b50d8d01d46d79b7d6cf2f247a8 Mon Sep 17 00:00:00 2001 From: bobzel Date: Sat, 29 Aug 2020 17:42:48 -0400 Subject: fixed maximize docdecoration to toggle target w/o making an alias. use ctrlKey to make an alias. removed dashboardBrush stuff. cleaned up DocDecoartions css slightly --- src/client/util/CurrentUserUtils.ts | 9 ++- src/client/views/DocumentDecorations.scss | 84 ++++++++++++---------- src/client/views/DocumentDecorations.tsx | 82 +++++++++------------ src/client/views/MainView.tsx | 17 +++-- .../views/collections/CollectionDockingView.tsx | 13 ++-- .../views/collections/CollectionTreeView.tsx | 2 +- src/client/views/collections/CollectionView.tsx | 2 +- .../CollectionFreeFormLayoutEngines.tsx | 3 +- .../collectionGrid/CollectionGridView.scss | 11 --- src/client/views/nodes/DocumentView.tsx | 7 +- .../views/nodes/formattedText/DashDocView.tsx | 5 +- .../views/nodes/formattedText/RichTextSchema.tsx | 5 +- src/client/views/search/SearchBox.tsx | 2 +- 13 files changed, 117 insertions(+), 125 deletions(-) (limited to 'src/client/util/CurrentUserUtils.ts') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 20f8fe9c1..2ca395164 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -1020,7 +1020,7 @@ export class CurrentUserUtils { } public static snapshotDashboard = (userDoc: Doc) => { - const copy = CollectionDockingView.Copy(Cast(userDoc.activeDashboard, Doc, null)); + const copy = CollectionDockingView.Copy(CurrentUserUtils.ActiveDashboard); Doc.AddDocToList(Cast(userDoc.myDashboards, Doc, null), "data", copy); CurrentUserUtils.openDashboard(userDoc, copy); } @@ -1031,7 +1031,11 @@ export class CurrentUserUtils { const dashboards = Cast(userDoc.myDashboards, Doc) as Doc; const dashboardCount = DocListCast(dashboards.data).length + 1; const emptyPane = Cast(userDoc.emptyPane, Doc, null); - emptyPane["dragFactory-count"] = NumCast(emptyPane["dragFactory-count"]) + 1; + try { + emptyPane["dragFactory-count"] = NumCast(emptyPane["dragFactory-count"]) + 1; + } catch (e) { + console.log(e) + } const freeformOptions: DocumentOptions = { x: 0, y: 400, @@ -1054,6 +1058,7 @@ export class CurrentUserUtils { CurrentUserUtils.openDashboard(userDoc, dashboardDoc); } + public static get ActiveDashboard() { return Cast(Doc.UserDoc().activeDashboard, Doc, null); } public static get ActivePresentation() { return Cast(Doc.UserDoc().activePresentation, Doc, null); } public static get MyRecentlyClosed() { return Cast(Doc.UserDoc().myRecentlyClosedDocs, Doc, null); } public static get MyDashboards() { return Cast(Doc.UserDoc().myDashboards, Doc, null); } diff --git a/src/client/views/DocumentDecorations.scss b/src/client/views/DocumentDecorations.scss index b5c32ca1d..8291d7212 100644 --- a/src/client/views/DocumentDecorations.scss +++ b/src/client/views/DocumentDecorations.scss @@ -16,21 +16,11 @@ $linkGap : 3px; grid-template-columns: 8px 16px 1fr 8px 8px; pointer-events: none; - #documentDecorations-centerCont { + .documentDecorations-centerCont { grid-column: 3; background: none; } - .documentDecorations-resizer { - pointer-events: auto; - background: $alt-accent; - opacity: 0.1; - } - - .documentDecorations-resizer:hover { - opacity: 1; - } - .documentDecorations-selector { pointer-events: auto; height: 15px; @@ -49,27 +39,43 @@ $linkGap : 3px; grid-row: 4; } - #documentDecorations-topLeftResizer, - #documentDecorations-leftResizer, - #documentDecorations-bottomLeftResizer { + .documentDecorations-topLeftResizer, + .documentDecorations-topRightResizer, + .documentDecorations-bottomLeftResizer, + .documentDecorations-bottomRightResizer, + .documentDecorations-leftResizer, + .documentDecorations-topResizer, + .documentDecorations-bottomResizer, + .documentDecorations-rightResizer { + pointer-events: auto; + background: $alt-accent; + opacity: 0.1; + &:hover { + opacity: 1; + } + } + + .documentDecorations-topLeftResizer, + .documentDecorations-leftResizer, + .documentDecorations-bottomLeftResizer { grid-column: 1 } - #documentDecorations-topResizer, - #documentDecorations-bottomResizer { + .documentDecorations-topResizer, + .documentDecorations-bottomResizer { grid-column-start: 2; grid-column-end: 5; } - #documentDecorations-bottomRightResizer, - #documentDecorations-topRightResizer, - #documentDecorations-rightResizer { + .documentDecorations-bottomRightResizer, + .documentDecorations-topRightResizer, + .documentDecorations-rightResizer { grid-column-start: 5; grid-column-end: 7; } - #documentDecorations-rotation, - #documentDecorations-borderRadius { + .documentDecorations-rotation, + .documentDecorations-borderRadius { grid-column: 5; grid-row: 4; border-radius: 100%; @@ -87,69 +93,69 @@ $linkGap : 3px; position: absolute; } } - #documentDecorations-rotation { + .documentDecorations-rotation { background: transparent; right: -15; } - #documentDecorations-topLeftResizer, - #documentDecorations-bottomRightResizer { + .documentDecorations-topLeftResizer, + .documentDecorations-bottomRightResizer { cursor: nwse-resize; background: unset; opacity: 1; } - #documentDecorations-topLeftResizer { + .documentDecorations-topLeftResizer { border-left: 2px solid; border-top: solid 2px; } - #documentDecorations-bottomRightResizer { + .documentDecorations-bottomRightResizer { border-right: 2px solid; border-bottom: solid 2px; } - #documentDecorations-topLeftResizer:hover, - #documentDecorations-bottomRightResizer:hover { + .documentDecorations-topLeftResizer:hover, + .documentDecorations-bottomRightResizer:hover { opacity: 1; } - #documentDecorations-bottomRightResizer { + .documentDecorations-bottomRightResizer { grid-row: 4; } - #documentDecorations-topRightResizer, - #documentDecorations-bottomLeftResizer { + .documentDecorations-topRightResizer, + .documentDecorations-bottomLeftResizer { cursor: nesw-resize; background: unset; opacity: 1; } - #documentDecorations-topRightResizer { + .documentDecorations-topRightResizer { border-right: 2px solid; border-top: 2px solid; } - #documentDecorations-bottomLeftResizer { + .documentDecorations-bottomLeftResizer { border-left: 2px solid; border-bottom: 2px solid; } - #documentDecorations-topRightResizer:hover, - #documentDecorations-bottomLeftResizer:hover { + .documentDecorations-topRightResizer:hover, + .documentDecorations-bottomLeftResizer:hover { cursor: nesw-resize; background: dimGray; opacity: 1; } - #documentDecorations-topResizer, - #documentDecorations-bottomResizer { + .documentDecorations-topResizer, + .documentDecorations-bottomResizer { cursor: ns-resize; } - #documentDecorations-leftResizer, - #documentDecorations-rightResizer { + .documentDecorations-leftResizer, + .documentDecorations-rightResizer { cursor: ew-resize; } diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index c7a24133c..6db5186ba 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -24,6 +24,7 @@ import { FormatShapePane } from './FormatShapePane'; import { DocumentView } from "./nodes/DocumentView"; import React = require("react"); import e = require('express'); +import { CurrentUserUtils } from '../util/CurrentUserUtils'; @observer export class DocumentDecorations extends React.Component<{}, { value: string }> { @@ -177,7 +178,17 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> } @action onMaximizeDown = (e: React.PointerEvent): void => { - setupMoveUpEvents(this, e, (e, d) => false, (e) => { }, this.onMaximizeClick); + if (e.ctrlKey) { + const selectedDocs = SelectionManager.SelectedDocuments(); + const alias = Doc.MakeAlias(selectedDocs[0].props.Document); + alias.context = undefined; + //CollectionDockingView.Instance?.OpenFullScreen(selectedDocs[0]); + CollectionDockingView.AddSplit(alias, "right"); + e.stopPropagation(); + e.preventDefault(); + } else { + setupMoveUpEvents(this, e, (e, d) => false, (e) => { }, this.onMaximizeClick); + } } @undoBatch @action @@ -185,10 +196,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> if (e.button === 0) { const selectedDocs = SelectionManager.SelectedDocuments(); if (selectedDocs.length) { - const alias = Doc.MakeAlias(selectedDocs[0].props.Document); - alias.context = undefined; - //CollectionDockingView.Instance?.OpenFullScreen(selectedDocs[0]); - CollectionDockingView.AddSplit(alias, "right"); + CollectionDockingView.ToggleSplit(selectedDocs[0].props.Document, "right"); } } SelectionManager.DeselectAll(); @@ -333,7 +341,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> setupMoveUpEvents(this, e, this.onPointerMove, this.onPointerUp, (e) => { }); if (e.button === 0) { - this._resizeHdlId = e.currentTarget.id; + this._resizeHdlId = e.currentTarget.className; const bounds = e.currentTarget.getBoundingClientRect(); this._offX = this._resizeHdlId.toLowerCase().includes("left") ? bounds.right - e.clientX : bounds.left - e.clientX; this._offY = this._resizeHdlId.toLowerCase().includes("top") ? bounds.bottom - e.clientY : bounds.top - e.clientY; @@ -575,7 +583,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> return ; } render() { - const darkScheme = Cast(Doc.UserDoc().activeDashboard, Doc, null)?.darkScheme ? "dimgray" : undefined; + const darkScheme = CurrentUserUtils.ActiveDashboard?.darkScheme ? "dimgray" : undefined; const bounds = this.Bounds; const seldoc = SelectionManager.SelectedDocuments().length ? SelectionManager.SelectedDocuments()[0] : undefined; if (SnappingManager.GetIsDragging() || bounds.r - bounds.x < 1 || bounds.x === Number.MAX_VALUE || !seldoc || this._hidden || isNaN(bounds.r) || isNaN(bounds.b) || isNaN(bounds.x) || isNaN(bounds.y)) { @@ -587,35 +595,21 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> }); const minimal = bounds.r - bounds.x < 100 ? true : false; const maximizeIcon = minimal ? ( -
Show context menu
} placement="top"> + Show context menu
} placement="top">
) : canDelete ? ( -
Close
} placement="top"> + Close} placement="top">
{/* Currently, this is set to be enabled if there is no ink selected. It might be interesting to think about minimizing ink if it's useful? -syip2*/}
) : (null); const titleArea = this._edtingTitle ? + this.titleBlur(true)} onChange={action(e => this._accumulatedTitle = e.target.value)} onKeyPress={this.titleEntered} /> : <> - this.titleBlur(true)} onChange={action(e => this._accumulatedTitle = e.target.value)} onKeyPress={this.titleEntered} /> - {minimal ? (null) :
{ - // if (!seldoc.props.Document["title-custom"]) { - // seldoc.props.Document["title-custom"] = true; - // StrCast(Doc.GetProto(seldoc.props.Document).title).startsWith("-") && (Doc.GetProto(seldoc.props.Document).title = StrCast(seldoc.props.Document.title).substring(1)); - // this._accumulatedTitle = StrCast(seldoc.props.Document.title); - // } - // DocUtils.Publish(seldoc.props.Document, this._accumulatedTitle, seldoc.props.addDocument, seldoc.props.removeDocument); - // })} - > - {/* */} -
} - : - <> - {minimal ? (null) :
Show context menu
} placement="top">
+ {minimal ? (null) : Show context menu
} placement="top">
}
@@ -656,37 +650,29 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> {maximizeIcon} {titleArea} {SelectionManager.SelectedDocuments().length !== 1 || seldoc.Document.type === DocumentType.INK ? (null) : -
{`${seldoc.finalLayoutKey.includes("icon") ? "De" : ""}Iconify Document`}
} placement="top"> + {`${seldoc.finalLayoutKey.includes("icon") ? "De" : ""}Iconify Document`}
} placement="top">
} -
Open In a New Pane
} placement="top">
+ Open In a New Pane
} placement="top">
{ e.preventDefault(); e.stopPropagation(); }} onPointerDown={this.onMaximizeDown}> {SelectionManager.SelectedDocuments().length === 1 ? : "..."}
-
e.preventDefault()}>
-
e.preventDefault()}>
-
e.preventDefault()}>
-
e.preventDefault()}>
-
-
e.preventDefault()}>
-
e.preventDefault()}>
-
e.preventDefault()}>
-
e.preventDefault()}>
+
e.preventDefault()} /> +
e.preventDefault()} /> +
e.preventDefault()} /> +
e.preventDefault()} /> +
+
e.preventDefault()} /> +
e.preventDefault()} /> +
e.preventDefault()} /> +
e.preventDefault()} /> {seldoc.props.renderDepth <= 1 || !seldoc.props.ContainingCollectionView ? (null) : -
tap to select containing document
} placement="top"> -
tap to select containing document
} placement="top"> +
e.preventDefault()}>
} -
e.preventDefault()}>{useRotation && "⟲"}
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 7b0c6fa9f..83d9a3498 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -13,7 +13,7 @@ import { Doc, DocListCast, Field, Opt } from '../../fields/Doc'; import { List } from '../../fields/List'; import { PrefetchProxy } from '../../fields/Proxy'; import { listSpec } from '../../fields/Schema'; -import { BoolCast, Cast, FieldValue, StrCast } from '../../fields/Types'; +import { BoolCast, Cast, FieldValue, StrCast, PromiseValue } from '../../fields/Types'; import { TraceMobx } from '../../fields/util'; import { emptyFunction, emptyPath, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnOne, returnTrue, returnZero, setupMoveUpEvents, simulateMouseClick, Utils } from '../../Utils'; import { GoogleAuthenticationManager } from '../apis/GoogleAuthenticationManager'; @@ -75,10 +75,10 @@ export class MainView extends React.Component { @observable private _panelHeight: number = 0; @observable private _flyoutTranslate: boolean = false; @observable public flyoutWidth: number = 0; - private get darkScheme() { return BoolCast(Cast(this.userDoc?.activeDashboard, Doc, null)?.darkScheme); } + private get darkScheme() { return BoolCast(CurrentUserUtils.ActiveDashboard?.darkScheme); } @computed private get userDoc() { return Doc.UserDoc(); } - @computed private get mainContainer() { return this.userDoc ? FieldValue(Cast(this.userDoc.activeDashboard, Doc)) : CurrentUserUtils.GuestDashboard; } + @computed private get mainContainer() { return this.userDoc ? CurrentUserUtils.ActiveDashboard : CurrentUserUtils.GuestDashboard; } @computed public get mainFreeform(): Opt { return (docs => (docs && docs.length > 1) ? docs[1] : undefined)(DocListCast(this.mainContainer!.data)); } @computed public get searchDoc() { return Cast(this.userDoc.mySearchPanelDoc, Doc) as Doc; } @@ -226,8 +226,7 @@ export class MainView extends React.Component { // Load the user's active dashboard, or create a new one if initial session after signup const received = CurrentUserUtils.MainDocId; if (received && !this.userDoc) { - reaction( - () => CurrentUserUtils.GuestTarget, + reaction(() => CurrentUserUtils.GuestTarget, target => target && CurrentUserUtils.createNewDashboard(Doc.UserDoc()), { fireImmediately: true } ); @@ -241,11 +240,11 @@ export class MainView extends React.Component { }), ); } - const doc = this.userDoc && await Cast(this.userDoc.activeDashboard, Doc); - if (doc) { - CurrentUserUtils.openDashboard(Doc.UserDoc(), doc); + const activeDash = PromiseValue(this.userDoc.activeDashboard); + if (activeDash) { + activeDash.then(dash => dash instanceof Doc && CurrentUserUtils.openDashboard(this.userDoc, dash)); } else { - CurrentUserUtils.createNewDashboard(Doc.UserDoc()); + CurrentUserUtils.createNewDashboard(this.userDoc); } } } diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 62c865436..93e77e0a2 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -140,6 +140,13 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) { return CollectionDockingView.AddSplit(document, panelName, stack, panelName); } + + @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 // @@ -262,8 +269,6 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) { config => { if (!this._goldenLayout || this._ignoreStateChange !== config) { this.setupGoldenLayout(); - DocListCast(CurrentUserUtils.MyDashboards.data).map(d => d.dashboardBrush = false); - this.props.Document.dashboardBrush = true; } this._ignoreStateChange = ""; }); @@ -274,7 +279,6 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) { componentWillUnmount: () => void = () => { try { - this.props.Document.dashboardBrush = false; this._goldenLayout.unbind('stackCreated', this.stackCreated); this._goldenLayout.unbind('tabDestroyed', this.tabDestroyed); } catch (e) { } @@ -619,8 +623,7 @@ export class DockedFrameRenderer extends React.Component { case "replace": return CollectionDockingView.ReplaceTab(doc, locationParams, this.stack); case "inPlace": case "add": - default: return Array.from(CollectionDockingView.Instance.tabMap.keys()).findIndex((tab) => tab.DashDoc === doc) !== -1 ? - CollectionDockingView.CloseSplit(doc) : CollectionDockingView.AddSplit(doc, locationParams, this.stack); + default: return CollectionDockingView.ToggleSplit(doc, locationParams, this.stack); } } diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 80a6afba7..84fd4cbe8 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -483,7 +483,7 @@ class TreeView extends React.Component { style={{ fontWeight: Doc.IsSearchMatch(this.doc) !== undefined ? "bold" : undefined, textDecoration: Doc.GetT(this.doc, "title", "string", true) ? "underline" : undefined, - outline: BoolCast(this.doc.dashboardBrush) ? "dashed 1px #06123232" : undefined, + outline: this.doc === CurrentUserUtils.ActiveDashboard ? "dashed 1px #06123232" : undefined, pointerEvents: !this.props.active() && !SnappingManager.GetIsDragging() ? "none" : undefined }} > {view} diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 64ad3abbf..81403de46 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -583,7 +583,7 @@ export class CollectionView extends Touchable {this.showIsTagged()} diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx index 646ae94fb..bc2cb2d20 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx @@ -10,6 +10,7 @@ import { Id, ToString } from "../../../../fields/FieldSymbols"; import { ObjectField } from "../../../../fields/ObjectField"; import { RefField } from "../../../../fields/RefField"; import { listSpec } from "../../../../fields/Schema"; +import { CurrentUserUtils } from "../../../util/CurrentUserUtils"; export interface ViewDefBounds { type: string; @@ -359,7 +360,7 @@ export function computeTimelineLayout( groupNames.push({ type: "text", text: toLabel(Math.ceil(maxTime)), x: Math.ceil(maxTime - minTime) * scaling, y: 0, height: fontHeight, fontSize, payload: undefined }); } - const divider = { type: "div", color: Cast(Doc.UserDoc().activeDashboard, Doc, null)?.darkScheme ? "dimGray" : "black", x: 0, y: 0, width: panelDim[0], height: -1, payload: undefined }; + const divider = { type: "div", color: CurrentUserUtils.ActiveDashboard?.darkScheme ? "dimGray" : "black", x: 0, y: 0, width: panelDim[0], height: -1, payload: undefined }; return normalizeResults(panelDim, fontHeight, docMap, poolData, viewDefsToJSX, groupNames, (maxTime - minTime) * scaling, [divider]); function layoutDocsAtTime(keyDocs: Doc[], key: number) { diff --git a/src/client/views/collections/collectionGrid/CollectionGridView.scss b/src/client/views/collections/collectionGrid/CollectionGridView.scss index 4d8473be9..60ec02f47 100644 --- a/src/client/views/collections/collectionGrid/CollectionGridView.scss +++ b/src/client/views/collections/collectionGrid/CollectionGridView.scss @@ -134,17 +134,6 @@ } -// .documentDecorations-container .documentDecorations-resizer { -// pointer-events: none; -// } - -// #documentDecorations-bottomRightResizer, -// #documentDecorations-bottomLeftResizer, -// #documentDecorations-topRightResizer, -// #documentDecorations-topLeftResizer { -// visibility: collapse; -// } - /* Chrome, Safari, Edge, Opera */ input::-webkit-outer-spin-button, diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 48a1688a6..f4eb71145 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -39,6 +39,7 @@ import { LinkDescriptionPopup } from './LinkDescriptionPopup'; import { RadialMenu } from './RadialMenu'; import { TaskCompletionBox } from './TaskCompletedBox'; import React = require("react"); +import { CurrentUserUtils } from '../../util/CurrentUserUtils'; export type DocFocusFunc = () => boolean; @@ -588,7 +589,7 @@ export class DocumentView extends DocComponent(Docu @undoBatch @action deleteClicked = (): void => { - if (Doc.UserDoc().activeDashboard === this.props.Document) { + if (CurrentUserUtils.ActiveDashboard === this.props.Document) { alert("Can't delete the active dashboard"); } else { const selected = SelectionManager.SelectedDocuments().slice(); @@ -625,7 +626,7 @@ export class DocumentView extends DocComponent(Docu @undoBatch @action drop = async (e: Event, de: DragManager.DropEvent) => { - if (this.props.Document === Doc.UserDoc().activeDashboard) { + if (this.props.Document === CurrentUserUtils.ActiveDashboard) { alert("linking to document tabs not yet supported. Drop link on document content."); return; } @@ -1041,7 +1042,7 @@ export class DocumentView extends DocComponent(Docu const fullDegree = Doc.isBrushedHighlightedDegree(this.props.Document); const borderRounding = this.layoutDoc.borderRounding; const localScale = fullDegree; - const highlightColors = Cast(Doc.UserDoc().activeDashboard, Doc, null)?.darkScheme ? + const highlightColors = CurrentUserUtils.ActiveDashboard?.darkScheme ? ["transparent", "#65350c", "#65350c", "yellow", "magenta", "cyan", "orange"] : ["transparent", "maroon", "maroon", "yellow", "magenta", "cyan", "orange"]; const highlightStyles = ["solid", "dashed", "solid", "solid", "solid", "solid", "solid"]; diff --git a/src/client/views/nodes/formattedText/DashDocView.tsx b/src/client/views/nodes/formattedText/DashDocView.tsx index 08bab8d12..3b77735a7 100644 --- a/src/client/views/nodes/formattedText/DashDocView.tsx +++ b/src/client/views/nodes/formattedText/DashDocView.tsx @@ -12,6 +12,7 @@ import { DocumentView } from "../DocumentView"; import { FormattedTextBox } from "./FormattedTextBox"; import { Transform } from "../../../util/Transform"; import React = require("react"); +import { CurrentUserUtils } from "../../../util/CurrentUserUtils"; interface IDashDocView { node: any; @@ -175,7 +176,7 @@ export class DashDocView extends React.Component { const outerStyle = { position: "relative" as "relative", textIndent: "0", - border: "1px solid " + StrCast(this._textBox.Document.color, (Cast(Doc.UserDoc().activeDashboard, Doc, null).darkScheme ? "dimGray" : "lightGray")), + border: "1px solid " + StrCast(this._textBox.Document.color, (CurrentUserUtils.ActiveDashboard.darkScheme ? "dimGray" : "lightGray")), width: this.props.node.props.width, height: this.props.node.props.height, display: this.props.node.props.hidden ? "none" : "inline-block", @@ -202,7 +203,7 @@ export class DashDocView extends React.Component { ({ dim, color }) => { spanStyle.width = outerStyle.width = Math.max(20, dim[0]) + "px"; spanStyle.height = outerStyle.height = Math.max(20, dim[1]) + "px"; - outerStyle.border = "1px solid " + StrCast(finalLayout.color, (Cast(Doc.UserDoc().activeDashboard, Doc, null).darkScheme ? "dimGray" : "lightGray")); + outerStyle.border = "1px solid " + StrCast(finalLayout.color, (CurrentUserUtils.ActiveDashboard.darkScheme ? "dimGray" : "lightGray")); }, { fireImmediately: true }); if (node.attrs.width !== dashDoc._width + "px" || node.attrs.height !== dashDoc._height + "px") { diff --git a/src/client/views/nodes/formattedText/RichTextSchema.tsx b/src/client/views/nodes/formattedText/RichTextSchema.tsx index b76e520b7..f0bacb735 100644 --- a/src/client/views/nodes/formattedText/RichTextSchema.tsx +++ b/src/client/views/nodes/formattedText/RichTextSchema.tsx @@ -13,6 +13,7 @@ import { Transform } from "../../../util/Transform"; import { DocumentView } from "../DocumentView"; import { FormattedTextBox } from "./FormattedTextBox"; import React = require("react"); +import { CurrentUserUtils } from "../../../util/CurrentUserUtils"; export class DashDocView { @@ -43,7 +44,7 @@ export class DashDocView { this._outer = document.createElement("span"); this._outer.style.position = "relative"; this._outer.style.textIndent = "0"; - this._outer.style.border = "1px solid " + StrCast(tbox.layoutDoc.color, (Cast(Doc.UserDoc().activeDashboard, Doc, null).darkScheme ? "dimGray" : "lightGray")); + this._outer.style.border = "1px solid " + StrCast(tbox.layoutDoc.color, (CurrentUserUtils.ActiveDashboard.darkScheme ? "dimGray" : "lightGray")); this._outer.style.width = node.attrs.width; this._outer.style.height = node.attrs.height; this._outer.style.display = node.attrs.hidden ? "none" : "inline-block"; @@ -126,7 +127,7 @@ export class DashDocView { this._reactionDisposer = reaction(() => ({ dim: [finalLayout[WidthSym](), finalLayout[HeightSym]()], color: finalLayout.color }), ({ dim, color }) => { this._dashSpan.style.width = this._outer.style.width = Math.max(20, dim[0]) + "px"; this._dashSpan.style.height = this._outer.style.height = Math.max(20, dim[1]) + "px"; - this._outer.style.border = "1px solid " + StrCast(finalLayout.color, (Cast(Doc.UserDoc().activeDashboard, Doc, null).darkScheme ? "dimGray" : "lightGray")); + this._outer.style.border = "1px solid " + StrCast(finalLayout.color, (CurrentUserUtils.ActiveDashboard.darkScheme ? "dimGray" : "lightGray")); }, { fireImmediately: true }); const doReactRender = (finalLayout: Doc, resolvedDataDoc: Doc) => { diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx index d7a8ed404..3926208ae 100644 --- a/src/client/views/search/SearchBox.tsx +++ b/src/client/views/search/SearchBox.tsx @@ -507,7 +507,7 @@ export class SearchBox extends ViewBoxBaseComponent
CurrentUserUtils.createNewDashboard(Doc.UserDoc()))}> -- cgit v1.2.3-70-g09d2 From 51f9da2bb6529c102c300a1ceb4ccd23065a76fc Mon Sep 17 00:00:00 2001 From: bobzel Date: Sat, 29 Aug 2020 17:58:32 -0400 Subject: split out TabDocView from CollectionDockingView (where it had been called DockedFrameRenderer) --- src/client/util/CurrentUserUtils.ts | 6 +- src/client/views/DocumentButtonBar.tsx | 5 +- src/client/views/PropertiesButtons.tsx | 2 +- .../views/collections/CollectionDockingView.scss | 31 -- .../views/collections/CollectionDockingView.tsx | 370 +------------------- src/client/views/collections/CollectionMenu.tsx | 10 +- src/client/views/collections/TabDocView.scss | 20 ++ src/client/views/collections/TabDocView.tsx | 379 +++++++++++++++++++++ src/client/views/nodes/PresBox.tsx | 43 ++- src/mobile/MobileInterface.tsx | 4 +- 10 files changed, 437 insertions(+), 433 deletions(-) create mode 100644 src/client/views/collections/TabDocView.scss create mode 100644 src/client/views/collections/TabDocView.tsx (limited to 'src/client/util/CurrentUserUtils.ts') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 2ca395164..260552879 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -1031,11 +1031,7 @@ export class CurrentUserUtils { const dashboards = Cast(userDoc.myDashboards, Doc) as Doc; const dashboardCount = DocListCast(dashboards.data).length + 1; const emptyPane = Cast(userDoc.emptyPane, Doc, null); - try { - emptyPane["dragFactory-count"] = NumCast(emptyPane["dragFactory-count"]) + 1; - } catch (e) { - console.log(e) - } + emptyPane["dragFactory-count"] = NumCast(emptyPane["dragFactory-count"]) + 1; const freeformOptions: DocumentOptions = { x: 0, y: 400, diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx index 764a2547c..45336129a 100644 --- a/src/client/views/DocumentButtonBar.tsx +++ b/src/client/views/DocumentButtonBar.tsx @@ -15,7 +15,8 @@ import { CurrentUserUtils } from '../util/CurrentUserUtils'; import { DragManager } from '../util/DragManager'; import { SelectionManager } from '../util/SelectionManager'; import { SharingManager } from '../util/SharingManager'; -import { CollectionDockingView, DockedFrameRenderer } from './collections/CollectionDockingView'; +import { CollectionDockingView } from './collections/CollectionDockingView'; +import { TabDocView } from './collections/TabDocView'; import './DocumentButtonBar.scss'; import { MetadataEntryMenu } from './MetadataEntryMenu'; import { DocumentLinksButton } from './nodes/DocumentLinksButton'; @@ -194,7 +195,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV return !targetDoc ? (null) :
{Doc.isDocPinned(targetDoc) ? "Unpin from presentation" : "Pin to presentation"}
}>
this.props.views().map(view => view && DockedFrameRenderer.PinDoc(view.props.Document, isPinned))}> + onClick={e => this.props.views().map(view => view && TabDocView.PinDoc(view.props.Document, isPinned))}>
; diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx index 6ac6571d3..dcd3d47cf 100644 --- a/src/client/views/PropertiesButtons.tsx +++ b/src/client/views/PropertiesButtons.tsx @@ -160,7 +160,7 @@ export class PropertiesButtons extends React.Component<{}, {}> { googleDoc = Docs.Create.WebDocument(googleDocUrl, options); dataDoc.googleDoc = googleDoc; } - CollectionDockingView.AddRight(googleDoc, "right"); + CollectionDockingView.AddSplit(googleDoc, "right"); } else if (e.altKey) { e.preventDefault(); window.open(googleDocUrl); diff --git a/src/client/views/collections/CollectionDockingView.scss b/src/client/views/collections/CollectionDockingView.scss index f1a29bd13..d3be1636d 100644 --- a/src/client/views/collections/CollectionDockingView.scss +++ b/src/client/views/collections/CollectionDockingView.scss @@ -1,24 +1,5 @@ @import "../../views/globalCssVariables.scss"; -.miniMap { - position: absolute; - overflow: hidden; - right: 10; - bottom: 10; - border: solid 1px; - box-shadow: black 0.4vw 0.4vw 0.8vw; - - .miniOverlay { - width: 100%; - height: 100%; - position: absolute; - - .miniThumb { - background: #25252525; - position: absolute; - } - } -} .lm_title { margin-top: 3px; @@ -72,18 +53,6 @@ display: inline; } -.messageCounter { - width: 18px; - height: 20px; - text-align: center; - border-radius: 20px; - margin-left: 5px; - transform: translate(0px, -8px); - display: inline-block; - background: transparent; - border: 1px #999999 solid; -} - .collectiondockingview-container { width: 100%; height: 100%; diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 93e77e0a2..d8e95bcdc 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -1,37 +1,25 @@ import 'golden-layout/src/css/goldenlayout-base.css'; import 'golden-layout/src/css/goldenlayout-dark-theme.css'; -import { clamp, pull } 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 { DataSym, Doc, DocListCast, 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, returnFalse, returnOne, returnTrue, returnZero, setupMoveUpEvents, Utils } from "../../../Utils"; import { DocServer } from "../../DocServer"; import { Docs } from '../../documents/Documents'; import { CurrentUserUtils } from '../../util/CurrentUserUtils'; -import { DocumentManager } from '../../util/DocumentManager'; -import { DragManager, dropActionType } from "../../util/DragManager"; +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, UndoManager } from "../../util/UndoManager"; -import { DocumentView } from "../nodes/DocumentView"; -import { PresBox } from '../nodes/PresBox'; import "./CollectionDockingView.scss"; -import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormView'; import { CollectionSubView, SubCollectionViewProps } from "./CollectionSubView"; import { CollectionViewType } from './CollectionView'; -import { CollectionDockingViewMenu } from './CollectionDockingViewMenu'; +import { TabDocView } from './TabDocView'; import React = require("react"); const _global = (window /* browser */ || global /* node */) as any; @@ -249,7 +237,7 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) { 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 { @@ -407,354 +395,6 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) { } } -interface DockedFrameProps { - documentId: FieldId; - glContainer: any; -} -@observer -export class DockedFrameRenderer extends React.Component { - _mainCont: HTMLDivElement | null = null; - _tabReaction: IReactionDisposer | undefined; - @observable private _panelWidth = 0; - @observable private _panelHeight = 0; - @observable private _isActive: boolean = false; - @observable private _document: Doc | undefined; - @observable private _view: DocumentView | undefined; - - get stack(): any { return (this.props as any).glContainer.parent.parent; } - get tab() { return (this.props as any).glContainer.tab; } - get view() { return this._view; } - - @action - init = (tab: any, doc: Opt) => { - 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; - - CollectionDockingView.Instance.tabMap.add(tab); - 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; - }; - 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) => { - e.target.className !== "lm_close_tab" && this.view && SelectionManager.SelectDoc(this.view, false); - }; - // 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); - }; - const onDown = (e: React.PointerEvent) => { - setupMoveUpEvents(this, e, (e) => { - !e.defaultPrevented && DragManager.StartDocumentDrag([gearSpan], new DragManager.DocumentDragData([doc], doc.dropAction as dropActionType), e.clientX, e.clientY); - return !e.defaultPrevented; - }, returnFalse, emptyFunction); - }; - - tab._disposers.selectionDisposer = reaction(() => SelectionManager.SelectedDocuments().some(v => v.props.Document === doc), - (selected) => { - selected && tab.contentItem !== tab.header.parent.getActiveContentItem() && tab.header.parent.setActiveContentItem(tab.contentItem); - } - ); - tab._disposers.buttonDisposer = reaction(() => this.view, - (view) => { - if (view) { - ReactDOM.render( - [view]} Stack={stack} /> - , - gearSpan); - tab._disposers.buttonDisposer?.(); - } - }, { fireImmediately: true }); - - tab.reactComponents = [gearSpan]; - tab.element.append(gearSpan); - tab._disposers.reactionDisposer = reaction(() => ({ title: doc.title, degree: Doc.IsBrushedDegree(doc) }), ({ title, degree }) => { - tab.titleElement[0].value = title; - tab.titleElement[0].style.padding = degree ? 0 : 2; - tab.titleElement[0].style.border = `${["gray", "gray", "gray"][degree]} ${["none", "dashed", "solid"][degree]} 2px`; - }, { fireImmediately: true }); - tab.closeElement.off('click') //unbind the current click handler - .click(function () { - Object.values(tab._disposers).forEach((disposer: any) => disposer?.()); - Doc.AddDocToList(CurrentUserUtils.MyRecentlyClosed, "data", doc, undefined, true, true); - SelectionManager.DeselectAll(); - tab.contentItem.remove(); - }); - } - } - /** - * 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 = CurrentUserUtils.ActivePresentation; - 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.AddSplit(curPres, "right"); - } - 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 = CurrentUserUtils.ActivePresentation; - 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 => v.props.Document === this._document); - const updateTabColor = () => this.tab?.titleElement[0] && (this.tab.titleElement[0].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(() => this.view && SelectionManager.SelectDoc(this.view, false), 0); - (CollectionDockingView.Instance as any)._goldenLayout.isInitialised && CollectionDockingView.Instance.stateChanged(); - !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) { - const wscale = this.panelWidth() / nativeW; - scaling = wscale * nativeH > this._panelHeight ? this._panelHeight / nativeH : wscale; - } - 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; } - - // 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, libraryPath?: Doc[]) => { - SelectionManager.DeselectAll(); - if (doc._viewType === CollectionViewType.Docking) return CurrentUserUtils.openDashboard(Doc.UserDoc(), doc); - const locationFields = location.split(":"); - const locationParams = locationFields.length > 1 ? locationFields[1] : ""; - switch (locationFields[0]) { - case "close": return CollectionDockingView.CloseSplit(doc, locationParams); - case "fullScreen": return CollectionDockingView.OpenFullScreen(doc); - case "replace": return CollectionDockingView.ReplaceTab(doc, locationParams, this.stack); - case "inPlace": - case "add": - default: return CollectionDockingView.ToggleSplit(doc, locationParams, this.stack); - } - } - - @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) => { - this._document && 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
- -
-
-
-
; - } - setView = action((view: DocumentView) => this._view = view); - @computed get docView() { - TraceMobx(); - return !this._document ? (null) : - <> - {this._document._viewType === CollectionViewType.Freeform && !this._document?.hideMinimap ? this.renderMiniMap() : (null)} - ; - } - - render() { - return !this._isActive ? (null) : - (
{ - if (this._mainCont = ref) { - (this._mainCont as any).InitTab = (tab: any) => this.init(tab, this._document); - DocServer.GetRefField(this.tab.contentItem.config.props.documentId).then(action(doc => doc instanceof Doc && (this._document = doc) && this.init(this.tab, this._document))); - } - }} - style={{ - transform: `translate(${this.previewPanelCenteringOffset}px, 0px)`, - height: this.layoutDoc?._fitWidth ? undefined : "100%", - width: this.widthpercent - }}> - {this.docView} -
); - } -} 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.ReplaceTab(doc, "right", undefined, shiftKey); }); diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx index a41b5eaca..eaada32b0 100644 --- a/src/client/views/collections/CollectionMenu.tsx +++ b/src/client/views/collections/CollectionMenu.tsx @@ -5,7 +5,7 @@ import { Tooltip } from "@material-ui/core"; import { action, computed, Lambda, observable, reaction, runInAction } from "mobx"; import { observer } from "mobx-react"; import { ColorState } from "react-color"; -import { Doc, DocListCast, Opt, Field } from "../../../fields/Doc"; +import { Doc, DocListCast, Field, Opt } from "../../../fields/Doc"; import { Document } from "../../../fields/documentSchemas"; import { Id } from "../../../fields/FieldSymbols"; import { InkTool } from "../../../fields/InkField"; @@ -29,10 +29,10 @@ import { ActiveFillColor, ActiveInkColor, SetActiveArrowEnd, SetActiveArrowStart import { CollectionFreeFormDocumentView } from "../nodes/CollectionFreeFormDocumentView"; import { DocumentView } from "../nodes/DocumentView"; import { RichTextMenu } from "../nodes/formattedText/RichTextMenu"; +import { PresBox } from "../nodes/PresBox"; import "./CollectionMenu.scss"; import { CollectionViewType, COLLECTION_BORDER_WIDTH } from "./CollectionView"; -import { DockedFrameRenderer } from "./CollectionDockingView"; -import { PresBox } from "../nodes/PresBox"; +import { TabDocView } from "./TabDocView"; @observer export class CollectionMenu extends AntimodeMenu { @@ -379,7 +379,7 @@ export class CollectionViewBaseChrome extends React.Component{Doc.isDocPinned(targetDoc) ? "Unpin from presentation" : "Pin to presentation"}
} placement="top"> ; @@ -443,7 +443,7 @@ export class CollectionViewBaseChrome extends React.Component { if (targetDoc) { - DockedFrameRenderer.PinDoc(targetDoc, false); + TabDocView.PinDoc(targetDoc, false); const activeDoc = PresBox.Instance.childDocs[PresBox.Instance.childDocs.length - 1]; const x = targetDoc._panX; const y = targetDoc._panY; diff --git a/src/client/views/collections/TabDocView.scss b/src/client/views/collections/TabDocView.scss new file mode 100644 index 000000000..9a4b5cbd1 --- /dev/null +++ b/src/client/views/collections/TabDocView.scss @@ -0,0 +1,20 @@ + +.miniMap { + position: absolute; + overflow: hidden; + right: 10; + bottom: 10; + border: solid 1px; + box-shadow: black 0.4vw 0.4vw 0.8vw; + + .miniOverlay { + width: 100%; + height: 100%; + position: absolute; + + .miniThumb { + background: #25252525; + position: absolute; + } + } +} \ No newline at end of file diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx new file mode 100644 index 000000000..36959f254 --- /dev/null +++ b/src/client/views/collections/TabDocView.tsx @@ -0,0 +1,379 @@ +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, DocListCast, Opt } from "../../../fields/Doc"; +import { Id } from '../../../fields/FieldSymbols'; +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, returnFalse, returnOne, returnTrue, returnZero, setupMoveUpEvents, Utils } from "../../../Utils"; +import { DocServer } from "../../DocServer"; +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 } from "../../util/UndoManager"; +import { DocumentView } from "../nodes/DocumentView"; +import { PresBox } from '../nodes/PresBox'; +import { CollectionDockingView } from './CollectionDockingView'; +import "./TabDocView.scss"; +import { CollectionDockingViewMenu } from './CollectionDockingViewMenu'; +import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormView'; +import { CollectionViewType } from './CollectionView'; +import React = require("react"); +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 private _panelWidth = 0; + @observable private _panelHeight = 0; + @observable private _isActive: boolean = false; + @observable private _document: Doc | undefined; + @observable private _view: DocumentView | undefined; + + get stack(): any { return (this.props as any).glContainer.parent.parent; } + get tab() { return (this.props as any).glContainer.tab; } + get view() { return this._view; } + + @action + init = (tab: any, doc: Opt) => { + 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; + + CollectionDockingView.Instance.tabMap.add(tab); + 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; + }; + 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) => { + e.target.className !== "lm_close_tab" && this.view && SelectionManager.SelectDoc(this.view, false); + }; + // 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); + }; + const onDown = (e: React.PointerEvent) => { + setupMoveUpEvents(this, e, (e) => { + !e.defaultPrevented && DragManager.StartDocumentDrag([gearSpan], new DragManager.DocumentDragData([doc], doc.dropAction as dropActionType), e.clientX, e.clientY); + return !e.defaultPrevented; + }, returnFalse, emptyFunction); + }; + + tab._disposers.selectionDisposer = reaction(() => SelectionManager.SelectedDocuments().some(v => v.props.Document === doc), + (selected) => { + selected && tab.contentItem !== tab.header.parent.getActiveContentItem() && tab.header.parent.setActiveContentItem(tab.contentItem); + } + ); + tab._disposers.buttonDisposer = reaction(() => this.view, + (view) => { + if (view) { + ReactDOM.render( + [view]} Stack={stack} /> + , + gearSpan); + tab._disposers.buttonDisposer?.(); + } + }, { fireImmediately: true }); + + tab.reactComponents = [gearSpan]; + tab.element.append(gearSpan); + tab._disposers.reactionDisposer = reaction(() => ({ title: doc.title, degree: Doc.IsBrushedDegree(doc) }), ({ title, degree }) => { + tab.titleElement[0].value = title; + tab.titleElement[0].style.padding = degree ? 0 : 2; + tab.titleElement[0].style.border = `${["gray", "gray", "gray"][degree]} ${["none", "dashed", "solid"][degree]} 2px`; + }, { fireImmediately: true }); + tab.closeElement.off('click') //unbind the current click handler + .click(function () { + Object.values(tab._disposers).forEach((disposer: any) => disposer?.()); + Doc.AddDocToList(CurrentUserUtils.MyRecentlyClosed, "data", doc, undefined, true, true); + SelectionManager.DeselectAll(); + tab.contentItem.remove(); + }); + } + } + /** + * Adds a document to the presentation view + **/ + @undoBatch + @action + public static PinDoc(doc: Doc, unpin = false) { + if (unpin) TabDocView.UnpinDoc(doc); + else { + //add this new doc to props.Document + const curPres = CurrentUserUtils.ActivePresentation; + 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.AddSplit(curPres, "right"); + } + 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 = CurrentUserUtils.ActivePresentation; + 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 => v.props.Document === this._document); + const updateTabColor = () => this.tab?.titleElement[0] && (this.tab.titleElement[0].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(() => this.view && SelectionManager.SelectDoc(this.view, false), 0); + (CollectionDockingView.Instance as any)._goldenLayout.isInitialised && CollectionDockingView.Instance.stateChanged(); + !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) { + const wscale = this.panelWidth() / nativeW; + scaling = wscale * nativeH > this._panelHeight ? this._panelHeight / nativeH : wscale; + } + 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; } + + // 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, libraryPath?: Doc[]) => { + SelectionManager.DeselectAll(); + if (doc._viewType === CollectionViewType.Docking) return CurrentUserUtils.openDashboard(Doc.UserDoc(), doc); + const locationFields = location.split(":"); + const locationParams = locationFields.length > 1 ? locationFields[1] : ""; + switch (locationFields[0]) { + case "close": return CollectionDockingView.CloseSplit(doc, locationParams); + case "fullScreen": return CollectionDockingView.OpenFullScreen(doc); + case "replace": return CollectionDockingView.ReplaceTab(doc, locationParams, this.stack); + case "inPlace": + case "add": + default: return CollectionDockingView.ToggleSplit(doc, locationParams, this.stack); + } + } + + @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) => { + this._document && 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
+ +
+
+
+
; + } + setView = action((view: DocumentView) => this._view = view); + @computed get docView() { + TraceMobx(); + return !this._document ? (null) : + <> + {this._document._viewType === CollectionViewType.Freeform && !this._document?.hideMinimap ? this.renderMiniMap() : (null)} + ; + } + + render() { + return !this._isActive ? (null) : + (
{ + if (this._mainCont = ref) { + (this._mainCont as any).InitTab = (tab: any) => this.init(tab, this._document); + DocServer.GetRefField(this.tab.contentItem.config.props.documentId).then(action(doc => doc instanceof Doc && (this._document = doc) && this.init(this.tab, this._document))); + } + }} + style={{ + transform: `translate(${this.previewPanelCenteringOffset}px, 0px)`, + height: this.layoutDoc?._fitWidth ? undefined : "100%", + width: this.widthpercent + }}> + {this.docView} +
); + } +} diff --git a/src/client/views/nodes/PresBox.tsx b/src/client/views/nodes/PresBox.tsx index a03c45a53..07b2d51d1 100644 --- a/src/client/views/nodes/PresBox.tsx +++ b/src/client/views/nodes/PresBox.tsx @@ -1,34 +1,33 @@ import React = require("react"); import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { Tooltip } from "@material-ui/core"; import { action, computed, observable, runInAction } from "mobx"; import { observer } from "mobx-react"; -import { Doc, DocListCast, DocCastAsync, WidthSym } from "../../../fields/Doc"; -import { InkTool } from "../../../fields/InkField"; -import { BoolCast, Cast, NumCast, StrCast, ScriptCast } from "../../../fields/Types"; -import { returnFalse, returnOne, numberRange, returnTrue } from "../../../Utils"; +import { ColorState, SketchPicker } from "react-color"; +import { Doc, DocCastAsync, DocListCast } from "../../../fields/Doc"; import { documentSchema } from "../../../fields/documentSchemas"; -import { DocumentManager } from "../../util/DocumentManager"; -import { undoBatch } from "../../util/UndoManager"; -import { CollectionDockingView, DockedFrameRenderer } from "../collections/CollectionDockingView"; -import { CollectionView, CollectionViewType } from "../collections/CollectionView"; -import { FieldView, FieldViewProps } from './FieldView'; -import { DocumentType } from "../../documents/DocumentTypes"; -import "./PresBox.scss"; -import { ViewBoxBaseComponent } from "../DocComponent"; -import { makeInterface, listSpec } from "../../../fields/Schema"; -import { Docs, DocUtils } from "../../documents/Documents"; +import { InkTool } from "../../../fields/InkField"; +import { List } from "../../../fields/List"; import { PrefetchProxy } from "../../../fields/Proxy"; +import { listSpec, makeInterface } from "../../../fields/Schema"; import { ScriptField } from "../../../fields/ScriptField"; +import { Cast, NumCast, StrCast } from "../../../fields/Types"; +import { returnFalse, returnOne } from "../../../Utils"; +import { Docs } from "../../documents/Documents"; +import { DocumentType } from "../../documents/DocumentTypes"; +import { CurrentUserUtils } from "../../util/CurrentUserUtils"; +import { DocumentManager } from "../../util/DocumentManager"; import { Scripting } from "../../util/Scripting"; -import { CollectionFreeFormDocumentView } from "./CollectionFreeFormDocumentView"; -import { List } from "../../../fields/List"; -import { Tooltip } from "@material-ui/core"; -import { actionAsync } from "mobx-utils"; import { SelectionManager } from "../../util/SelectionManager"; +import { undoBatch } from "../../util/UndoManager"; +import { CollectionDockingView } from "../collections/CollectionDockingView"; +import { CollectionView, CollectionViewType } from "../collections/CollectionView"; +import { TabDocView } from "../collections/TabDocView"; +import { ViewBoxBaseComponent } from "../DocComponent"; import { AudioBox } from "./AudioBox"; -import { DocumentView } from "./DocumentView"; -import { SketchPicker, ColorState } from "react-color"; -import { CurrentUserUtils } from "../../util/CurrentUserUtils"; +import { CollectionFreeFormDocumentView } from "./CollectionFreeFormDocumentView"; +import { FieldView, FieldViewProps } from './FieldView'; +import "./PresBox.scss"; type PresBoxSchema = makeInterface<[typeof documentSchema]>; const PresBoxDocument = makeInterface(documentSchema); @@ -1112,7 +1111,7 @@ export class PresBox extends ViewBoxBaseComponent const presData = Cast(this.rootDoc.data, listSpec(Doc)); if (data && presData) { data.push(doc); - DockedFrameRenderer.PinDoc(doc, false); + TabDocView.PinDoc(doc, false); this.gotoDocument(this.childDocs.length, this.itemIndex); } else { this.props.addDocTab(doc, "add:right"); diff --git a/src/mobile/MobileInterface.tsx b/src/mobile/MobileInterface.tsx index d4eb76ecd..b8633b06f 100644 --- a/src/mobile/MobileInterface.tsx +++ b/src/mobile/MobileInterface.tsx @@ -20,7 +20,7 @@ import { Scripting } from '../client/util/Scripting'; import { SettingsManager } from '../client/util/SettingsManager'; import { Transform } from '../client/util/Transform'; import { UndoManager } from "../client/util/UndoManager"; -import { DockedFrameRenderer } from '../client/views/collections/CollectionDockingView'; +import { TabDocView } from '../client/views/collections/TabDocView'; import { CollectionViewType } from "../client/views/collections/CollectionView"; import { GestureOverlay } from "../client/views/GestureOverlay"; import { AudioBox } from "../client/views/nodes/AudioBox"; @@ -516,7 +516,7 @@ export class MobileInterface extends React.Component { return
DockedFrameRenderer.PinDoc(this._activeDoc, isPinned)}> + onClick={e => TabDocView.PinDoc(this._activeDoc, isPinned)}>
; } else return (null); -- cgit v1.2.3-70-g09d2