From dd08c20ec6df3fad6ecd6b16c787f10b0c23feb4 Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 2 May 2024 00:39:31 -0400 Subject: lots more dependency cycle unwinding. --- .../CollectionFreeFormClusters.ts | 31 ++++---- .../CollectionFreeFormInfoUI.tsx | 10 ++- .../CollectionFreeFormPannableContents.tsx | 9 ++- .../collectionFreeForm/CollectionFreeFormView.tsx | 88 +++++++++------------- .../collections/collectionFreeForm/MarqueeView.tsx | 15 ++-- 5 files changed, 72 insertions(+), 81 deletions(-) (limited to 'src/client/views/collections/collectionFreeForm') diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormClusters.ts b/src/client/views/collections/collectionFreeForm/CollectionFreeFormClusters.ts index 6fc295e27..26a52cd2a 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormClusters.ts +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormClusters.ts @@ -1,17 +1,16 @@ import { action, observable } from 'mobx'; +import { CollectionFreeFormView } from '.'; +import { intersectRect } from '../../../../Utils'; import { Doc, Opt } from '../../../../fields/Doc'; import { NumCast, StrCast } from '../../../../fields/Types'; -import { intersectRect } from '../../../../Utils'; import { DocumentType } from '../../../documents/DocumentTypes'; -import { DocumentManager } from '../../../util/DocumentManager'; import { DragManager } from '../../../util/DragManager'; import { dropActionType } from '../../../util/DropActionTypes'; -import { SelectionManager } from '../../../util/SelectionManager'; +import { StyleProp } from '../../StyleProp'; import { CollectionFreeFormDocumentView } from '../../nodes/CollectionFreeFormDocumentView'; +import { DocumentView } from '../../nodes/DocumentView'; import { FieldViewProps } from '../../nodes/FieldView'; -import { StyleProp } from '../../StyleProvider'; import './CollectionFreeFormView.scss'; -import { CollectionFreeFormView } from '.'; export class CollectionFreeFormClusters { private _view: CollectionFreeFormView; @@ -44,7 +43,7 @@ export class CollectionFreeFormClusters { const h = NumCast(doc1Layout._height) + clusterDistance; return doc1.z === doc2.z && intersectRect({ left: x, top: y, width: w, height: h }, { left: x2, top: y2, width: w2, height: h2 }); } - pickCluster(probe: number[]) { + handlePointerDown(probe: number[]) { this._hitCluster = this.childLayoutPairs .map(pair => pair.layout) .reduce((cluster, cd) => { @@ -62,13 +61,13 @@ export class CollectionFreeFormClusters { return this._hitCluster; } - tryDragCluster(e: PointerEvent) { + tryToDrag(e: PointerEvent) { const cluster = this._hitCluster; if (cluster !== -1) { const ptsParent = e; if (ptsParent) { const eles = this.childLayoutPairs.map(pair => pair.layout).filter(cd => (this.Document._freeform_useClusters ? NumCast(cd.layout_cluster) : NumCast(cd.group, -1)) === cluster); - const clusterDocs = eles.map(ele => DocumentManager.Instance.getDocumentView(ele, this.DocumentView)!); + const clusterDocs = eles.map(ele => DocumentView.getDocumentView(ele, this.DocumentView)!); const { left, top } = clusterDocs[0].getBounds || { left: 0, top: 0 }; const de = new DragManager.DocumentDragData(eles, e.ctrlKey || e.altKey ? dropActionType.embed : undefined); de.moveDocument = this.viewMoveDocument; @@ -87,7 +86,7 @@ export class CollectionFreeFormClusters { return false; } - initClusters() { + initLayout() { if (this.Document._freeform_useClusters && !this._clusterSets.length && this.childDocs.length) { return this.updateClusters(true); } @@ -97,11 +96,11 @@ export class CollectionFreeFormClusters { updateClusters(useClusters: boolean) { this.Document._freeform_useClusters = useClusters; this._clusterSets.length = 0; - this.childLayoutPairs.map(pair => pair.layout).map(c => this.updateCluster(c)); + this.childLayoutPairs.map(pair => pair.layout).map(c => this.addDocument(c)); } @action - updateClusterDocs(docs: Doc[]) { + addDocuments(docs: Doc[]) { const childLayouts = this.childLayoutPairs.map(pair => pair.layout); if (this.Document._freeform_useClusters) { const docFirst = docs[0]; @@ -143,12 +142,12 @@ export class CollectionFreeFormClusters { this._clusterSets[(doc.layout_cluster = NumCast(docFirst.layout_cluster))].push(doc); }); } - childLayouts.map(child => !this._clusterSets.some((set, i) => Doc.IndexOf(child, set) !== -1 && child.layout_cluster === i) && this.updateCluster(child)); + childLayouts.map(child => !this._clusterSets.some((set, i) => Doc.IndexOf(child, set) !== -1 && child.layout_cluster === i) && this.addDocument(child)); } } @action - updateCluster = (doc: Doc) => { + addDocument = (doc: Doc) => { const childLayouts = this.childLayoutPairs.map(pair => pair.layout); if (this.Document._freeform_useClusters) { this._clusterSets.forEach(set => Doc.IndexOf(doc, set) !== -1 && set.splice(Doc.IndexOf(doc, set), 1)); @@ -188,7 +187,7 @@ export class CollectionFreeFormClusters { const cluster = NumCast(doc?.layout_cluster); if (this.Document._freeform_useClusters && doc?.type !== DocumentType.IMG) { if (this._clusterSets.length <= cluster) { - setTimeout(() => doc && this.updateCluster(doc)); + setTimeout(() => doc && this.addDocument(doc)); } else { // choose a cluster color from a palette const colors = ['#da42429e', '#31ea318c', 'rgba(197, 87, 20, 0.55)', '#4a7ae2c4', 'rgba(216, 9, 255, 0.5)', '#ff7601', '#1dffff', 'yellow', 'rgba(27, 130, 49, 0.55)', 'rgba(0, 0, 0, 0.268)']; @@ -212,9 +211,9 @@ export class CollectionFreeFormClusters { return styleProp; }; - trySelectCluster = (addToSel: boolean) => { + tryToSelect = (addToSel: boolean) => { if (addToSel && this._hitCluster !== -1) { - !addToSel && SelectionManager.DeselectAll(); + !addToSel && DocumentView.DeselectAll(); const eles = this.childLayoutPairs.map(pair => pair.layout).filter(cd => (this.Document._freeform_useClusters ? NumCast(cd.layout_cluster) : NumCast(cd.group, -1)) === this._hitCluster); this.selectDocuments(eles); return true; diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx index 65a529d62..5d8373fc7 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx @@ -4,13 +4,13 @@ import * as React from 'react'; import { Doc, DocListCast, FieldType, FieldResult } from '../../../../fields/Doc'; import { InkTool } from '../../../../fields/InkField'; import { StrCast } from '../../../../fields/Types'; -import { LinkManager } from '../../../util/LinkManager'; import { ObservableReactComponent } from '../../ObservableReactComponent'; import { DocButtonState, DocumentLinksButton } from '../../nodes/DocumentLinksButton'; import { TopBar } from '../../topbar/TopBar'; import { CollectionFreeFormInfoState, InfoState, StateEntryFunc, infoState } from './CollectionFreeFormInfoState'; import './CollectionFreeFormView.scss'; import { DocData } from '../../../../fields/DocSymbols'; +import { CollectionFreeFormView } from './CollectionFreeFormView'; export interface CollectionFreeFormInfoUIProps { Document: Doc; @@ -21,6 +21,12 @@ export interface CollectionFreeFormInfoUIProps { @observer export class CollectionFreeFormInfoUI extends ObservableReactComponent { + public static Init() { + CollectionFreeFormView.SetInfoUICreator((doc: Doc, layout: Doc, childDocs: () => Doc[], close: () => void) => ( + // + + )); + } _firstDocPos = { x: 0, y: 0 }; constructor(props: any) { @@ -63,7 +69,7 @@ export class CollectionFreeFormInfoUI extends ObservableReactComponent DocumentLinksButton.StartLink; const linkUnstart = () => !DocumentLinksButton.StartLink; - const numDocLinks = () => LinkManager.Instance.getAllDirectLinks(firstDoc())?.length; + const numDocLinks = () => Doc.Links(firstDoc())?.length; const linkMenuOpen = () => DocButtonState.Instance.LinkEditorDocView; const activeTool = () => Doc.ActiveTool; diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormPannableContents.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormPannableContents.tsx index 90977d955..65a2fe0aa 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormPannableContents.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormPannableContents.tsx @@ -3,9 +3,8 @@ import { observer } from 'mobx-react'; import * as React from 'react'; import { Doc } from '../../../../fields/Doc'; import { ScriptField } from '../../../../fields/ScriptField'; -import { PresBox } from '../../nodes/trails/PresBox'; -import './CollectionFreeFormView.scss'; import { ObservableReactComponent } from '../../ObservableReactComponent'; +import './CollectionFreeFormView.scss'; export interface CollectionFreeFormPannableContentsProps { Document: Doc; @@ -20,12 +19,16 @@ export interface CollectionFreeFormPannableContentsProps { @observer export class CollectionFreeFormPannableContents extends ObservableReactComponent { + static _overlayPlugin: ((fform: Doc) => React.JSX.Element) | null = null; + public static SetOverlayPlugin(plugin: ((fform: Doc) => React.JSX.Element) | null) { + CollectionFreeFormPannableContents._overlayPlugin = plugin; + } constructor(props: CollectionFreeFormPannableContentsProps) { super(props); makeObservable(this); } @computed get presPaths() { - return this._props.showPresPaths() ? PresBox.Instance.pathLines(this._props.Document) : null; + return this._props.showPresPaths() ? CollectionFreeFormPannableContents._overlayPlugin?.(this._props.Document) : null; } // rectangle highlight used when following trail/link to a region of a collection that isn't a document showViewport = (viewport: { panX: number; panY: number; width: number; height: number } | undefined) => diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index f55d5a23f..6d901119e 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -26,15 +26,12 @@ import { aggregateBounds, emptyFunction, intersectRect, Utils } from '../../../. import { Docs } from '../../../documents/Documents'; import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes'; import { DocUtils } from '../../../documents/DocUtils'; -import { DocumentManager } from '../../../util/DocumentManager'; import { DragManager } from '../../../util/DragManager'; import { dropActionType } from '../../../util/DropActionTypes'; import { ReplayMovements } from '../../../util/ReplayMovements'; import { CompileScript } from '../../../util/Scripting'; import { ScriptingGlobals } from '../../../util/ScriptingGlobals'; -import { SelectionManager } from '../../../util/SelectionManager'; -import { freeformScrollMode } from '../../../util/SettingsManager'; -import { SnappingManager } from '../../../util/SnappingManager'; +import { freeformScrollMode, SnappingManager } from '../../../util/SnappingManager'; import { Transform } from '../../../util/Transform'; import { undoable, undoBatch, UndoManager } from '../../../util/UndoManager'; import { Timeline } from '../../animationtimeline/Timeline'; @@ -50,12 +47,11 @@ import { FocusViewOptions } from '../../nodes/FocusViewOptions'; import { FormattedTextBox } from '../../nodes/formattedText/FormattedTextBox'; import { OpenWhere } from '../../nodes/OpenWhere'; import { PinDocView, PinProps } from '../../PinFuncs'; -import { StyleProp } from '../../StyleProvider'; +import { StyleProp } from '../../StyleProp'; import { CollectionSubView } from '../CollectionSubView'; -import { TreeViewType } from '../CollectionTreeView'; +import { TreeViewType } from '../CollectionTreeViewType'; import { CollectionFreeFormBackgroundGrid } from './CollectionFreeFormBackgroundGrid'; import { CollectionFreeFormClusters } from './CollectionFreeFormClusters'; -import { CollectionFreeFormInfoUI } from './CollectionFreeFormInfoUI'; import { computePassLayout, computePivotLayout, computeStarburstLayout, computeTimelineLayout, PoolData, ViewDefBounds, ViewDefResult } from './CollectionFreeFormLayoutEngines'; import { CollectionFreeFormPannableContents } from './CollectionFreeFormPannableContents'; import { CollectionFreeFormRemoteCursors } from './CollectionFreeFormRemoteCursors'; @@ -87,8 +83,9 @@ export class CollectionFreeFormView extends CollectionSubView(); - public static from(dv?: DocumentView) { - return CollectionFreeFormDocumentView.from(dv)?.CollectionFreeFormView; + public static from(dv?: DocumentView): CollectionFreeFormView | undefined { + const parent = CollectionFreeFormDocumentView.from(dv)?._props.parent; + return parent instanceof CollectionFreeFormView ? parent : undefined; } _oldWheel: any; @@ -227,25 +224,6 @@ export class CollectionFreeFormView extends CollectionSubView { - CollectionFreeFormDocumentView.animFields.forEach(val => { - const findexed = Cast(doc[`${val.key}_indexed`], listSpec('number'), null); - findexed?.length <= timecode + 1 && findexed.push(undefined as any as number); - }); - CollectionFreeFormDocumentView.animStringFields.forEach(val => { - const findexed = Cast(doc[`${val}_indexed`], listSpec('string'), null); - findexed?.length <= timecode + 1 && findexed.push(undefined as any as string); - }); - CollectionFreeFormDocumentView.animDataFields(doc).forEach(val => { - const findexed = Cast(doc[`${val}_indexed`], listSpec(InkField), null); - findexed?.length <= timecode + 1 && findexed.push(undefined as any); - }); - }); - return newTimer; - } changeKeyFrame = (back = false) => { const currentFrame = Cast(this.Document._currentFrame, 'number', null); if (currentFrame === undefined) { @@ -256,7 +234,7 @@ export class CollectionFreeFormView extends CollectionSubView { - SelectionManager.DeselectAll(); - docs.map(doc => DocumentManager.Instance.getDocumentView(doc, this.DocumentView?.())).forEach(dv => dv && SelectionManager.SelectView(dv, true)); + DocumentView.DeselectAll(); + docs.map(doc => DocumentView.getDocumentView(doc, this.DocumentView?.())).forEach(dv => dv && DocumentView.SelectView(dv, true)); }; addDocument = (newBox: Doc | Doc[]) => { let retVal = false; @@ -301,7 +279,7 @@ export class CollectionFreeFormView extends CollectionSubView void) => DocumentManager.Instance.AddViewRenderedCb(doc, dv => finish(dv)); + const findDoc = (finish: (dv: DocumentView) => void) => DocumentView.addViewRenderedCb(doc, dv => finish(dv)); findDoc(dv => res(dv)); }); @@ -459,7 +437,7 @@ export class CollectionFreeFormView extends CollectionSubView { - if (this._clusters.tryDragCluster(e)) { + if (this._clusters.tryToDrag(e)) { e.stopPropagation(); // we're moving a cluster, so stop propagation and return true to end panning and let the document drag take over return true; } @@ -675,7 +653,7 @@ export class CollectionFreeFormView extends CollectionSubView DocumentManager.Instance.getDocumentView(doc, this.DocumentView?.())) + .map(doc => DocumentView.getDocumentView(doc, this.DocumentView?.())) .filter(inkView => inkView?.ComponentView instanceof InkingStroke) .map(inkView => inkView!) .map(inkView => ({ inkViewBounds: inkView.getBounds, inkStroke: inkView.ComponentView as InkingStroke, inkView })) @@ -776,7 +754,7 @@ export class CollectionFreeFormView extends CollectionSubView doc.type === DocumentType.INK && !doc.dontIntersect) .forEach(doc => { - const otherInk = DocumentManager.Instance.getDocumentView(doc, this.DocumentView?.())?.ComponentView as InkingStroke; + const otherInk = DocumentView.getDocumentView(doc, this.DocumentView?.())?.ComponentView as InkingStroke; const { inkData: otherInkData } = otherInk?.inkScaledData() ?? { inkData: [] }; const otherScreenPts = otherInkData.map(point => otherInk.ptToScreen(point)); const otherCtrlPts = otherScreenPts.map(spt => (ink.ComponentView as InkingStroke).ptFromScreen(spt)); @@ -1078,17 +1056,18 @@ export class CollectionFreeFormView extends CollectionSubView this.childDocs; closeInfo = action(() => { Doc.IsInfoUIDisabled = true }); // prettier-ignore - infoUI = () => (Doc.IsInfoUIDisabled || this.Document.annotationOn || this._props.renderDepth ? null : ); + static _infoUI: ((doc: Doc, layout: Doc, childDocs: () => Doc[], close: () => void) => JSX.Element) | null = null; + static SetInfoUICreator(func: (doc: Doc, layout: Doc, childDocs: () => Doc[], close: () => void) => JSX.Element) { + CollectionFreeFormView._infoUI = func; + } + infoUI = () => + Doc.IsInfoUIDisabled || this.Document.annotationOn || this._props.renderDepth + ? null // + : CollectionFreeFormView._infoUI?.(this.Document, this.layoutDoc, this.childDocsFunc, this.closeInfo) || null; componentDidMount() { this._props.setContentViewBox?.(this); @@ -1551,9 +1537,7 @@ export class CollectionFreeFormView extends CollectionSubView intersectRect(docDims(doc), rect); const snappableDocs = activeDocs.filter(doc => doc.z === undefined && isDocInView(doc, selRect)); // first see if there are any foreground docs to snap to - activeDocs - .filter(doc => doc.isGroup && SnappingManager.IsResizing !== doc[Id] && !DragManager.docsBeingDragged.includes(doc)) - .forEach(doc => DocumentManager.Instance.getDocumentView(doc)?.ComponentView?.dragStarting?.(snapToDraggedDoc, false, visited)); + activeDocs.filter(doc => doc.isGroup && SnappingManager.IsResizing !== doc[Id] && !DragManager.docsBeingDragged.includes(doc)).forEach(doc => DocumentView.getDocumentView(doc)?.ComponentView?.dragStarting?.(snapToDraggedDoc, false, visited)); const horizLines: number[] = []; const vertLines: number[] = []; @@ -1654,7 +1638,7 @@ export class CollectionFreeFormView extends CollectionSubView 0 ? undefined : this.nudge} addDocTab={this.addDocTab} slowLoadDocuments={this.slowLoadDocuments} - trySelectCluster={this._clusters.trySelectCluster} + trySelectCluster={this._clusters.tryToSelect} activeDocuments={this.getActiveDocuments} selectDocuments={this.selectDocuments} addDocument={this.addDocument} @@ -1744,22 +1728,22 @@ export class CollectionFreeFormView extends CollectionSubView selView[0].ComponentView?.setKeyFrameEditing?.(!selView[0].ComponentView?.getKeyFrameEditing?.())); return undefined; }); // eslint-disable-next-line prefer-arrow-callback ScriptingGlobals.add(function pinWithView(pinContent: boolean) { - SelectionManager.Views.forEach(view => + DocumentView.Selected().forEach(view => view._props.pinToPres(view.Document, { currentFrame: Cast(view.Document.currentFrame, 'number', null), pinData: { @@ -1772,16 +1756,16 @@ ScriptingGlobals.add(function pinWithView(pinContent: boolean) { }); // eslint-disable-next-line prefer-arrow-callback ScriptingGlobals.add(function bringToFront() { - SelectionManager.Views.forEach(view => CollectionFreeFormView.from(view)?.bringToFront(view.Document)); + DocumentView.Selected().forEach(view => CollectionFreeFormView.from(view)?.bringToFront(view.Document)); }); // eslint-disable-next-line prefer-arrow-callback ScriptingGlobals.add(function sendToBack() { - SelectionManager.Views.forEach(view => CollectionFreeFormView.from(view)?.bringToFront(view.Document, true)); + DocumentView.Selected().forEach(view => CollectionFreeFormView.from(view)?.bringToFront(view.Document, true)); }); // eslint-disable-next-line prefer-arrow-callback ScriptingGlobals.add(function datavizFromSchema() { // creating a dataviz doc to represent the schema table - SelectionManager.Views.forEach(viewIn => { + DocumentView.Selected().forEach(viewIn => { const view = viewIn; if (!view.layoutDoc.schema_columnKeys) { view.layoutDoc.schema_columnKeys = new List(['title', 'type', 'author', 'author_date']); diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index 3ab04f403..b96444024 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -17,15 +17,14 @@ import { CognitiveServices } from '../../../cognitive_services/CognitiveServices import { DocUtils } from '../../../documents/DocUtils'; import { DocumentType } from '../../../documents/DocumentTypes'; import { Docs, DocumentOptions } from '../../../documents/Documents'; -import { SelectionManager } from '../../../util/SelectionManager'; -import { freeformScrollMode } from '../../../util/SettingsManager'; -import { SnappingManager } from '../../../util/SnappingManager'; +import { SnappingManager, freeformScrollMode } from '../../../util/SnappingManager'; import { Transform } from '../../../util/Transform'; import { UndoManager, undoBatch } from '../../../util/UndoManager'; import { ContextMenu } from '../../ContextMenu'; import { ObservableReactComponent } from '../../ObservableReactComponent'; import { MarqueeViewBounds } from '../../PinFuncs'; import { PreviewCursor } from '../../PreviewCursor'; +import { DocumentView } from '../../nodes/DocumentView'; import { OpenWhere } from '../../nodes/OpenWhere'; import { pasteImageBitmap } from '../../nodes/WebBoxRenderer'; import { FormattedTextBox } from '../../nodes/formattedText/FormattedTextBox'; @@ -175,7 +174,7 @@ export class MarqueeView extends ObservableReactComponent | KeyboardEvent | undefined, hide?: boolean) => { const selected = this.marqueeSelect(false); - SelectionManager.DeselectAll(); + DocumentView.DeselectAll(); selected.forEach(doc => { hide ? (doc.hidden = true) : this._props.removeDocument?.(doc); }); @@ -380,7 +379,7 @@ export class MarqueeView extends ObservableReactComponent { const selected = this.marqueeSelect(false); - SelectionManager.DeselectAll(); + DocumentView.DeselectAll(); selected.forEach(d => this._props.removeDocument?.(d)); const newCollection = DocUtils.pileup(selected, this.Bounds.left + this.Bounds.width / 2, this.Bounds.top + this.Bounds.height / 2)!; this._props.addDocument?.(newCollection); @@ -390,7 +389,7 @@ export class MarqueeView extends ObservableReactComponent