aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/collections/CollectionDockingView.tsx
diff options
context:
space:
mode:
authorSophie Zhang <sophie_zhang@brown.edu>2024-01-25 11:35:26 -0500
committerSophie Zhang <sophie_zhang@brown.edu>2024-01-25 11:35:26 -0500
commitf3dab2a56db5e4a6a3dca58185d94e1ff7d1dc32 (patch)
treea7bc895266b53bb620dbd2dd71bad2e83b555446 /src/client/views/collections/CollectionDockingView.tsx
parentb5c5410b4af5d2c68d2107d3f064f6e3ec4ac3f2 (diff)
parent136f3d9f349d54e8bdd73b6380ea47c19e5edebf (diff)
Merge branch 'master' into sophie-ai-images
Diffstat (limited to 'src/client/views/collections/CollectionDockingView.tsx')
-rw-r--r--src/client/views/collections/CollectionDockingView.tsx125
1 files changed, 67 insertions, 58 deletions
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 4873a61ff..87973fd81 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -1,9 +1,10 @@
-import { action, IReactionDisposer, observable, reaction, runInAction } from 'mobx';
+import { action, IReactionDisposer, makeObservable, observable, reaction } from 'mobx';
import { observer } from 'mobx-react';
+import * as React from 'react';
import * as ReactDOM from 'react-dom/client';
import * as GoldenLayout from '../../../client/goldenLayout';
import { Doc, DocListCast, Opt } from '../../../fields/Doc';
-import { AclAdmin, AclEdit } from '../../../fields/DocSymbols';
+import { AclAdmin, AclEdit, DocData } from '../../../fields/DocSymbols';
import { Id } from '../../../fields/FieldSymbols';
import { InkTool } from '../../../fields/InkField';
import { List } from '../../../fields/List';
@@ -19,7 +20,8 @@ import { DragManager } from '../../util/DragManager';
import { InteractionUtils } from '../../util/InteractionUtils';
import { ScriptingGlobals } from '../../util/ScriptingGlobals';
import { SelectionManager } from '../../util/SelectionManager';
-import { undoBatch, UndoManager } from '../../util/UndoManager';
+import { SettingsManager } from '../../util/SettingsManager';
+import { undoable, undoBatch, UndoManager } from '../../util/UndoManager';
import { DashboardView } from '../DashboardView';
import { LightboxView } from '../LightboxView';
import { OpenWhere, OpenWhereMod } from '../nodes/DocumentView';
@@ -28,15 +30,13 @@ import { ScriptingRepl } from '../ScriptingRepl';
import { UndoStack } from '../UndoStack';
import './CollectionDockingView.scss';
import { CollectionFreeFormView } from './collectionFreeForm';
-import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView';
+import { CollectionSubView } from './CollectionSubView';
import { TabDocView } from './TabDocView';
-import React = require('react');
-import { SettingsManager } from '../../util/SettingsManager';
const _global = (window /* browser */ || global) /* node */ as any;
@observer
export class CollectionDockingView extends CollectionSubView() {
- @observable public static Instance: CollectionDockingView | undefined;
+ @observable public static Instance: CollectionDockingView | undefined = undefined;
public static makeDocumentConfig(document: Doc, panelName?: string, width?: number, keyValue?: boolean) {
return {
type: 'react-component',
@@ -62,14 +62,16 @@ export class CollectionDockingView extends CollectionSubView() {
}
private _goldenLayout: any = null;
static _highlightStyleSheet: any = addStyleSheet();
- constructor(props: SubCollectionViewProps) {
+
+ constructor(props: any) {
super(props);
- if (this.props.renderDepth < 0) runInAction(() => (CollectionDockingView.Instance = this));
+ makeObservable(this);
+ if (this._props.renderDepth < 0) CollectionDockingView.Instance = this;
//Why is this here?
(window as any).React = React;
(window as any).ReactDOM = ReactDOM;
DragManager.StartWindowDrag = this.StartOtherDrag;
- this.rootDoc.myTrails; // this is equivalent to having a prefetchProxy for myTrails which is needed for the My Trails button in the UI which assumes that Doc.ActiveDashboard.myTrails is legit...
+ this.Document.myTrails; // this is equivalent to having a prefetchProxy for myTrails which is needed for the My Trails button in the UI which assumes that Doc.ActiveDashboard.myTrails is legit...
}
/**
@@ -85,6 +87,7 @@ export class CollectionDockingView extends CollectionSubView() {
const dragSource = CollectionDockingView.Instance?._goldenLayout.createDragSource(document.createElement('div'), config);
this.tabDragStart(dragSource, finishDrag);
dragSource._dragListener.onMouseDown({ pageX: e.pageX, pageY: e.pageY, preventDefault: emptyFunction, button: 0 });
+ return true;
};
tabItemDropped = () => DragManager.CompleteWindowDrag?.(false);
@@ -130,7 +133,7 @@ export class CollectionDockingView extends CollectionSubView() {
@undoBatch
@action
- public static ReplaceTab(document: Doc, panelName: OpenWhereMod, stack: any, addToSplit?: boolean, keyValue?: boolean): boolean {
+ public static ReplaceTab(document: Doc, mods: OpenWhereMod, stack: any, panelName: string, addToSplit?: boolean, keyValue?: boolean): boolean {
const instance = CollectionDockingView.Instance;
if (!instance) return false;
const newConfig = CollectionDockingView.makeDocumentConfig(document, panelName, undefined, keyValue);
@@ -151,7 +154,7 @@ export class CollectionDockingView extends CollectionSubView() {
}
return false;
}
- return CollectionDockingView.AddSplit(document, panelName, stack, panelName);
+ return CollectionDockingView.AddSplit(document, mods, stack, panelName);
}
@undoBatch
@@ -273,8 +276,9 @@ export class CollectionDockingView extends CollectionSubView() {
return true;
}
setupGoldenLayout = async () => {
- //const config = StrCast(this.props.Document.dockingConfig, JSON.stringify(DashboardView.resetDashboard(this.props.Document)));
- const config = StrCast(this.props.Document.dockingConfig);
+ if (this._unmounting) return;
+ //const config = StrCast(this.Document.dockingConfig, JSON.stringify(DashboardView.resetDashboard(this.Document)));
+ const config = StrCast(this.Document.dockingConfig);
if (config) {
const matches = config.match(/\"documentId\":\"[a-z0-9-]+\"/g);
const docids = matches?.map(m => m.replace('"documentId":"', '').replace('"', '')) ?? [];
@@ -310,6 +314,7 @@ export class CollectionDockingView extends CollectionSubView() {
};
componentDidMount: () => void = async () => {
+ this._unmounting = false;
if (this._containerRef.current) {
this._lightboxReactionDisposer = reaction(
() => LightboxView.LightboxDoc,
@@ -317,7 +322,7 @@ export class CollectionDockingView extends CollectionSubView() {
);
new _global.ResizeObserver(this.onResize).observe(this._containerRef.current);
this._reactionDisposer = reaction(
- () => StrCast(this.props.Document.dockingConfig),
+ () => StrCast(this.Document.dockingConfig),
config => {
if (!this._goldenLayout || this._ignoreStateChange !== config) {
// bcz: TODO! really need to diff config with ignoreStateChange and modify the current goldenLayout instead of building a new one.
@@ -327,7 +332,7 @@ export class CollectionDockingView extends CollectionSubView() {
}
);
reaction(
- () => this.props.PanelWidth(),
+ () => this._props.PanelWidth(),
width => !this._goldenLayout && width > 20 && setTimeout(() => this.setupGoldenLayout()), // need to wait for the collectiondockingview-container to have it's width/height since golden layout reads that to configure its windows
{ fireImmediately: true }
);
@@ -344,7 +349,9 @@ export class CollectionDockingView extends CollectionSubView() {
}
};
+ _unmounting = false;
componentWillUnmount: () => void = () => {
+ this._unmounting = true;
try {
this._goldenLayout.unbind('stackCreated', this.stackCreated);
this._goldenLayout.unbind('tabDestroyed', this.tabDestroyed);
@@ -374,14 +381,14 @@ export class CollectionDockingView extends CollectionSubView() {
.map(id => DocServer.GetCachedRefField(id))
.filter(f => f)
.map(f => f as Doc);
- const changesMade = this.props.Document.dockingConfig !== json;
+ const changesMade = this.Document.dockingConfig !== json;
if (changesMade) {
if (![AclAdmin, AclEdit].includes(GetEffectiveAcl(this.dataDoc))) {
this.layoutDoc.dockingConfig = json;
this.layoutDoc.data = new List<Doc>(docs);
} else {
- Doc.SetInPlace(this.rootDoc, 'dockingConfig', json, true);
- Doc.SetInPlace(this.rootDoc, 'data', new List<Doc>(docs), true);
+ Doc.SetInPlace(this.Document, 'dockingConfig', json, true);
+ Doc.SetInPlace(this.Document, 'data', new List<Doc>(docs), true);
}
}
this._flush?.end();
@@ -406,8 +413,9 @@ export class CollectionDockingView extends CollectionSubView() {
window.addEventListener('mouseup', this.onPointerUp);
if (!htmlTarget.closest('*.lm_content') && (htmlTarget.closest('*.lm_tab') || htmlTarget.closest('*.lm_stack'))) {
const className = typeof htmlTarget.className === 'string' ? htmlTarget.className : '';
- if (className.includes('lm_maximise')) this._flush = UndoManager.StartBatch('tab maximize');
- else {
+ if (className.includes('lm_maximise')) {
+ // this._flush = UndoManager.StartBatch('tab maximize');
+ } else {
const tabTarget = (e.target as HTMLElement)?.parentElement?.className.includes('lm_tab') ? (e.target as HTMLElement).parentElement : (e.target as HTMLElement);
const map = Array.from(this.tabMap).find(tab => tab.element[0] === tabTarget);
if (map?.DashDoc && DocumentManager.Instance.getFirstDocumentView(map.DashDoc)) {
@@ -423,7 +431,7 @@ export class CollectionDockingView extends CollectionSubView() {
};
public CaptureThumbnail() {
- const content = this.props.DocumentView?.()?.ContentDiv;
+ const content = this.DocumentView?.()?.ContentDiv;
if (content) {
const _width = Number(getComputedStyle(content).width.replace('px', ''));
const _height = Number(getComputedStyle(content).height.replace('px', ''));
@@ -441,7 +449,7 @@ export class CollectionDockingView extends CollectionSubView() {
if (clone) {
const cloned = await Doc.MakeClone(doc);
Array.from(cloned.map.entries()).map(entry => (json = json.replace(entry[0], entry[1][Id])));
- Doc.GetProto(cloned.clone).dockingConfig = json;
+ cloned.clone[DocData].dockingConfig = json;
return DashboardView.openDashboard(cloned.clone);
}
const matches = json.match(/\"documentId\":\"[a-z0-9-]+\"/g);
@@ -455,7 +463,7 @@ export class CollectionDockingView extends CollectionSubView() {
const newtab = origtabdocs.length ? Doc.MakeCopy(origtab, true, undefined, true) : Doc.MakeEmbedding(origtab);
const newtabdocs = origtabdocs.map(origtabdoc => Doc.MakeEmbedding(origtabdoc));
if (newtabdocs.length) {
- Doc.GetProto(newtab).data = new List<Doc>(newtabdocs);
+ newtab[DocData].data = new List<Doc>(newtabdocs);
newtabdocs.forEach(ntab => Doc.SetContainer(ntab, newtab));
}
json = json.replace(origtab[Id], newtab[Id]);
@@ -463,6 +471,7 @@ export class CollectionDockingView extends CollectionSubView() {
});
const copy = Docs.Create.DockDocument(newtabs, json, { title: incrementTitleCopy(StrCast(doc.title)) });
DashboardView.SetupDashboardTrails(copy);
+ DashboardView.SetupDashboardCalendars(copy); // Zaul TODO: needed?
return DashboardView.openDashboard(copy);
}
@@ -470,21 +479,21 @@ export class CollectionDockingView extends CollectionSubView() {
stateChanged = () => {
this._ignoreStateChange = JSON.stringify(this._goldenLayout.toConfig());
const json = JSON.stringify(this._goldenLayout.toConfig());
- const changesMade = this.props.Document.dockingConfig !== json;
+ const changesMade = this.Document.dockingConfig !== json;
return changesMade;
};
tabDestroyed = (tab: any) => {
this._flush = this._flush ?? UndoManager.StartBatch('tab movement');
if (tab.DashDoc && ![DocumentType.PRES].includes(tab.DashDoc?.type) && !tab.contentItem.config.props.keyValue) {
- Doc.AddDocToList(Doc.MyHeaderBar, 'data', tab.DashDoc);
+ Doc.AddDocToList(Doc.MyHeaderBar, 'data', tab.DashDoc, undefined, undefined, true);
// if you close a tab that is not embedded somewhere else (an embedded Doc can be opened simultaneously in a tab), then add the tab to recently closed
- if (tab.DashDoc.embedContainer === this.rootDoc) tab.DashDoc.embedContainer = undefined;
+ if (tab.DashDoc.embedContainer === this.Document) tab.DashDoc.embedContainer = undefined;
if (!tab.DashDoc.embedContainer) Doc.AddDocToList(Doc.MyRecentlyClosed, 'data', tab.DashDoc, undefined, true, true);
- Doc.RemoveDocFromList(Doc.GetProto(tab.DashDoc), 'proto_embeddings', tab.DashDoc);
+ Doc.RemoveDocFromList(tab.DashDoc[DocData], 'proto_embeddings', tab.DashDoc);
}
if (CollectionDockingView.Instance) {
- const dview = CollectionDockingView.Instance.props.Document;
+ const dview = CollectionDockingView.Instance.Document;
const fieldKey = CollectionDockingView.Instance.props.fieldKey;
Doc.RemoveDocFromList(dview, fieldKey, tab.DashDoc);
this.tabMap.delete(tab);
@@ -504,34 +513,37 @@ export class CollectionDockingView extends CollectionSubView() {
if (dashboard && e.target === stack.header?.element[0] && e.button === 2) {
dashboard['pane-count'] = NumCast(dashboard['pane-count']) + 1;
const docToAdd = Docs.Create.FreeformDocument([], {
- _width: this.props.PanelWidth(),
- _height: this.props.PanelHeight(),
+ _width: this._props.PanelWidth(),
+ _height: this._props.PanelHeight(),
_freeform_backgroundGrid: true,
_layout_fitWidth: true,
title: `Untitled Tab ${NumCast(dashboard['pane-count'])}`,
});
- Doc.AddDocToList(Doc.MyHeaderBar, 'data', docToAdd);
- inheritParentAcls(this.rootDoc, docToAdd, false);
+ Doc.AddDocToList(Doc.MyHeaderBar, 'data', docToAdd, undefined, undefined, true);
+ inheritParentAcls(this.Document, docToAdd, false);
CollectionDockingView.AddSplit(docToAdd, OpenWhereMod.none, stack);
}
});
- let addNewDoc = action(() => {
- const dashboard = Doc.ActiveDashboard;
- if (dashboard) {
- dashboard['pane-count'] = NumCast(dashboard['pane-count']) + 1;
- const docToAdd = Docs.Create.FreeformDocument([], {
- _width: this.props.PanelWidth(),
- _height: this.props.PanelHeight(),
- _layout_fitWidth: true,
- _freeform_backgroundGrid: true,
- title: `Untitled Tab ${NumCast(dashboard['pane-count'])}`,
- });
- Doc.AddDocToList(Doc.MyHeaderBar, 'data', docToAdd);
- inheritParentAcls(this.dataDoc, docToAdd, false);
- CollectionDockingView.AddSplit(docToAdd, OpenWhereMod.none, stack);
- }
- });
+ let addNewDoc = undoable(
+ action(() => {
+ const dashboard = Doc.ActiveDashboard;
+ if (dashboard) {
+ dashboard['pane-count'] = NumCast(dashboard['pane-count']) + 1;
+ const docToAdd = Docs.Create.FreeformDocument([], {
+ _width: this._props.PanelWidth(),
+ _height: this._props.PanelHeight(),
+ _layout_fitWidth: true,
+ _freeform_backgroundGrid: true,
+ title: `Untitled Tab ${NumCast(dashboard['pane-count'])}`,
+ });
+ Doc.AddDocToList(Doc.MyHeaderBar, 'data', docToAdd, undefined, undefined, true);
+ inheritParentAcls(this.dataDoc, docToAdd, false);
+ CollectionDockingView.AddSplit(docToAdd, OpenWhereMod.none, stack);
+ }
+ }),
+ 'add new tab'
+ );
stack.header?.controlsContainer
.find('.lm_close') //get the close icon
@@ -567,15 +579,15 @@ export class CollectionDockingView extends CollectionSubView() {
};
render() {
- const href = ImageCast(this.rootDoc.thumb)?.url?.href;
- return this.props.renderDepth > -1 ? (
+ const href = ImageCast(this.Document.thumb)?.url?.href;
+ return this._props.renderDepth > -1 ? (
<div>
{href ? (
<img
style={{ background: 'white', top: 0, position: 'absolute' }}
src={href} // + '?d=' + (new Date()).getTime()}
- width={this.props.PanelWidth()}
- height={this.props.PanelHeight()}
+ width={this._props.PanelWidth()}
+ height={this._props.PanelHeight()}
/>
) : (
<p>nested dashboards has no thumbnail</p>
@@ -589,7 +601,7 @@ export class CollectionDockingView extends CollectionSubView() {
ScriptingGlobals.add(
function openInLightbox(doc: any) {
- LightboxView.AddDocTab(doc, OpenWhere.lightbox);
+ LightboxView.Instance.AddDocTab(doc, OpenWhere.lightbox);
},
'opens up document in a lightbox',
'(doc: any)'
@@ -603,7 +615,7 @@ ScriptingGlobals.add(
// prettier-ignore
switch (doc) {
case '<ScriptingRepl />': return OverlayView.Instance.addWindow(<ScriptingRepl />, { x: 300, y: 100, width: 200, height: 200, title: 'Scripting REPL' });
- case "<UndoStack>": return OverlayView.Instance.addWindow(<UndoStack />, { x: 300, y: 100, width: 200, height: 200, title: 'Scripting REPL' });
+ case "<UndoStack />": return OverlayView.Instance.addWindow(<UndoStack />, { x: 300, y: 100, width: 200, height: 200, title: 'Undo stack' });
}
Doc.AddToMyOverlay(doc);
}
@@ -618,6 +630,3 @@ ScriptingGlobals.add(
'opens up document in screen overlay layer',
'(doc: any)'
);
-ScriptingGlobals.add(function useRightSplit(doc: any, addToRightSplit?: boolean) {
- CollectionDockingView.ReplaceTab(doc, OpenWhereMod.right, undefined, addToRightSplit);
-});