aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/collections/CollectionDockingView.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views/collections/CollectionDockingView.tsx')
-rw-r--r--src/client/views/collections/CollectionDockingView.tsx653
1 files changed, 383 insertions, 270 deletions
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 5270a4624..f44ab50c7 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -1,122 +1,39 @@
import 'golden-layout/src/css/goldenlayout-base.css';
import 'golden-layout/src/css/goldenlayout-dark-theme.css';
-import { action, observable, reaction, Lambda, IReactionDisposer } from "mobx";
+import { action, Lambda, observable, reaction } from "mobx";
import { observer } from "mobx-react";
import * as ReactDOM from 'react-dom';
-import Measure, { ContentRect } from "react-measure";
+import Measure from "react-measure";
import * as GoldenLayout from "../../../client/goldenLayout";
-import { Doc, Field, Opt, DocListCast } from "../../../new_fields/Doc";
+import { Doc, DocListCast, Field, Opt } from "../../../new_fields/Doc";
+import { Id } from '../../../new_fields/FieldSymbols';
+import { FieldId } from "../../../new_fields/RefField";
import { listSpec } from "../../../new_fields/Schema";
-import { Cast, NumCast, StrCast } from "../../../new_fields/Types";
-import { emptyFunction, returnTrue, Utils } from "../../../Utils";
+import { Cast, NumCast, StrCast, BoolCast } from "../../../new_fields/Types";
+import { emptyFunction, returnTrue, Utils, returnOne } from "../../../Utils";
import { DocServer } from "../../DocServer";
+import { DocumentManager } from '../../util/DocumentManager';
import { DragLinksAsDocuments, DragManager } from "../../util/DragManager";
+import { SelectionManager } from '../../util/SelectionManager';
+import { Transform } from '../../util/Transform';
import { undoBatch, UndoManager } from "../../util/UndoManager";
+import { DocumentView } from "../nodes/DocumentView";
+import { CollectionViewType } from './CollectionBaseView';
import "./CollectionDockingView.scss";
import { SubCollectionViewProps } from "./CollectionSubView";
-import React = require("react");
import { ParentDocSelector } from './ParentDocumentSelector';
-import { DocumentManager } from '../../util/DocumentManager';
-import { Id } from '../../../new_fields/FieldSymbols';
-import { DockedFrameRenderer } from './DockedFrameRenderer';
+import React = require("react");
+import { MainView } from '../MainView';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { library } from '@fortawesome/fontawesome-svg-core';
+import { faFile } from '@fortawesome/free-solid-svg-icons';
+import { CurrentUserUtils } from '../../../server/authentication/models/current_user_utils';
+library.add(faFile);
@observer
export class CollectionDockingView extends React.Component<SubCollectionViewProps> {
- public static TopLevel: CollectionDockingView;
- private _goldenLayout: any = null;
- private _containerRef = React.createRef<HTMLDivElement>();
- reactionDisposer?: IReactionDisposer;
- _removedDocs: Doc[] = [];
- private _flush: boolean = false;
- private _ignoreStateChange = "";
- private _isPointerDown = false;
- hack: boolean = false;
- undohack: any = null;
-
- constructor(props: SubCollectionViewProps) {
- super(props);
- CollectionDockingView.TopLevel = this;
- (window as any).React = React;
- (window as any).ReactDOM = ReactDOM;
- }
-
- componentDidMount: () => void = () => {
- if (this._containerRef.current) {
- this.reactionDisposer = reaction(
- () => StrCast(this.props.Document.dockingConfig),
- () => {
- if (!this._goldenLayout || this._ignoreStateChange !== this.retrieveConfiguration()) {
- // Because this is in a set timeout, if this component unmounts right after mounting,
- // we will leak a GoldenLayout, because we try to destroy it before we ever create it
- setTimeout(() => this.setupGoldenLayout(), 1);
- }
- this._ignoreStateChange = "";
- }, { fireImmediately: true });
-
- // 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._goldenLayout.unbind('itemDropped', this.itemDropped);
- this._goldenLayout.unbind('tabCreated', this.tabCreated);
- this._goldenLayout.unbind('stackCreated', this.stackCreated);
- this._goldenLayout.unbind('tabDestroyed', this.tabDestroyed);
- } catch (e) {
- console.log("Unable to unbind Golden Layout event listener...", e);
- }
- if (this._goldenLayout) this._goldenLayout.destroy();
- this._goldenLayout = null;
-
- if (this.reactionDisposer) {
- this.reactionDisposer();
- }
- }
-
- setupGoldenLayout() {
- var config = StrCast(this.props.Document.dockingConfig);
- if (config) {
- if (!this._goldenLayout) {
- this.initializeConfiguration(config);
- }
- else {
- if (config === this.retrieveConfiguration()) {
- return;
- }
- try {
- this._goldenLayout.unbind('itemDropped', this.itemDropped);
- this._goldenLayout.unbind('tabCreated', this.tabCreated);
- this._goldenLayout.unbind('tabDestroyed', this.tabDestroyed);
- this._goldenLayout.unbind('stackCreated', this.stackCreated);
- } catch (e) { }
- this._goldenLayout.destroy();
- this.initializeConfiguration(config);
- }
- this._goldenLayout.on('itemDropped', this.itemDropped);
- 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.container = this._containerRef.current;
- if (this._goldenLayout.config.maximisedItemId === '__glMaximised') {
- try {
- this._goldenLayout.config.root.getItemsById(this._goldenLayout.config.maximisedItemId)[0].toggleMaximise();
- } catch (e) {
- this._goldenLayout.config.maximisedItemId = null;
- }
- }
- this._goldenLayout.init();
- }
- }
-
- private makeDocConfig = (document: Doc, width?: number) => {
- const config = CollectionDockingView.makeDocumentConfig(document, width);
- (config.props as any).parent = this;
- return config;
- }
-
- public static makeDocumentConfig(document: Doc, width?: number) {
+ public static Instance: CollectionDockingView;
+ public static makeDocumentConfig(document: Doc, dataDoc: Doc | undefined, width?: number) {
return {
type: 'react-component',
component: 'DocumentFrameRenderer',
@@ -124,83 +41,147 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
width: width,
props: {
documentId: document[Id],
+ dataDocumentId: dataDoc ? dataDoc[Id] : ""
+ //collectionDockingView: CollectionDockingView.Instance
}
};
}
- initializeConfiguration = (configText: string) => {
- let configuration: any = JSON.parse(configText);
- this.injectParentProp(configuration.content);
- this._goldenLayout = new GoldenLayout(configuration);
- }
+ private _goldenLayout: any = null;
+ private _containerRef = React.createRef<HTMLDivElement>();
+ private _flush: boolean = false;
+ private _ignoreStateChange = "";
+ private _isPointerDown = false;
+ private _maximizedSrc: Opt<DocumentView>;
- retrieveConfiguration = () => {
- let configuration: any = this._goldenLayout.toConfig();
- this.injectParentProp(configuration.content, true);
- return JSON.stringify(configuration);
+ constructor(props: SubCollectionViewProps) {
+ super(props);
+ if (props.addDocTab === emptyFunction) CollectionDockingView.Instance = this;
+ //Why is this here?
+ (window as any).React = React;
+ (window as any).ReactDOM = ReactDOM;
}
-
- injectParentProp = (contentArray: any[], reverse: boolean = false) => {
- if (!contentArray || contentArray.length === 0) return;
- contentArray.forEach(member => {
- let baseCase = Object.keys(member).includes("props");
- if (!baseCase) {
- this.injectParentProp(member.content, reverse);
- } else {
- reverse ? delete member.props.parent : member.props.parent = this;
- }
+ hack: boolean = false;
+ undohack: any = null;
+ public StartOtherDrag(e: any, dragDocs: Doc[], dragDataDocs: (Doc | undefined)[] = []) {
+ let config: any;
+ if (dragDocs.length === 1) {
+ config = CollectionDockingView.makeDocumentConfig(dragDocs[0], dragDataDocs[0]);
+ } else {
+ config = {
+ type: 'row',
+ content: dragDocs.map((doc, i) => {
+ CollectionDockingView.makeDocumentConfig(doc, dragDataDocs[i]);
+ })
+ };
+ }
+ const div = document.createElement("div");
+ const dragSource = this._goldenLayout.createDragSource(div, config);
+ dragSource._dragListener.on("dragStop", () => {
+ dragSource.destroy();
});
- }
-
- public StartOtherDrag(dragDocs: Doc[], e: any) {
- this.hack = true;
- this.undohack = UndoManager.StartBatch("goldenDrag");
- dragDocs.map(dragDoc =>
- CollectionDockingView.AddRightSplit(dragDoc, true).contentItems[0].tab._dragListener.
- onMouseDown({ pageX: e.pageX, pageY: e.pageY, preventDefault: emptyFunction, button: 0 }));
+ dragSource._dragListener.onMouseDown(e);
+ // dragSource.destroy();
+ // this.hack = true;
+ // this.undohack = UndoManager.StartBatch("goldenDrag");
+ // dragDocs.map((dragDoc, i) =>
+ // this.AddRightSplit(dragDoc, dragDataDocs[i], true).contentItems[0].tab._dragListener.
+ // onMouseDown({ pageX: e.pageX, pageY: e.pageY, preventDefault: emptyFunction, button: 0 }));
}
@action
- public static OpenFullScreen(document: Doc, dockingView: CollectionDockingView = CollectionDockingView.TopLevel) {
- dockingView.openFullScreen(document);
- }
-
- private openFullScreen = (document: Doc) => {
+ public OpenFullScreen(docView: DocumentView) {
+ let document = Doc.MakeAlias(docView.props.Document);
+ let dataDoc = docView.dataDoc;
let newItemStackConfig = {
type: 'stack',
- content: [this.makeDocConfig(document)]
+ content: [CollectionDockingView.makeDocumentConfig(document, dataDoc)]
};
var docconfig = this._goldenLayout.root.layoutManager.createContentItem(newItemStackConfig, this._goldenLayout);
this._goldenLayout.root.contentItems[0].addChild(docconfig);
docconfig.callDownwards('_$init');
this._goldenLayout._$maximiseItem(docconfig);
- this._ignoreStateChange = this.retrieveConfiguration();
+ this._maximizedSrc = docView;
+ this._ignoreStateChange = JSON.stringify(this._goldenLayout.toConfig());
+ this.stateChanged();
+ }
+
+ public CloseFullScreen = () => {
+ let target = this._goldenLayout._maximisedItem;
+ if (target !== null && this._maximizedSrc) {
+ this._goldenLayout._maximisedItem.remove();
+ SelectionManager.SelectDoc(this._maximizedSrc, false);
+ this._maximizedSrc = undefined;
+ this.stateChanged();
+ }
+ }
+
+ public HasFullScreen = () => {
+ return this._goldenLayout._maximisedItem !== null;
+ }
+
+ @undoBatch
+ @action
+ public CloseRightSplit = (document: Doc): boolean => {
+ let retVal = false;
+ if (this._goldenLayout.root.contentItems[0].isRow) {
+ retVal = Array.from(this._goldenLayout.root.contentItems[0].contentItems).some((child: any) => {
+ if (child.contentItems.length === 1 && child.contentItems[0].config.component === "DocumentFrameRenderer" &&
+ Doc.AreProtosEqual(DocumentManager.Instance.getDocumentViewById(child.contentItems[0].config.props.documentId)!.Document, document)) {
+ child.contentItems[0].remove();
+ this.layoutChanged(document);
+ return true;
+ } else {
+ Array.from(child.contentItems).filter((tab: any) => tab.config.component === "DocumentFrameRenderer").some((tab: any, j: number) => {
+ if (Doc.AreProtosEqual(DocumentManager.Instance.getDocumentViewById(tab.config.props.documentId)!.Document, document)) {
+ child.contentItems[j].remove();
+ child.config.activeItemIndex = Math.max(child.contentItems.length - 1, 0);
+ let docs = Cast(this.props.Document.data, listSpec(Doc));
+ docs && docs.indexOf(document) !== -1 && docs.splice(docs.indexOf(document), 1);
+ return true;
+ }
+ return false;
+ });
+ }
+ return false;
+ });
+ }
+ if (retVal) {
+ this.stateChanged();
+ }
+ return retVal;
+ }
+
+ @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());
+ if (removed) CollectionDockingView.Instance._removedDocs.push(removed);
this.stateChanged();
}
//
// Creates a vertical split on the right side of the docking view, and then adds the Document to that split
//
- public static AddRightSplit = (document: Doc, minimize: boolean = false, dockingView: CollectionDockingView = CollectionDockingView.TopLevel) => {
- return dockingView.addRightSplit(document, minimize);
- }
-
- private addRightSplit(document: Doc, minimize = false) {
+ @action
+ public AddRightSplit = (document: Doc, dataDoc: Doc | undefined, minimize: boolean = false) => {
let docs = Cast(this.props.Document.data, listSpec(Doc));
if (docs) {
docs.push(document);
}
let newItemStackConfig = {
type: 'stack',
- content: [this.makeDocConfig(document)]
+ content: [CollectionDockingView.makeDocumentConfig(document, dataDoc)]
};
var newContentItem = this._goldenLayout.root.layoutManager.createContentItem(newItemStackConfig, this._goldenLayout);
- if (this._goldenLayout.root.contentItems[0].isRow) {
+ if (this._goldenLayout.root.contentItems.length === 0) {
+ this._goldenLayout.root.addChild(newContentItem);
+ } else if (this._goldenLayout.root.contentItems[0].isRow) {
this._goldenLayout.root.contentItems[0].addChild(newContentItem);
- }
- else {
+ } else {
var collayout = this._goldenLayout.root.contentItems[0];
var newRow = collayout.layoutManager.createContentItem({ type: "row" }, this._goldenLayout);
collayout.parent.replaceChild(collayout, newRow);
@@ -221,75 +202,99 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
return newContentItem;
}
-
- public static AddTab = (stack: any, document: Doc, dockingView: CollectionDockingView = CollectionDockingView.TopLevel) => {
- dockingView.addTab(stack, document);
- }
-
- private addTab = (stack: any, document: Doc) => {
+ @action
+ public AddTab = (stack: any, document: Doc, dataDocument: Doc | undefined) => {
let docs = Cast(this.props.Document.data, listSpec(Doc));
if (docs) {
docs.push(document);
}
- let docContentConfig = this.makeDocConfig(document);
+ let docContentConfig = CollectionDockingView.makeDocumentConfig(document, dataDocument);
var newContentItem = stack.layoutManager.createContentItem(docContentConfig, this._goldenLayout);
stack.addChild(newContentItem.contentItems[0], undefined);
this.layoutChanged();
}
- @undoBatch
- @action
- public static CloseRightSplit = (document: Doc, dockingView: CollectionDockingView = CollectionDockingView.TopLevel): boolean => {
- let retVal = false;
- if (dockingView._goldenLayout.root.contentItems[0].isRow) {
- retVal = Array.from(dockingView._goldenLayout.root.contentItems[0].contentItems).some((child: any) => {
- if (child.contentItems.length === 1 && child.contentItems[0].config.component === "DocumentFrameRenderer" &&
- Doc.AreProtosEqual(DocumentManager.Instance.getDocumentViewById(child.contentItems[0].config.props.documentId)!.Document, document)) {
- child.contentItems[0].remove();
- dockingView.layoutChanged(document);
- return true;
- } else {
- Array.from(child.contentItems).filter((tab: any) => tab.config.component === "DocumentFrameRenderer").some((tab: any, j: number) => {
- if (Doc.AreProtosEqual(DocumentManager.Instance.getDocumentViewById(tab.config.props.documentId)!.Document, document)) {
- child.contentItems[j].remove();
- child.config.activeItemIndex = Math.max(child.contentItems.length - 1, 0);
- let docs = Cast(dockingView.props.Document.data, listSpec(Doc));
- docs && docs.indexOf(document) !== -1 && docs.splice(docs.indexOf(document), 1);
- return true;
- }
- return false;
- });
+ setupGoldenLayout() {
+ var config = StrCast(this.props.Document.dockingConfig);
+ if (config) {
+ if (!this._goldenLayout) {
+ this._goldenLayout = new GoldenLayout(JSON.parse(config));
+ }
+ else {
+ if (config === JSON.stringify(this._goldenLayout.toConfig())) {
+ return;
}
- return false;
- });
- }
- if (retVal) {
- dockingView.stateChanged();
+ try {
+ this._goldenLayout.unbind('itemDropped', this.itemDropped);
+ this._goldenLayout.unbind('tabCreated', this.tabCreated);
+ this._goldenLayout.unbind('tabDestroyed', this.tabDestroyed);
+ this._goldenLayout.unbind('stackCreated', this.stackCreated);
+ } catch (e) { }
+ this._goldenLayout.destroy();
+ this._goldenLayout = new GoldenLayout(JSON.parse(config));
+ }
+ this._goldenLayout.on('itemDropped', this.itemDropped);
+ 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.container = this._containerRef.current;
+ if (this._goldenLayout.config.maximisedItemId === '__glMaximised') {
+ try {
+ this._goldenLayout.config.root.getItemsById(this._goldenLayout.config.maximisedItemId)[0].toggleMaximise();
+ } catch (e) {
+ this._goldenLayout.config.maximisedItemId = null;
+ }
+ }
+ this._goldenLayout.init();
}
- return retVal;
}
+ reactionDisposer?: Lambda;
+ componentDidMount: () => void = () => {
+ if (this._containerRef.current) {
+ this.reactionDisposer = reaction(
+ () => StrCast(this.props.Document.dockingConfig),
+ () => {
+ if (!this._goldenLayout || this._ignoreStateChange !== JSON.stringify(this._goldenLayout.toConfig())) {
+ // Because this is in a set timeout, if this component unmounts right after mounting,
+ // we will leak a GoldenLayout, because we try to destroy it before we ever create it
+ setTimeout(() => this.setupGoldenLayout(), 1);
+ this.props.Document.workspaceBrush = true;
+ }
+ this._ignoreStateChange = "";
+ }, { fireImmediately: true });
- @action
- layoutChanged(removed?: Doc) {
- this._goldenLayout.root.callDownwards('setSize', [this._goldenLayout.width, this._goldenLayout.height]);
- this._goldenLayout.emit('stateChanged');
- this._ignoreStateChange = this.retrieveConfiguration();
- if (removed) CollectionDockingView.TopLevel._removedDocs.push(removed);
- this.stateChanged();
+ 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.workspaceBrush = false;
+ this._goldenLayout.unbind('itemDropped', this.itemDropped);
+ this._goldenLayout.unbind('tabCreated', this.tabCreated);
+ this._goldenLayout.unbind('stackCreated', this.stackCreated);
+ this._goldenLayout.unbind('tabDestroyed', this.tabDestroyed);
+ } catch (e) {
+ }
+ if (this._goldenLayout) this._goldenLayout.destroy();
+ this._goldenLayout = null;
+ window.removeEventListener('resize', this.onResize);
+
+ if (this.reactionDisposer) {
+ this.reactionDisposer();
+ }
+ }
@action
- onResize = (size: ContentRect) => {
+ onResize = (event: any) => {
+ var cur = this._containerRef.current;
+
// bcz: since GoldenLayout isn't a React component itself, we need to notify it to resize when its document container's size has changed
- // this._goldenLayout.updateSize(cur!.getBoundingClientRect().width, cur!.getBoundingClientRect().height);
- if (this._goldenLayout) {
- this._goldenLayout.updateSize(size.offset!.width, size.offset!.height);
- }
+ this._goldenLayout && this._goldenLayout.updateSize(cur!.getBoundingClientRect().width, cur!.getBoundingClientRect().height);
}
@action
onPointerUp = (e: React.PointerEvent): void => {
- this._isPointerDown = false;
if (this._flush) {
this._flush = false;
setTimeout(() => this.stateChanged(), 10);
@@ -298,6 +303,11 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
@action
onPointerDown = (e: React.PointerEvent): void => {
this._isPointerDown = true;
+ let onPointerUp = action(() => {
+ window.removeEventListener("pointerup", onPointerUp);
+ this._isPointerDown = false;
+ });
+ window.addEventListener("pointerup", onPointerUp);
var className = (e.target as any).className;
if (className === "messageCounter") {
e.stopPropagation();
@@ -306,48 +316,22 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
let y = e.clientY;
let docid = (e.target as any).DashDocId;
let tab = (e.target as any).parentElement as HTMLElement;
- DocServer.getRefField(docid).then(action(async (sourceDoc: Opt<Field>) =>
+ DocServer.GetRefField(docid).then(action(async (sourceDoc: Opt<Field>) =>
(sourceDoc instanceof Doc) && DragLinksAsDocuments(tab, x, y, sourceDoc)));
- } else
- if ((className === "lm_title" || className === "lm_tab lm_active") && !e.shiftKey) {
- e.stopPropagation();
- e.preventDefault();
- let x = e.clientX;
- let y = e.clientY;
- let docid = (e.target as any).DashDocId;
- let tab = (e.target as any).parentElement as HTMLElement;
- let glTab = (e.target as any).Tab;
- if (glTab && glTab.contentItem && glTab.contentItem.parent) {
- glTab.contentItem.parent.setActiveContentItem(glTab.contentItem);
- }
- DocServer.getRefField(docid).then(action((f: Opt<Field>) => {
- if (f instanceof Doc) {
- DragManager.StartDocumentDrag([tab], new DragManager.DocumentDragData([f]), x, y,
- {
- handlers: {
- dragComplete: emptyFunction,
- },
- hideSource: false
- });
- }
- }));
- }
+ }
if (className === "lm_drag_handle" || className === "lm_close" || className === "lm_maximise" || className === "lm_minimise" || className === "lm_close_tab") {
this._flush = true;
}
- if (this.props.active()) {
- e.stopPropagation();
- }
}
@undoBatch
stateChanged = () => {
- let docs = Cast(CollectionDockingView.TopLevel.props.Document.data, listSpec(Doc));
- CollectionDockingView.TopLevel._removedDocs.map(theDoc =>
+ let docs = Cast(CollectionDockingView.Instance.props.Document.data, listSpec(Doc));
+ CollectionDockingView.Instance._removedDocs.map(theDoc =>
docs && docs.indexOf(theDoc) !== -1 &&
docs.splice(docs.indexOf(theDoc), 1));
- CollectionDockingView.TopLevel._removedDocs.length = 0;
- var json = this.retrieveConfiguration();
+ CollectionDockingView.Instance._removedDocs.length = 0;
+ var json = JSON.stringify(this._goldenLayout.toConfig());
this.props.Document.dockingConfig = json;
if (this.undohack && !this.hack) {
this.undohack.end();
@@ -356,52 +340,61 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
this.hack = false;
}
- private itemDropped = () => {
+ itemDropped = () => {
this.stateChanged();
}
- private htmlToElement(html: string) {
+ htmlToElement(html: string) {
var template = document.createElement('template');
html = html.trim(); // Never return a text node of whitespace as the result
template.innerHTML = html;
return template.content.firstChild;
}
- private tabCreated = async (tab: any) => {
+ tabCreated = async (tab: any) => {
if (tab.hasOwnProperty("contentItem") && tab.contentItem.config.type !== "stack") {
if (tab.contentItem.config.fixed) {
tab.contentItem.parent.config.fixed = true;
}
- DocServer.getRefField(tab.contentItem.config.props.documentId).then(async doc => {
- if (doc instanceof Doc) {
- let counter: any = this.htmlToElement(`<span class="messageCounter">0</div>`);
- tab.element.append(counter);
- let upDiv = document.createElement("span");
- const stack = tab.contentItem.parent;
- // shifts the focus to this tab when another tab is dragged over it
- tab.element[0].onmouseenter = (e: any) => {
- if (!this._isPointerDown) return;
- var activeContentItem = tab.header.parent.getActiveContentItem();
- if (tab.contentItem !== activeContentItem) {
- tab.header.parent.setActiveContentItem(tab.contentItem);
- }
- tab.setActive(true);
- };
- tab.header.element[0].ondrop = (e: any) => {
- console.log("DROPPPP THE BASS!", e);
- };
- ReactDOM.render(<ParentDocSelector Document={doc} addDocTab={(doc, location) => CollectionDockingView.AddTab(stack, doc)} />, upDiv);
- tab.reactComponents = [upDiv];
- tab.element.append(upDiv);
- counter.DashDocId = tab.contentItem.config.props.documentId;
- tab.reactionDisposer = reaction(() => [doc.linkedFromDocs, doc.LinkedToDocs, doc.title],
- () => {
- counter.innerHTML = DocListCast(doc.linkedFromDocs).length + DocListCast(doc.linkedToDocs).length;
- tab.titleElement[0].textContent = doc.title;
- }, { fireImmediately: true });
- tab.titleElement[0].DashDocId = tab.contentItem.config.props.documentId;
- }
- });
+ let doc = await DocServer.GetRefField(tab.contentItem.config.props.documentId) as Doc;
+ let dataDoc = await DocServer.GetRefField(tab.contentItem.config.props.dataDocumentId) as Doc;
+ if (doc instanceof Doc) {
+ let dragSpan = document.createElement("span");
+ dragSpan.style.position = "relative";
+ dragSpan.style.bottom = "6px";
+ dragSpan.style.paddingLeft = "4px";
+ dragSpan.style.paddingRight = "2px";
+ let upDiv = document.createElement("span");
+ const stack = tab.contentItem.parent;
+ // shifts the focus to this tab when another tab is dragged over it
+ tab.element[0].onmouseenter = (e: any) => {
+ if (!this._isPointerDown) return;
+ var activeContentItem = tab.header.parent.getActiveContentItem();
+ if (tab.contentItem !== activeContentItem) {
+ tab.header.parent.setActiveContentItem(tab.contentItem);
+ }
+ tab.setActive(true);
+ };
+ ReactDOM.render(<span title="Drag as document" onPointerDown={
+ e => {
+ e.preventDefault();
+ e.stopPropagation();
+ DragManager.StartDocumentDrag([dragSpan], new DragManager.DocumentDragData([doc], [dataDoc]), e.clientX, e.clientY, {
+ handlers: { dragComplete: emptyFunction },
+ hideSource: false
+ });
+ }}><FontAwesomeIcon icon="file" size="lg" /></span>, dragSpan);
+ ReactDOM.render(<ParentDocSelector Document={doc} addDocTab={doc => CollectionDockingView.Instance.AddTab(stack, doc, dataDoc)} />, upDiv);
+ tab.reactComponents = [dragSpan, upDiv];
+ tab.element.append(dragSpan);
+ tab.element.append(upDiv);
+ tab.reactionDisposer = reaction(() => [doc.title],
+ () => {
+ tab.titleElement[0].textContent = doc.title;
+ }, { fireImmediately: true });
+ //TODO why can't this just be doc instead of the id?
+ tab.titleElement[0].DashDocId = tab.contentItem.config.props.documentId;
+ }
}
tab.titleElement[0].Tab = tab;
tab.closeElement.off('click') //unbind the current click handler
@@ -409,35 +402,41 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
if (tab.reactionDisposer) {
tab.reactionDisposer();
}
- let doc = await DocServer.getRefField(tab.contentItem.config.props.documentId);
+ let doc = await DocServer.GetRefField(tab.contentItem.config.props.documentId);
if (doc instanceof Doc) {
let theDoc = doc;
- CollectionDockingView.TopLevel._removedDocs.push(theDoc);
+ CollectionDockingView.Instance._removedDocs.push(theDoc);
+ if (CurrentUserUtils.UserDocument.recentlyClosed instanceof Doc) {
+ Doc.AddDocToList(CurrentUserUtils.UserDocument.recentlyClosed, "data", doc, undefined, true, true);
+ }
+ SelectionManager.DeselectAll();
}
tab.contentItem.remove();
});
}
- private tabDestroyed = (tab: any) => {
+ tabDestroyed = (tab: any) => {
if (tab.reactComponents) {
for (const ele of tab.reactComponents) {
ReactDOM.unmountComponentAtNode(ele);
}
}
}
+ _removedDocs: Doc[] = [];
- private stackCreated = (stack: any) => {
+ stackCreated = (stack: any) => {
//stack.header.controlsContainer.find('.lm_popout').hide();
+ stack.header.element[0].style.backgroundColor = DocServer.Control.isReadOnly() ? "#228540" : undefined;
stack.header.controlsContainer.find('.lm_close') //get the close icon
.off('click') //unbind the current click handler
.click(action(function () {
//if (confirm('really close this?')) {
stack.remove();
stack.contentItems.map(async (contentItem: any) => {
- let doc = await DocServer.getRefField(contentItem.config.props.documentId);
+ let doc = await DocServer.GetRefField(contentItem.config.props.documentId);
if (doc instanceof Doc) {
let theDoc = doc;
- CollectionDockingView.TopLevel._removedDocs.push(theDoc);
+ CollectionDockingView.Instance._removedDocs.push(theDoc);
}
});
//}
@@ -452,16 +451,130 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
}
render() {
+ if (this.props.renderDepth > 0) {
+ return <div style={{ width: "100%", height: "100%" }}>Nested workspaces can't be rendered</div>;
+ }
return (
- <Measure onResize={this.onResize} offset>
- {({ measureRef }) => (
- <div ref={measureRef} style={{ width: "100%", height: "100%" }}>
+ <Measure offset onResize={this.onResize}>
+ {({ measureRef }) =>
+ <div ref={measureRef}>
<div className="collectiondockingview-container" id="menuContainer"
onPointerDown={this.onPointerDown} onPointerUp={this.onPointerUp} ref={this._containerRef} />
</div>
- )}
+ }
</Measure>
);
}
+}
+
+interface DockedFrameProps {
+ documentId: FieldId;
+ dataDocumentId: FieldId;
+ //collectionDockingView: CollectionDockingView
+}
+@observer
+export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
+ _mainCont = React.createRef<HTMLDivElement>();
+ @observable private _panelWidth = 0;
+ @observable private _panelHeight = 0;
+ @observable private _document: Opt<Doc>;
+ @observable private _dataDoc: Opt<Doc>;
+ get _stack(): any {
+ let parent = (this.props as any).glContainer.parent.parent;
+ if (this._document && this._document.excludeFromLibrary && parent.parent && parent.parent.contentItems.length > 1) {
+ return parent.parent.contentItems[1];
+ }
+ return parent;
+ }
+ constructor(props: any) {
+ super(props);
+ DocServer.GetRefField(this.props.documentId).then(action((f: Opt<Field>) => {
+ this._document = f as Doc;
+ if (this.props.dataDocumentId && this.props.documentId !== this.props.dataDocumentId) {
+ DocServer.GetRefField(this.props.dataDocumentId).then(action((f: Opt<Field>) => this._dataDoc = f as Doc));
+ }
+ }));
+ }
+
+ nativeWidth = () => NumCast(this._document!.nativeWidth, this._panelWidth);
+ nativeHeight = () => {
+ let nh = NumCast(this._document!.nativeHeight, this._panelHeight);
+ let res = BoolCast(this._document!.ignoreAspect) ? this._panelHeight : nh;
+ return res;
+ }
+ contentScaling = () => {
+ const nativeH = this.nativeHeight();
+ const nativeW = this.nativeWidth();
+ let wscale = this._panelWidth / nativeW;
+ return wscale * nativeH > this._panelHeight ? this._panelHeight / nativeH : wscale;
+ }
+
+ ScreenToLocalTransform = () => {
+ if (this._mainCont.current && this._mainCont.current.children) {
+ let { scale, translateX, translateY } = Utils.GetScreenTransform(this._mainCont.current.children[0].firstChild as HTMLElement);
+ scale = Utils.GetScreenTransform(this._mainCont.current).scale;
+ return CollectionDockingView.Instance.props.ScreenToLocalTransform().translate(-translateX, -translateY).scale(1 / this.contentScaling() / scale);
+ }
+ return Transform.Identity();
+ }
+ get scaleToFitMultiplier() {
+ let docWidth = NumCast(this._document!.width);
+ let docHeight = NumCast(this._document!.height);
+ if (NumCast(this._document!.nativeWidth) || !docWidth || !this._panelWidth || !this._panelHeight) return 1;
+ if (StrCast(this._document!.layout).indexOf("Collection") === -1 ||
+ !BoolCast(this._document!.fitToContents, false) ||
+ NumCast(this._document!.viewType) !== CollectionViewType.Freeform) return 1;
+ let scaling = Math.max(1, this._panelWidth / docWidth * docHeight > this._panelHeight ?
+ this._panelHeight / docHeight : this._panelWidth / docWidth);
+ return scaling;
+ }
+ get previewPanelCenteringOffset() { return (this._panelWidth - this.nativeWidth() * this.contentScaling()) / 2; }
+
+ addDocTab = (doc: Doc, dataDoc: Doc | undefined, location: string) => {
+ if (doc.dockingConfig) {
+ MainView.Instance.openWorkspace(doc);
+ } else if (location === "onRight") {
+ CollectionDockingView.Instance.AddRightSplit(doc, dataDoc);
+ } else {
+ CollectionDockingView.Instance.AddTab(this._stack, doc, dataDoc);
+ }
+ }
+ get content() {
+ if (!this._document) {
+ return (null);
+ }
+ let resolvedDataDoc = this._document.layout instanceof Doc ? this._document : this._dataDoc;
+ return (
+ <div className="collectionDockingView-content" ref={this._mainCont}
+ style={{ transform: `translate(${this.previewPanelCenteringOffset}px, 0px) scale(${this.scaleToFitMultiplier})` }}>
+ <DocumentView key={this._document[Id]}
+ Document={this._document}
+ DataDoc={resolvedDataDoc}
+ bringToFront={emptyFunction}
+ addDocument={undefined}
+ removeDocument={undefined}
+ ContentScaling={this.contentScaling}
+ PanelWidth={this.nativeWidth}
+ PanelHeight={this.nativeHeight}
+ ScreenToLocalTransform={this.ScreenToLocalTransform}
+ renderDepth={0}
+ selectOnLoad={false}
+ parentActive={returnTrue}
+ whenActiveChanged={emptyFunction}
+ focus={emptyFunction}
+ addDocTab={this.addDocTab}
+ ContainingCollectionView={undefined}
+ zoomToScale={emptyFunction}
+ getScale={returnOne} />
+ </div >);
+ }
+
+ render() {
+ let theContent = this.content;
+ return !this._document ? (null) :
+ <Measure offset onResize={action((r: any) => { this._panelWidth = r.offset.width; this._panelHeight = r.offset.height; })}>
+ {({ measureRef }) => <div ref={measureRef}> {theContent} </div>}
+ </Measure>;
+ }
} \ No newline at end of file