diff options
Diffstat (limited to 'src')
130 files changed, 1254 insertions, 1461 deletions
diff --git a/src/client/documents/DocUtils.ts b/src/client/documents/DocUtils.ts index 0a47e0359..0c9fe0315 100644 --- a/src/client/documents/DocUtils.ts +++ b/src/client/documents/DocUtils.ts @@ -9,7 +9,7 @@ import { ClientUtils } from '../../ClientUtils'; import * as JSZipUtils from '../../JSZipUtils'; import { decycle } from '../../decycler/decycler'; import { DateField } from '../../fields/DateField'; -import { Doc, DocListCast, Field, FieldType, LinkedTo, Opt, SetActiveAudioLinker, StrListCast } from '../../fields/Doc'; +import { Doc, DocListCast, Field, FieldResult, FieldType, LinkedTo, Opt, StrListCast } from '../../fields/Doc'; import { DocData } from '../../fields/DocSymbols'; import { Id } from '../../fields/FieldSymbols'; import { InkDataFieldName, InkField } from '../../fields/InkField'; @@ -29,9 +29,7 @@ import { SerializationHelper } from '../util/SerializationHelper'; import { UndoManager, undoable } from '../util/UndoManager'; import { ContextMenu } from '../views/ContextMenu'; import { ContextMenuProps } from '../views/ContextMenuItem'; -import { FieldViewProps } from '../views/nodes/FieldView'; import { LinkDescriptionPopup } from '../views/nodes/LinkDescriptionPopup'; -import { LoadingBox } from '../views/nodes/LoadingBox'; import { OpenWhere } from '../views/nodes/OpenWhere'; import { TaskCompletionBox } from '../views/nodes/TaskCompletedBox'; import { DocumentType } from './DocumentTypes'; @@ -50,7 +48,7 @@ export namespace DocUtils { } if (key === LinkedTo) { // links are not a field value, so handled here. value is an expression of form ([field=]idToDoc("...")) - const allLinks = LinkManager.Instance.getAllRelatedLinks(doc); + const allLinks = Doc.Links(doc); const matchLink = (val: string, anchor: Doc) => { const linkedToExp = (val ?? '').split('='); if (linkedToExp.length === 1) return Field.toScriptString(anchor) === val; @@ -131,7 +129,7 @@ export namespace DocUtils { if (!unsets.length && !exists.length && !xs.length && !checks.length && !matches.length) return true; const failsNotEqualFacets = !xs.length ? false : xs.some(value => matchFieldValue(d, facetKey, value)); const satisfiesCheckFacets = !checks.length ? true : checks.some(value => matchFieldValue(d, facetKey, value)); - const satisfiesExistsFacets = !exists.length ? true : facetKey !== LinkedTo ? d[facetKey] !== undefined : LinkManager.Instance.getAllRelatedLinks(d).length; + const satisfiesExistsFacets = !exists.length ? true : facetKey !== LinkedTo ? d[facetKey] !== undefined : Doc.Links(d).length; const satisfiesUnsetsFacets = !unsets.length ? true : d[facetKey] === undefined; const satisfiesMatchFacets = !matches.length ? true @@ -170,19 +168,7 @@ export namespace DocUtils { return rangeFilteredDocs; } - export const ActiveRecordings: { props: FieldViewProps; getAnchor: (addAsAnnotation: boolean) => Doc }[] = []; - - export function MakeLinkToActiveAudio(getSourceDoc: () => Doc | undefined, broadcastEvent = true) { - broadcastEvent && runInAction(() => { Doc.RecordingEvent += 1; }); // prettier-ignore - return DocUtils.ActiveRecordings.map(audio => { - const sourceDoc = getSourceDoc(); - return sourceDoc && DocUtils.MakeLink(sourceDoc, audio.getAnchor(true) || audio.props.Document, { link_displayLine: false, link_relationship: 'recording annotation:linked recording', link_description: 'recording timeline' }); - }); - } - - SetActiveAudioLinker(MakeLinkToActiveAudio); - - export function MakeLink(source: Doc, target: Doc, linkSettings: { link_relationship?: string; link_description?: string; link_displayLine?: boolean }, id?: string, showPopup?: number[]) { + export function MakeLink(source: Doc, target: Doc, linkSettings: { link_relationship?: string; link_description?: string }, id?: string, showPopup?: number[]) { if (!linkSettings.link_relationship) linkSettings.link_relationship = target.type === DocumentType.RTF ? 'Commentary:Comments On' : 'link'; if (target.doc === Doc.UserDoc()) return undefined; @@ -232,7 +218,6 @@ export namespace DocUtils { title: ComputedField.MakeFunction('generateLinkTitle(this)') as any, link_anchor_1_useSmallAnchor: source.useSmallAnchor ? true : undefined, link_anchor_2_useSmallAnchor: target.useSmallAnchor ? true : undefined, - link_displayLine: linkSettings.link_displayLine, link_relationship: linkSettings.link_relationship, link_description: linkSettings.link_description, x: ComputedField.MakeFunction(`((this.${a}?.x||0)+(this.${b}?.x||0))/2`) as any, @@ -256,7 +241,6 @@ export namespace DocUtils { const script = scripts[key]; if (ScriptCast(doc[key])?.script.originalScript !== scripts[key] && script) { (key.startsWith('_') ? doc : Doc.GetProto(doc))[key] = ScriptField.MakeScript(script, { - self: Doc.name, this: Doc.name, dragData: Doc.DocDragDataName, value: 'any', @@ -579,7 +563,7 @@ export namespace DocUtils { export function LeavePushpin(doc: Doc, annotationField: string) { if (doc.followLinkToggle) return undefined; const context = Cast(doc.embedContainer, Doc, null) ?? Cast(doc.annotationOn, Doc, null); - const hasContextAnchor = LinkManager.Links(doc).some(l => (l.link_anchor_2 === doc && Cast(l.link_anchor_1, Doc, null)?.annotationOn === context) || (l.link_anchor_1 === doc && Cast(l.link_anchor_2, Doc, null)?.annotationOn === context)); + const hasContextAnchor = Doc.Links(doc).some(l => (l.link_anchor_2 === doc && Cast(l.link_anchor_1, Doc, null)?.annotationOn === context) || (l.link_anchor_1 === doc && Cast(l.link_anchor_2, Doc, null)?.annotationOn === context)); if (context && !hasContextAnchor && (context.type === DocumentType.VID || context.type === DocumentType.WEB || context.type === DocumentType.PDF || context.type === DocumentType.IMG)) { const pushpin = Docs.Create.FontIconDocument({ title: '', @@ -673,7 +657,7 @@ export namespace DocUtils { proto.data_duration = result.duration; } if (overwriteDoc) { - LoadingBox.removeCurrentlyLoading(overwriteDoc); + Doc.removeCurrentlyLoading(overwriteDoc); } generatedDocuments.push(doc); } @@ -717,7 +701,7 @@ export namespace DocUtils { if (overwriteDoc) { overwriteDoc.isLoading = false; overwriteDoc.loadingError = (result as any).message; - LoadingBox.removeCurrentlyLoading(overwriteDoc); + Doc.removeCurrentlyLoading(overwriteDoc); } } else newFilename && processFileupload(generatedDocuments, newFilename, mimetype ?? '', result, options, overwriteDoc); }); @@ -755,7 +739,7 @@ export namespace DocUtils { if ((result as any).message) { if (overwriteDoc) { overwriteDoc.loadingError = (result as any).message; - LoadingBox.removeCurrentlyLoading(overwriteDoc); + Doc.removeCurrentlyLoading(overwriteDoc); } } else newFilename && mimetype && processFileupload(generatedDocuments, newFilename, mimetype, result, options, overwriteDoc); }); @@ -868,6 +852,10 @@ export function FollowLinkScript() { return ScriptField.MakeScript('return followLink(this,altKey)', { altKey: 'boolean' }); } +export function IsFollowLinkScript(field: FieldResult<FieldType>) { + return ScriptCast(field)?.script.originalScript.includes('return followLink('); +} + ScriptingGlobals.add('Docs', Docs); // eslint-disable-next-line prefer-arrow-callback ScriptingGlobals.add(function copyDragFactory(dragFactory: Doc, asDelegate?: boolean) { diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index a3fea1ce4..1c18c0d84 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -19,7 +19,6 @@ import { SharingPermissions } from '../../fields/util'; import { PointData } from '../../pen-gestures/GestureTypes'; import { DocServer } from '../DocServer'; import { dropActionType } from '../util/DropActionTypes'; -import { LinkManager } from '../util/LinkManager'; import { CollectionViewType, DocumentType } from './DocumentTypes'; class EmptyBox { @@ -394,7 +393,6 @@ export class DocumentOptions { link?: string; link_description?: string; // added for links link_relationship?: string; // type of relatinoship a link represents - link_displayLine?: BOOLt = new BoolInfo('whether a link line should be dipslayed between the two link anchors'); link_displayArrow?: BOOLt = new BoolInfo("whether to display link's directional arrowhead"); link_anchor_1?: DOCt = new DocInfo('start anchor of a link'); link_anchor_2?: DOCt = new DocInfo('end anchor of a link'); @@ -810,7 +808,7 @@ export namespace Docs { 'link' ); - LinkManager.Instance.addLink(linkDoc); + Doc.AddLink(linkDoc); return linkDoc; } diff --git a/src/client/util/BranchingTrailManager.tsx b/src/client/util/BranchingTrailManager.tsx index 28c00644f..119d103c5 100644 --- a/src/client/util/BranchingTrailManager.tsx +++ b/src/client/util/BranchingTrailManager.tsx @@ -6,8 +6,8 @@ import * as React from 'react'; import { Doc } from '../../fields/Doc'; import { Id } from '../../fields/FieldSymbols'; import { OverlayView } from '../views/OverlayView'; +import { DocumentView } from '../views/nodes/DocumentView'; import { PresBox } from '../views/nodes/trails'; -import { DocumentManager } from './DocumentManager'; @observer export class BranchingTrailManager extends React.Component { @@ -46,7 +46,6 @@ export class BranchingTrailManager extends React.Component { // hi.overlayY = 100; // Doc.AddToMyOverlay(hi); - console.log(DocumentManager._overlayViews); }; @action setSlideHistoryStack = action((newArr: String[]) => { @@ -103,7 +102,7 @@ export class BranchingTrailManager extends React.Component { this.setSlideHistoryStack(newStack); removed.forEach(info => this.containsSet.delete(info.toString())); - DocumentManager.Instance.showDocument(targetDoc, { willZoomCentered: true }); + DocumentView.showDocument(targetDoc, { willZoomCentered: true }); if (this.slideHistoryStack.length === 0) { Doc.UserDoc().isBranchingMode = false; } diff --git a/src/client/util/CalendarManager.tsx b/src/client/util/CalendarManager.tsx index 8e3936d34..77cf80151 100644 --- a/src/client/util/CalendarManager.tsx +++ b/src/client/util/CalendarManager.tsx @@ -13,15 +13,12 @@ import { Doc, DocListCast } from '../../fields/Doc'; import { DocData } from '../../fields/DocSymbols'; import { StrCast } from '../../fields/Types'; import { Docs } from '../documents/Documents'; -import { DictationOverlay } from '../views/DictationOverlay'; import { MainViewModal } from '../views/MainViewModal'; import { ObservableReactComponent } from '../views/ObservableReactComponent'; import { DocumentView } from '../views/nodes/DocumentView'; import { TaskCompletionBox } from '../views/nodes/TaskCompletedBox'; import './CalendarManager.scss'; -import { DocumentManager } from './DocumentManager'; -import { SelectionManager } from './SelectionManager'; -import { SettingsManager } from './SettingsManager'; +import { SnappingManager } from './SnappingManager'; // import 'react-date-range/dist/styles.css'; // import 'react-date-range/dist/theme/default.css'; @@ -85,11 +82,9 @@ export class CalendarManager extends ObservableReactComponent<{}> { }; public open = (target?: DocumentView, targetDoc?: Doc) => { - console.log('hi'); runInAction(() => { this.targetDoc = targetDoc || target?.Document; this.targetDocView = target; - DictationOverlay.Instance.hasActiveModal = true; this.isOpen = this.targetDoc !== undefined; }); }; @@ -99,7 +94,6 @@ export class CalendarManager extends ObservableReactComponent<{}> { TaskCompletionBox.taskCompleted = false; setTimeout( action(() => { - DictationOverlay.Instance.hasActiveModal = false; this.targetDoc = undefined; }), 500 @@ -137,7 +131,7 @@ export class CalendarManager extends ObservableReactComponent<{}> { // TODO: Make undoable private addToCalendar = () => { - const docs = SelectionManager.Views.length < 2 ? [this.targetDoc] : SelectionManager.Views.map(docView => docView.Document); + const docs = DocumentView.Selected().length < 2 ? [this.targetDoc] : DocumentView.Selected().map(docView => docView.Document); const targetDoc = this.layoutDocAcls ? docs[0] : docs[0]?.[DocData]; // doc to add to calendar console.log(targetDoc); @@ -190,14 +184,14 @@ export class CalendarManager extends ObservableReactComponent<{}> { private focusOn = (contents: string) => { const title = this.targetDoc ? StrCast(this.targetDoc.title) : ''; - const docs = SelectionManager.Views.length > 1 ? SelectionManager.Views.map(docView => docView.Document) : [this.targetDoc]; + const docs = DocumentView.Selected().length > 1 ? DocumentView.Selected().map(docView => docView.Document) : [this.targetDoc]; return ( <span className="focus-span" title={title} onClick={() => { if (this.targetDoc && this.targetDocView && docs.length === 1) { - DocumentManager.Instance.showDocument(this.targetDoc, { willZoomCentered: true }); + DocumentView.showDocument(this.targetDoc, { willZoomCentered: true }); } }} onPointerEnter={action(() => { @@ -251,17 +245,17 @@ export class CalendarManager extends ObservableReactComponent<{}> { @computed get calendarInterface() { - const docs = SelectionManager.Views.length < 2 ? [this.targetDoc] : SelectionManager.Views.map(docView => docView.Document); + const docs = DocumentView.Selected().length < 2 ? [this.targetDoc] : DocumentView.Selected().map(docView => docView.Document); const targetDoc = this.layoutDocAcls ? docs[0] : docs[0]?.[DocData]; return ( <div className="calendar-interface" style={{ - background: SettingsManager.userBackgroundColor, + background: SnappingManager.userBackgroundColor, color: StrCast(Doc.UserDoc().userColor), }}> - <p className="selected-doc-title" style={{ color: SettingsManager.userColor }}> + <p className="selected-doc-title" style={{ color: SnappingManager.userColor }}> <b>{this.focusOn(docs.length < 2 ? StrCast(targetDoc?.title, 'this document') : '-multiple-')}</b> </p> <div className="creation-type-container"> diff --git a/src/client/util/CaptureManager.tsx b/src/client/util/CaptureManager.tsx index a03c80f2a..4fd934774 100644 --- a/src/client/util/CaptureManager.tsx +++ b/src/client/util/CaptureManager.tsx @@ -10,8 +10,7 @@ import { DocCast, StrCast } from '../../fields/Types'; import { LightboxView } from '../views/LightboxView'; import { MainViewModal } from '../views/MainViewModal'; import './CaptureManager.scss'; -import { LinkManager } from './LinkManager'; -import { SelectionManager } from './SelectionManager'; +import { DocumentView } from '../views/nodes/DocumentView'; @observer export class CaptureManager extends React.Component<{}> { @@ -56,7 +55,7 @@ export class CaptureManager extends React.Component<{}> { const doc = this._document; const order: JSX.Element[] = []; if (doc) { - LinkManager.Links(doc).forEach((l, i) => + Doc.Links(doc).forEach((l, i) => order.push( <div className="list-item"> <div className="number">{i}</div> @@ -88,8 +87,8 @@ export class CaptureManager extends React.Component<{}> { <div className="cancel" onClick={() => { - const selected = SelectionManager.Views.slice(); - SelectionManager.DeselectAll(); + const selected = DocumentView.Selected(); + DocumentView.DeselectAll(); selected.map(dv => dv.props.removeDocument?.(dv.Document)); this.close(); }}> diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 15c8fec5a..ffc1304bf 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -1,4 +1,4 @@ -import { observable, reaction, runInAction } from "mobx"; +import { reaction, runInAction } from "mobx"; import * as rp from 'request-promise'; import { ClientUtils, OmitKeys } from "../../ClientUtils"; import { Doc, DocListCast, DocListCastAsync, Opt } from "../../fields/Doc"; @@ -19,7 +19,8 @@ import { CollectionViewType, DocumentType } from "../documents/DocumentTypes"; import { Docs, DocumentOptions, FInfo, FInfoFieldType } from "../documents/Documents"; import { DashboardView } from "../views/DashboardView"; import { OverlayView } from "../views/OverlayView"; -import { CollectionTreeView, TreeViewType } from "../views/collections/CollectionTreeView"; +import { CollectionTreeView } from "../views/collections/CollectionTreeView"; +import { TreeViewType } from "../views/collections/CollectionTreeViewType"; import { Colors } from "../views/global/globalEnums"; import { mediaState } from "../views/nodes/AudioBox"; import { ButtonType, FontIconBox } from "../views/nodes/FontIconBox/FontIconBox"; @@ -30,8 +31,9 @@ import { ImportElementBox } from "../views/nodes/importBox/ImportElementBox"; import { DragManager } from "./DragManager"; import { dropActionType } from "./DropActionTypes"; import { MakeTemplate } from "./DropConverter"; -import { LinkManager, UPDATE_SERVER_CACHE } from "./LinkManager"; +import { UPDATE_SERVER_CACHE } from "./LinkManager"; import { ScriptingGlobals } from "./ScriptingGlobals"; +import { SelectionManager } from "./SelectionManager"; import { ColorScheme } from "./SettingsManager"; import { SnappingManager } from "./SnappingManager"; import { UndoManager } from "./UndoManager"; @@ -937,14 +939,13 @@ pie title Minerals in my tap water DocUtils.AssignDocField(doc, "globalScriptDatabase", () => Docs.Prototypes.MainScriptDocument(), {}); DocUtils.AssignDocField(doc, "myHeaderBar", (opts) => Docs.Create.MulticolumnDocument([], opts), { title: "My Header Bar", isSystem: true, _chromeHidden:true, layout_maxShown: 10, childLayoutFitWidth:false, childDocumentsActive:false, dropAction: dropActionType.move}); // drop down panel at top of dashboard for stashing documents - Doc.AddDocToList(Doc.MyFilesystem, undefined, Doc.MyDashboards) - Doc.AddDocToList(Doc.MyFilesystem, undefined, Doc.MySharedDocs) - Doc.AddDocToList(Doc.MyFilesystem, undefined, Doc.MyRecentlyClosed) - - Doc.GetProto(DocCast(Doc.UserDoc().emptyWebpage)).data = new WebField("https://www.wikipedia.org") + SelectionManager.DeselectAll(); // this forces SelectionManager implementation to copy over to DocumentView's API. This also triggers the LinkManager to be created + + Doc.AddDocToList(Doc.MyFilesystem, undefined, Doc.MyDashboards); + Doc.AddDocToList(Doc.MyFilesystem, undefined, Doc.MySharedDocs); + Doc.AddDocToList(Doc.MyFilesystem, undefined, Doc.MyRecentlyClosed); - // eslint-disable-next-line no-new - new LinkManager(); + Doc.GetProto(DocCast(Doc.UserDoc().emptyWebpage)).data = new WebField("https://www.wikipedia.org"); DocServer.CacheNeedsUpdate() && setTimeout(UPDATE_SERVER_CACHE, 2500); setInterval(UPDATE_SERVER_CACHE, 120000); @@ -969,12 +970,11 @@ pie title Minerals in my tap water }); } - @observable public static ServerVersion: string = ';' public static async loadCurrentUser() { return rp.get(ClientUtils.prepend("/getCurrentUser")).then(async response => { if (response) { const result: { version: string, userDocumentId: string, sharingDocumentId: string, linkDatabaseId: string, email: string, cacheDocumentIds: string, resolvedPorts: string } = JSON.parse(response); - runInAction(() => { CurrentUserUtils.ServerVersion = result.version; }); + runInAction(() => { SnappingManager.SetServerVersion(result.version); }); ClientUtils.SetCurrentUserEmail(result.email); resolvedPorts = result.resolvedPorts as any; DocServer.init(window.location.protocol, window.location.hostname, resolvedPorts?.socket, result.email); diff --git a/src/client/util/DictationManager.ts b/src/client/util/DictationManager.ts index 97ee628e2..bc9fe813f 100644 --- a/src/client/util/DictationManager.ts +++ b/src/client/util/DictationManager.ts @@ -10,12 +10,13 @@ import { RichTextField } from '../../fields/RichTextField'; import { listSpec } from '../../fields/Schema'; import { Cast, CastCtor } from '../../fields/Types'; import { AudioField, ImageField } from '../../fields/URLField'; +import { AudioAnnoState } from '../../server/SharedMediaTypes'; +import { Networking } from '../Network'; import { DocumentType } from '../documents/DocumentTypes'; import { Docs } from '../documents/Documents'; import { DictationOverlay } from '../views/DictationOverlay'; import { DocumentView } from '../views/nodes/DocumentView'; import { OpenWhere } from '../views/nodes/OpenWhere'; -import { SelectionManager } from './SelectionManager'; import { UndoManager } from './UndoManager'; /** @@ -244,7 +245,7 @@ export namespace DictationManager { export const execute = async (phrase: string) => UndoManager.RunInBatch(async () => { console.log('PHRASE: ' + phrase); - const targets = SelectionManager.Views; + const targets = DocumentView.Selected(); if (!targets || !targets.length) { return undefined; } @@ -395,4 +396,43 @@ export namespace DictationManager { }, ]; } + export function recordAudioAnnotation(dataDoc: Doc, field: string, onRecording?: (stop: () => void) => void, onEnd?: () => void) { + let gumStream: any; + let recorder: any; + navigator.mediaDevices.getUserMedia({ audio: true }).then(stream => { + let audioTextAnnos = Cast(dataDoc[field + '_audioAnnotations_text'], listSpec('string'), null); + if (audioTextAnnos) audioTextAnnos.push(''); + else audioTextAnnos = dataDoc[field + '_audioAnnotations_text'] = new List<string>(['']); + DictationManager.Controls.listen({ + interimHandler: value => { audioTextAnnos[audioTextAnnos.length - 1] = value; }, // prettier-ignore + continuous: { indefinite: false }, + }).then(results => { + if (results && [DictationManager.Controls.Infringed].includes(results)) { + DictationManager.Controls.stop(); + } + onEnd?.(); + }); + + gumStream = stream; + recorder = new MediaRecorder(stream); + recorder.ondataavailable = async (e: any) => { + const [{ result }] = await Networking.UploadFilesToServer({ file: e.data }); + if (!(result instanceof Error)) { + const audioField = new AudioField(result.accessPaths.agnostic.client); + const audioAnnos = Cast(dataDoc[field + '_audioAnnotations'], listSpec(AudioField), null); + if (audioAnnos) audioAnnos.push(audioField); + else dataDoc[field + '_audioAnnotations'] = new List([audioField]); + } + }; + recorder.start(); + const stopFunc = () => { + recorder.stop(); + DictationManager.Controls.stop(/* false */); + dataDoc.audioAnnoState = AudioAnnoState.stopped; + gumStream.getAudioTracks()[0].stop(); + }; + if (onRecording) onRecording(stopFunc); + else setTimeout(stopFunc, 5000); + }); + } } diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index 8f0c2b0bf..5bcac7330 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -1,23 +1,17 @@ import { Howl } from 'howler'; import { action, computed, makeObservable, observable, ObservableSet, observe } from 'mobx'; import { Doc, Opt } from '../../fields/Doc'; -import { AclAdmin, AclEdit, Animation, DocData } from '../../fields/DocSymbols'; +import { Animation, DocData } from '../../fields/DocSymbols'; import { Id } from '../../fields/FieldSymbols'; import { listSpec } from '../../fields/Schema'; import { Cast, DocCast, NumCast, StrCast } from '../../fields/Types'; import { AudioField } from '../../fields/URLField'; -import { GetEffectiveAcl } from '../../fields/util'; import { CollectionViewType } from '../documents/DocumentTypes'; -import { TabDocView } from '../views/collections/TabDocView'; import { LightboxView } from '../views/LightboxView'; import { DocumentView, DocumentViewInternal } from '../views/nodes/DocumentView'; import { FocusViewOptions } from '../views/nodes/FocusViewOptions'; -import { KeyValueBox } from '../views/nodes/KeyValueBox'; -import { LinkAnchorBox } from '../views/nodes/LinkAnchorBox'; import { OpenWhere } from '../views/nodes/OpenWhere'; import { PresBox } from '../views/nodes/trails'; -import { ScriptingGlobals } from './ScriptingGlobals'; -import { SelectionManager } from './SelectionManager'; type childIterator = { viewSpec: Opt<Doc>; childDocView: Opt<DocumentView>; focused: boolean; contextPath: Doc[] }; export class DocumentManager { @@ -29,10 +23,9 @@ export class DocumentManager { } // global holds all of the nodes (regardless of which collection they're in) - @observable _documentViews = new Set<DocumentView>(); - @observable.shallow public CurrentlyLoading: Doc[] = []; + @observable private _documentViews = new Set<DocumentView>(); @computed public get DocumentViews() { - return Array.from(this._documentViews).filter(view => !(view.ComponentView instanceof KeyValueBox) && (!LightboxView.LightboxDoc || LightboxView.Contains(view))); + return Array.from(this._documentViews).filter(view => (!view.ComponentView?.dontRegisterView?.() && !LightboxView.LightboxDoc) || LightboxView.Contains(view)); } public AddDocumentView(dv: DocumentView) { this._documentViews.add(dv); @@ -44,7 +37,19 @@ export class DocumentManager { // private constructor so no other class can create a nodemanager private constructor() { makeObservable(this); - observe(this.CurrentlyLoading, change => { + + DocumentView.allViews = () => this.DocumentViews; + DocumentView.addView = this.AddView; + DocumentView.removeView = this.RemoveView; + DocumentView.showDocument = this.showDocument; + DocumentView.showDocumentView = this.showDocumentView; + DocumentView.linkCommonAncestor = DocumentManager.LinkCommonAncestor; + DocumentView.addViewRenderedCb = this.AddViewRenderedCb; + DocumentView.getFirstDocumentView = this.getFirstDocumentView; + DocumentView.getDocumentView = this.getDocumentView; + DocumentView.getContextPath = DocumentManager.GetContextPath; + DocumentView.getLightboxDocumentView = this.getLightboxDocumentView; + observe(Doc.CurrentlyLoading, change => { // watch CurrentlyLoading-- when something is loaded, it's removed from the list and we have to update its icon if it were iconified since LoadingBox icons are different than the media they become switch (change.type as any) { case 'update': @@ -92,17 +97,12 @@ export class DocumentManager { @action public AddView = (view: DocumentView) => { - if (!view._props.LayoutTemplateString?.includes(KeyValueBox.name) && - !view._props.LayoutTemplateString?.includes(LinkAnchorBox.name)) { - this.AddDocumentView(view); - this.callAddViewFuncs(view); - } // prettier-ignore + this.AddDocumentView(view); + this.callAddViewFuncs(view); }; public RemoveView = action((view: DocumentView) => { - if (!view._props.LayoutTemplateString?.includes(KeyValueBox.name) && !view._props.LayoutTemplateString?.includes(LinkAnchorBox.name)) { - this.DeleteDocumentView(view); - } - SelectionManager.DeselectView(view); + this.DeleteDocumentView(view); + DocumentView.DeselectView(view); }); // gets all views @@ -203,6 +203,11 @@ export class DocumentManager { } static _overlayViews = new ObservableSet<DocumentView>(); + /** + * Find the nearest common ancestor collection that contains a link's source and target + * @param linkDoc + * @returns common ancestor DocumentView + */ public static LinkCommonAncestor(linkDoc: Doc) { const getAnchor = (which: number) => { const anch = DocCast(linkDoc['link_anchor_' + which]); @@ -245,11 +250,8 @@ export class DocumentManager { Doc.RemoveDocFromList(Doc.MyRecentlyClosed, undefined, targetDoc); const docContextPath = DocumentManager.GetContextPath(targetDoc, true); if (docContextPath.some(doc => doc.hidden)) options.toggleTarget = false; - const tabView = Array.from(TabDocView._allTabs).find(view => view._document === docContextPath[0]); - if (!tabView?._activated && tabView?._document) { - options.toggleTarget = false; - TabDocView.Activate(tabView?._document); - } + if (DocumentView.activateTabView(docContextPath[0])) options.toggleTarget = false; + const rootContextView = docContextPath.length && (await new Promise<DocumentView>(res => { @@ -260,7 +262,7 @@ export class DocumentManager { return; } options.didMove = true; - (!LightboxView.LightboxDoc && docContextPath.some(doc => TabDocView.Activate(doc))) || DocumentViewInternal.addDocTabFunc(docContextPath[0], options.openLocation ?? OpenWhere.addRight); + (!LightboxView.LightboxDoc && docContextPath.some(doc => DocumentView.activateTabView(doc))) || DocumentViewInternal.addDocTabFunc(docContextPath[0], options.openLocation ?? OpenWhere.addRight); this.AddViewRenderedCb(docContextPath[0], dv => res(dv)); })); if (options.openLocation === OpenWhere.lightbox) { @@ -355,33 +357,4 @@ export class DocumentManager { } } } -// eslint-disable-next-line default-param-last -export function DocFocusOrOpen(docIn: Doc, optionsIn: FocusViewOptions = { willZoomCentered: true, zoomScale: 0, openLocation: OpenWhere.toggleRight }, containingDoc?: Doc) { - let doc = docIn; - const options = optionsIn; - const func = () => { - const cv = DocumentManager.Instance.getDocumentView(containingDoc); - const dv = DocumentManager.Instance.getDocumentView(doc, cv); - if (dv && (!containingDoc || dv.containerViewPath?.().lastElement()?.Document === containingDoc)) { - DocumentManager.Instance.showDocumentView(dv, options).then(() => dv && Doc.linkFollowHighlight(dv.Document)); - } else { - const container = DocCast(containingDoc ?? doc.embedContainer ?? Doc.BestEmbedding(doc)); - const showDoc = !Doc.IsSystem(container) && !cv ? container : doc; - options.toggleTarget = undefined; - DocumentManager.Instance.showDocument(showDoc, options, () => DocumentManager.Instance.showDocument(doc, { ...options, openLocation: undefined })).then(() => { - const cvFound = DocumentManager.Instance.getDocumentView(containingDoc); - const dvFound = DocumentManager.Instance.getDocumentView(doc, cvFound); - dvFound && Doc.linkFollowHighlight(dvFound.Document); - }); - } - }; - if (Doc.IsDataProto(doc) && Doc.GetEmbeddings(doc).some(embed => embed.hidden && [AclAdmin, AclEdit].includes(GetEffectiveAcl(embed)))) { - doc = Doc.GetEmbeddings(doc).find(embed => embed.hidden && [AclAdmin, AclEdit].includes(GetEffectiveAcl(embed)))!; - } - if (doc.hidden) { - doc.hidden = false; - options.toggleTarget = false; - setTimeout(func); - } else func(); -} -ScriptingGlobals.add(DocFocusOrOpen); +setTimeout(() => DocumentManager.Instance); diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 34f54be2e..fda505420 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -27,8 +27,6 @@ import { ScriptCast } from '../../fields/Types'; import { Docs } from '../documents/Documents'; import { DocumentView } from '../views/nodes/DocumentView'; import { dropActionType } from './DropActionTypes'; -import { ScriptingGlobals } from './ScriptingGlobals'; -import { SelectionManager } from './SelectionManager'; import { SnappingManager } from './SnappingManager'; import { UndoManager } from './UndoManager'; @@ -648,14 +646,3 @@ export namespace DragManager { document.addEventListener('pointerup', upHandler, true); } } - -// eslint-disable-next-line prefer-arrow-callback -ScriptingGlobals.add(function toggleRaiseOnDrag(readOnly?: boolean) { - if (readOnly) { - return SelectionManager.Views.some(dv => dv.Document.keepZWhenDragged); - } - SelectionManager.Views.forEach(dv => { - dv.Document.keepZWhenDragged = !dv.Document.keepZWhenDragged; - }); - return undefined; -}); diff --git a/src/client/util/GroupManager.tsx b/src/client/util/GroupManager.tsx index e1a503ac9..5701a22c0 100644 --- a/src/client/util/GroupManager.tsx +++ b/src/client/util/GroupManager.tsx @@ -77,7 +77,7 @@ export class GroupManager extends ObservableReactComponent<{}> { */ @action open = () => { - // SelectionManager.DeselectAll(); + // DocumentView.DeselectAll(); this.isOpen = true; this.populateUsers(); }; diff --git a/src/client/util/HypothesisUtils.ts b/src/client/util/HypothesisUtils.ts index dd18b2533..5a6522dc8 100644 --- a/src/client/util/HypothesisUtils.ts +++ b/src/client/util/HypothesisUtils.ts @@ -6,8 +6,6 @@ import { WebField } from '../../fields/URLField'; import { Docs } from '../documents/Documents'; import { DocumentLinksButton } from '../views/nodes/DocumentLinksButton'; import { DocumentView } from '../views/nodes/DocumentView'; -import { DocumentManager } from './DocumentManager'; -import { SelectionManager } from './SelectionManager'; export namespace Hypothesis { /** @@ -25,7 +23,7 @@ export namespace Hypothesis { * Search for a WebDocument whose url field matches the given uri, return undefined if not found */ export const findWebDoc = async (uri: string) => { - const currentDoc = SelectionManager.Docs.lastElement(); + const currentDoc = DocumentView.Selected().lastElement()?.Document; if (currentDoc && Cast(currentDoc.data, WebField)?.url.href === uri) return currentDoc; // always check first whether the currently selected doc is the annotation's source, only use Search otherwise const results: Doc[] = []; @@ -39,7 +37,7 @@ export namespace Hypothesis { // }) // ); - const onScreenResults = results.filter(doc => DocumentManager.Instance.getFirstDocumentView(doc)); + const onScreenResults = results.filter(doc => DocumentView.getFirstDocumentView(doc)); return onScreenResults.length ? onScreenResults[0] : results.length ? results[0] : undefined; // prioritize results that are currently on the screen }; @@ -65,7 +63,7 @@ export namespace Hypothesis { DocumentLinksButton.AnnotationId = annotationId; DocumentLinksButton.AnnotationUri = annotationUri; }); - const endLinkView = DocumentManager.Instance.getFirstDocumentView(sourceDoc); + const endLinkView = DocumentView.getFirstDocumentView(sourceDoc); const rect = document.body.getBoundingClientRect(); const x = rect.x + rect.width / 2; const y = 250; @@ -182,7 +180,7 @@ export namespace Hypothesis { bubbles: true, }) ); - const targetView: Opt<DocumentView> = DocumentManager.Instance.getFirstDocumentView(target); + const targetView: Opt<DocumentView> = DocumentView.getFirstDocumentView(target); const position = targetView?.screenToViewTransform().inverse().transformPoint(0, 0); targetView && position && simulateMouseClick(targetView.ContentDiv!, position[0], position[1], position[0], position[1], false); }, 300); diff --git a/src/client/util/Import & Export/DirectoryImportBox.tsx b/src/client/util/Import & Export/DirectoryImportBox.tsx index 398ba3c04..baa0786b7 100644 --- a/src/client/util/Import & Export/DirectoryImportBox.tsx +++ b/src/client/util/Import & Export/DirectoryImportBox.tsx @@ -17,7 +17,6 @@ // import { DocumentType } from '../../documents/DocumentTypes'; // import { Networking } from '../../Network'; // import { FieldView, FieldViewProps } from '../../views/nodes/FieldView'; -// import { DocumentManager } from '../DocumentManager'; // import './DirectoryImportBox.scss'; // import ImportMetadataEntry, { keyPlaceholder, valuePlaceholder } from './ImportMetadataEntry'; // import * as React from 'react'; diff --git a/src/client/util/LinkFollower.ts b/src/client/util/LinkFollower.ts index a6f6122ab..9a0edcfec 100644 --- a/src/client/util/LinkFollower.ts +++ b/src/client/util/LinkFollower.ts @@ -1,14 +1,12 @@ import { action, runInAction } from 'mobx'; -import { Doc, DocListCast, FieldResult, FieldType, Opt } from '../../fields/Doc'; -import { BoolCast, Cast, DocCast, NumCast, ScriptCast, StrCast } from '../../fields/Types'; +import { Doc, DocListCast, Opt } from '../../fields/Doc'; +import { BoolCast, Cast, DocCast, NumCast, StrCast } from '../../fields/Types'; import { DocumentType } from '../documents/DocumentTypes'; +import { DocumentView } from '../views/nodes/DocumentView'; import { FocusViewOptions } from '../views/nodes/FocusViewOptions'; import { OpenWhere } from '../views/nodes/OpenWhere'; import { PresBox } from '../views/nodes/trails'; -import { DocumentManager } from './DocumentManager'; -import { LinkManager } from './LinkManager'; import { ScriptingGlobals } from './ScriptingGlobals'; -import { SelectionManager } from './SelectionManager'; import { SnappingManager } from './SnappingManager'; import { UndoManager } from './UndoManager'; /* @@ -24,6 +22,9 @@ import { UndoManager } from './UndoManager'; * - user defined kvps */ export class LinkFollower { + public static Init() { + DocumentView.FollowLink = LinkFollower.FollowLink; + } // follows a link - if the target is on screen, it highlights/pans to it. // if the target isn't onscreen, then it will open up the target in the lightbox, or in place // depending on the followLinkLocation property of the source (or the link itself as a fallback); @@ -42,9 +43,9 @@ export class LinkFollower { }; public static traverseLink(link: Opt<Doc>, sourceDoc: Doc, finished?: () => void, traverseBacklink?: boolean) { - const getView = (doc: Doc) => DocumentManager.Instance.getFirstDocumentView(DocCast(doc.layout_unrendered ? doc.annotationOn : doc)); + const getView = (doc: Doc) => DocumentView.getFirstDocumentView(DocCast(doc.layout_unrendered ? doc.annotationOn : doc)); const isAnchor = (source: Doc, anchor: Doc) => Doc.AreProtosEqual(anchor, source) || Doc.AreProtosEqual(anchor.annotationOn as Doc, source); - const linkDocs = link ? [link] : LinkManager.Links(sourceDoc); + const linkDocs = link ? [link] : Doc.Links(sourceDoc); const fwdLinks = linkDocs.filter(l => isAnchor(sourceDoc, l.link_anchor_1 as Doc)); // link docs where 'sourceDoc' is link_anchor_1 const backLinks = linkDocs.filter(l => isAnchor(sourceDoc, l.link_anchor_2 as Doc)); // link docs where 'sourceDoc' is link_anchor_2 const fwdLinkWithoutTargetView = fwdLinks.find(l => !getView(DocCast(l.link_anchor_2))); @@ -68,7 +69,7 @@ export class LinkFollower { ? linkDoc.link_anchor_2 : linkDoc.link_anchor_1 ) as Doc; - const srcAnchor: Doc = LinkManager.getOppositeAnchor(linkDoc, target) ?? sourceDoc; + const srcAnchor: Doc = Doc.getOppositeAnchor(linkDoc, target) ?? sourceDoc; if (target) { const doFollow = (canToggle?: boolean) => { const toggleTarget = canToggle && BoolCast(sourceDoc.followLinkToggle); @@ -87,14 +88,14 @@ export class LinkFollower { zoomTextSelections: BoolCast(srcAnchor.followLinkZoomText), }; if (target.type === DocumentType.PRES) { - const containerDocContext = DocumentManager.GetContextPath(sourceDoc, true); // gather all views that affect layout of sourceDoc so we can revert them after playing the rail - SelectionManager.DeselectAll(); - if (!DocumentManager.Instance.AddViewRenderedCb(target, dv => containerDocContext.length && dv.ComponentView?.playTrail?.(containerDocContext))) { + const containerDocContext = DocumentView.getContextPath(sourceDoc, true); // gather all views that affect layout of sourceDoc so we can revert them after playing the rail + DocumentView.DeselectAll(); + if (!DocumentView.addViewRenderedCb(target, dv => containerDocContext.length && dv.ComponentView?.playTrail?.(containerDocContext))) { PresBox.OpenPresMinimized(target, [0, 0]); } finished?.(); } else { - DocumentManager.Instance.showDocument(target, options, allFinished); + DocumentView.showDocument(target, options, allFinished); } }; let movedTarget = false; @@ -133,10 +134,6 @@ export class LinkFollower { // eslint-disable-next-line prefer-arrow-callback ScriptingGlobals.add(function followLink(doc: Doc, altKey: boolean) { - SelectionManager.DeselectAll(); + DocumentView.DeselectAll(); return LinkFollower.FollowLink(undefined, doc, altKey) ? undefined : { select: true }; }); - -export function IsFollowLinkScript(field: FieldResult<FieldType>) { - return ScriptCast(field)?.script.originalScript.includes('return followLink('); -} diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts index dbd13f978..56d5dce4e 100644 --- a/src/client/util/LinkManager.ts +++ b/src/client/util/LinkManager.ts @@ -30,8 +30,8 @@ export class LinkManager { @observable.shallow userLinkDBs: Doc[] = []; @observable public currentLink: Opt<Doc> = undefined; @observable public currentLinkAnchor: Opt<Doc> = undefined; - public static get Instance() { - return LinkManager._instance; + public static get Instance(): LinkManager { + return Doc.UserDoc() ? LinkManager._instance ?? new LinkManager() : (undefined as any as LinkManager); } public static Links(doc: Doc | undefined) { @@ -47,10 +47,13 @@ export class LinkManager { this.userLinkDBs.push(linkDb); }; public static AutoKeywords = 'keywords:Usages'; - constructor() { + private constructor() { makeObservable(this); LinkManager._instance = this; Doc.AddLink = this.addLink; + Doc.DeleteLink = this.deleteLink; + Doc.Links = LinkManager.Links; + Doc.getOppositeAnchor = LinkManager.getOppositeAnchor; this.createlink_relationshipLists(); // since this is an action, not a reaction, we get only one shot to add this link to the Anchor docs // Thus make sure all promised values are resolved from link -> link.proto -> link.link_anchor_[1,2] -> link.link_anchor_[1,2].proto diff --git a/src/client/util/PingManager.ts b/src/client/util/PingManager.ts index e5e69c5ac..255e9cee0 100644 --- a/src/client/util/PingManager.ts +++ b/src/client/util/PingManager.ts @@ -1,6 +1,6 @@ import { action, makeObservable, observable, runInAction } from 'mobx'; import { Networking } from '../Network'; -import { CurrentUserUtils } from './CurrentUserUtils'; +import { SnappingManager } from './SnappingManager'; export class PingManager { // create static instance and getter for global use @@ -32,7 +32,7 @@ export class PingManager { try { const res = await Networking.PostToServer('/ping', { date: new Date() }); runInAction(() => { - CurrentUserUtils.ServerVersion = res.message; + SnappingManager.SetServerVersion(res.message); }); !this.IsBeating && this.setIsBeating(true); } catch { diff --git a/src/client/util/ReplayMovements.ts b/src/client/util/ReplayMovements.ts index dcc6aabab..530fcf211 100644 --- a/src/client/util/ReplayMovements.ts +++ b/src/client/util/ReplayMovements.ts @@ -4,8 +4,8 @@ import { CollectionDockingView } from '../views/collections/CollectionDockingVie import { CollectionFreeFormView } from '../views/collections/collectionFreeForm'; import { OpenWhereMod } from '../views/nodes/OpenWhere'; import { VideoBox } from '../views/nodes/VideoBox'; -import { DocumentManager } from './DocumentManager'; import { Movement, Presentation } from './TrackMovements'; +import { DocumentView } from '../views/nodes/DocumentView'; export class ReplayMovements { private timers: NodeJS.Timeout[] | null; @@ -106,7 +106,7 @@ export class ReplayMovements { // returns undefined if the docView isn't open on the screen getCollectionFFView = (doc: Doc) => { - const isInView = DocumentManager.Instance.getDocumentView(doc); + const isInView = DocumentView.getDocumentView(doc); return isInView?.ComponentView as CollectionFreeFormView; }; @@ -118,7 +118,7 @@ export class ReplayMovements { } // console.log('openTab', docId, doc); CollectionDockingView.AddSplit(doc, OpenWhereMod.right); - const docView = DocumentManager.Instance.getDocumentView(doc); + const docView = DocumentView.getDocumentView(doc); // BUG - this returns undefined if the doc is already open return docView?.ComponentView as CollectionFreeFormView; }; diff --git a/src/client/util/Scripting.ts b/src/client/util/Scripting.ts index 8df579e75..6948469cc 100644 --- a/src/client/util/Scripting.ts +++ b/src/client/util/Scripting.ts @@ -5,8 +5,8 @@ // import * as typescriptlib from '!!raw-loader!../../../node_modules/typescript/lib/lib.d.ts' // import * as typescriptes5 from '!!raw-loader!../../../node_modules/typescript/lib/lib.es5.d.ts' // eslint-disable-next-line node/no-unpublished-import -import * as ts from 'typescript'; import * as typescriptlib from '!!raw-loader!./type_decls.d'; +import * as ts from 'typescript'; import { Doc, FieldType } from '../../fields/Doc'; import { RefField } from '../../fields/RefField'; import { ScriptField } from '../../fields/ScriptField'; @@ -195,7 +195,6 @@ export function CompileScript(script: string, options: ScriptOptions = {}): Comp if (found) return found as CompiledScript; const { requiredType = '', addReturn = false, params = {}, capturedVariables = {}, typecheck = true } = options; if (options.params && !options.params.this) options.params.this = Doc.name; - if (options.params && !options.params.self) options.params.self = Doc.name; if (options.globals) { ScriptingGlobals.setScriptingGlobals(options.globals); } @@ -266,9 +265,3 @@ export function CompileScript(script: string, options: ScriptOptions = {}): Comp !signature.includes('XXX') && ScriptField._scriptFieldCache.set(script + ':' + signature, result as CompiledScript); return result; } - -ScriptingGlobals.add(CompileScript); -// eslint-disable-next-line prefer-arrow-callback -ScriptingGlobals.add(function runScript(doc: Doc, script: ScriptField) { - return script?.script.run({ this: doc }).result; -}); diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts index 7e8f42de9..0b942116c 100644 --- a/src/client/util/SelectionManager.ts +++ b/src/client/util/SelectionManager.ts @@ -1,6 +1,5 @@ import { action, makeObservable, observable, runInAction } from 'mobx'; import { Doc, Opt } from '../../fields/Doc'; -import { DocViews } from '../../fields/DocSymbols'; import { List } from '../../fields/List'; import { listSpec } from '../../fields/Schema'; import { Cast, DocCast } from '../../fields/Types'; @@ -24,6 +23,13 @@ export class SelectionManager { private constructor() { SelectionManager._manager = this; makeObservable(this); + DocumentView.DeselectAll = SelectionManager.DeselectAll; + DocumentView.DeselectView = SelectionManager.DeselectView; + DocumentView.SelectView = SelectionManager.SelectView; + DocumentView.SelectedDocs = SelectionManager.Docs; + DocumentView.Selected = SelectionManager.Views; + DocumentView.SelectSchemaDoc = SelectionManager.SelectSchemaViewDoc; + DocumentView.SelectedSchemaDoc = () => this.SelectedSchemaDocument; } @action @@ -54,8 +60,10 @@ export class SelectionManager { public static DeselectAll = (except?: Doc): void => { const found = this.Instance.SelectedViews.find(dv => dv.Document === except); runInAction(() => { - LinkManager.Instance.currentLink = undefined; - LinkManager.Instance.currentLinkAnchor = undefined; + if (LinkManager.Instance) { + LinkManager.Instance.currentLink = undefined; + LinkManager.Instance.currentLinkAnchor = undefined; + } this.Instance.SelectedSchemaDocument = undefined; }); this.Instance.SelectedViews.forEach(dv => { @@ -68,19 +76,18 @@ export class SelectionManager { if (found) this.SelectView(found, false); }; - public static IsSelected = (doc?: Doc) => Array.from(doc?.[DocViews] ?? []).some(dv => dv?.IsSelected); - public static get Views() { return this.Instance.SelectedViews; } // prettier-ignore - public static get SelectedSchemaDoc() { return this.Instance.SelectedSchemaDocument; } // prettier-ignore - public static get Docs() { return this.Instance.SelectedViews.map(dv => dv.Document).filter(doc => doc?._type_collection !== CollectionViewType.Docking); } // prettier-ignore + public static Views() { return SelectionManager.Instance.SelectedViews; } // prettier-ignore + public static get SelectedSchemaDoc() { return SelectionManager.Instance.SelectedSchemaDocument; } // prettier-ignore + public static Docs() { return SelectionManager.Instance.SelectedViews.map(dv => dv.Document).filter(doc => doc?._type_collection !== CollectionViewType.Docking); } // prettier-ignore } // eslint-disable-next-line prefer-arrow-callback ScriptingGlobals.add(function SelectedDocType(type: string, expertMode: boolean, checkContext?: boolean) { if (Doc.noviceMode && expertMode) return false; if (type === 'tab') { - return SelectionManager.Views.lastElement()?._props.renderDepth === 0; + return DocumentView.Selected().lastElement()?._props.renderDepth === 0; } - const selected = (sel => (checkContext ? DocCast(sel?.embedContainer) : sel))(SelectionManager.SelectedSchemaDoc ?? SelectionManager.Docs.lastElement()); + const selected = (sel => (checkContext ? DocCast(sel?.embedContainer) : sel))(DocumentView.SelectedSchemaDoc() ?? SelectionManager.Docs().lastElement()); return selected?.type === type || selected?.type_collection === type || !type; }); // eslint-disable-next-line prefer-arrow-callback @@ -109,8 +116,6 @@ ScriptingGlobals.add(function redo() { }); // eslint-disable-next-line prefer-arrow-callback ScriptingGlobals.add(function selectedDocs(container: Doc, excludeCollections: boolean, prevValue: any) { - const docs = SelectionManager.Views.map(dv => dv.Document).filter( - d => !Doc.AreProtosEqual(d, container) && !d.annotationOn && d.type !== DocumentType.KVP && (!excludeCollections || d.type !== DocumentType.COL || !Cast(d.data, listSpec(Doc), null)) - ); + const docs = SelectionManager.Docs().filter(d => !Doc.AreProtosEqual(d, container) && !d.annotationOn && d.type !== DocumentType.KVP && (!excludeCollections || d.type !== DocumentType.COL || !Cast(d.data, listSpec(Doc), null))); return docs.length ? new List(docs) : prevValue; }); diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx index 8cb97fe50..d3c10f9f4 100644 --- a/src/client/util/SettingsManager.tsx +++ b/src/client/util/SettingsManager.tsx @@ -8,7 +8,7 @@ import * as React from 'react'; import { BsGoogle } from 'react-icons/bs'; import { FaFillDrip, FaPalette } from 'react-icons/fa'; import { ClientUtils, addStyleSheet, addStyleSheetRule } from '../../ClientUtils'; -import { Doc, Opt } from '../../fields/Doc'; +import { Doc } from '../../fields/Doc'; import { DashVersion } from '../../fields/DocSymbols'; import { BoolCast, Cast, NumCast, StrCast } from '../../fields/Types'; import { DocServer } from '../DocServer'; @@ -17,7 +17,7 @@ import { GoogleAuthenticationManager } from '../apis/GoogleAuthenticationManager import { MainViewModal } from '../views/MainViewModal'; import { GroupManager } from './GroupManager'; import './SettingsManager.scss'; -import { SnappingManager } from './SnappingManager'; +import { SnappingManager, freeformScrollMode } from './SnappingManager'; import { undoable } from './UndoManager'; export enum ColorScheme { @@ -28,11 +28,6 @@ export enum ColorScheme { Cupcake = 'Cupcake', } -export enum freeformScrollMode { - Pan = 'pan', - Zoom = 'zoom', -} - @observer export class SettingsManager extends React.Component<{}> { // eslint-disable-next-line no-use-before-define @@ -44,17 +39,15 @@ export class SettingsManager extends React.Component<{}> { @observable private _curr_password = ''; @observable private _new_password = ''; @observable private _new_confirm = ''; - @observable private _lastPressedSidebarBtn: Opt<Doc> = undefined; // bcz: this is a hack to handle highlighting buttons in the leftpanel menu .. need to find a cleaner approach @observable private _activeTab = 'Accounts'; @observable private _isOpen = false; - @observable public propertiesWidth: number = 0; - private googleAuthorize = action(() => GoogleAuthenticationManager.Instance.fetchOrGenerateAccessToken(true)); public closeMgr = action(() => { this._isOpen = false; }); + // eslint-disable-next-line react/no-unused-class-component-methods public openMgr = action(() => { this._isOpen = true; }); @@ -154,9 +147,6 @@ export class SettingsManager extends React.Component<{}> { SnappingManager.SettingsStyle = SettingsManager._settingsStyle; } - public get LastPressedBtn() { return this._lastPressedSidebarBtn; } // prettier-ignore - public set LastPressedBtn(state:Doc|undefined) { this._lastPressedSidebarBtn = state; } // prettier-ignore - @computed public static get userColor() { return StrCast(Doc.UserDoc().userColor); } @@ -287,7 +277,7 @@ export class SettingsManager extends React.Component<{}> { size={Size.XSMALL} color={SettingsManager.userColor} /> - <Toggle + {/* <Toggle formLabel="Show Link Lines" formLabelPlacement="right" toggleType={ToggleType.SWITCH} @@ -297,7 +287,7 @@ export class SettingsManager extends React.Component<{}> { toggleStatus={BoolCast(Doc.UserDoc().showLinkLines)} size={Size.XSMALL} color={SettingsManager.userColor} - /> + /> */} <Group formLabel="Title Height"> <NumberDropdown number={NumCast(Doc.UserDoc().headerHeight, 30)} diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx index b0140b73c..31a99896f 100644 --- a/src/client/util/SharingManager.tsx +++ b/src/client/util/SharingManager.tsx @@ -21,11 +21,9 @@ import { DocServer } from '../DocServer'; import { MainViewModal } from '../views/MainViewModal'; import { DocumentView } from '../views/nodes/DocumentView'; import { TaskCompletionBox } from '../views/nodes/TaskCompletedBox'; -import { DocumentManager } from './DocumentManager'; import { GroupManager, UserOptions } from './GroupManager'; import { GroupMemberView } from './GroupMemberView'; import { SearchUtil } from './SearchUtil'; -import { SelectionManager } from './SelectionManager'; import './SharingManager.scss'; import { SnappingManager } from './SnappingManager'; import { undoable } from './UndoManager'; @@ -152,7 +150,7 @@ export class SharingManager extends React.Component<{}> { const users = this.individualSort === 'ascending' ? this.users.slice().sort(this.sortUsers) : this.individualSort === 'descending' ? this.users.slice().sort(this.sortUsers).reverse() : this.users; const groups = this.groupSort === 'ascending' ? groupList.slice().sort(this.sortGroups) : this.groupSort === 'descending' ? groupList.slice().sort(this.sortGroups).reverse() : groupList; - let docs = SelectionManager.Views.length < 2 ? [this.targetDoc] : SelectionManager.Views.map(docView => docView.Document); + let docs = DocumentView.Selected().length < 2 ? [this.targetDoc] : DocumentView.Selected().map(docView => docView.Document); if (this.myDocAcls) { const newDocs: Doc[] = []; @@ -442,7 +440,7 @@ export class SharingManager extends React.Component<{}> { const { user, sharingDoc } = recipient; const target = targetDoc || this.targetDoc!; const acl = `acl_${normalizeEmail(user.email)}`; - const docs = SelectionManager.Views.length < 2 ? [target] : SelectionManager.Views.map(docView => docView.Document); + const docs = DocumentView.Selected().length < 2 ? [target] : DocumentView.Selected().map(docView => docView.Document); docs.map(doc => (this.layoutDocAcls || doc.dockingConfig ? doc : Doc.GetProto(doc))).forEach(doc => { distributeAcls(acl, permission as SharingPermissions, doc, undefined, this.upgradeNested ? true : undefined); if (permission !== SharingPermissions.None) { @@ -460,7 +458,7 @@ export class SharingManager extends React.Component<{}> { const target = targetDoc || this.targetDoc!; const acl = `acl_${normalizeEmail(StrCast(group.title))}`; - const docs = SelectionManager.Views.length < 2 ? [target] : SelectionManager.Views.map(docView => docView.Document); + const docs = DocumentView.Selected().length < 2 ? [target] : DocumentView.Selected().map(docView => docView.Document); docs.map(doc => (this.layoutDocAcls || doc.dockingConfig ? doc : Doc.GetProto(doc))).forEach(doc => { distributeAcls(acl, permission as SharingPermissions, doc, undefined, this.upgradeNested ? true : undefined); @@ -643,14 +641,14 @@ export class SharingManager extends React.Component<{}> { private focusOn = (contents: string) => { const title = this.targetDoc ? StrCast(this.targetDoc.title) : ''; - const docs = SelectionManager.Views.length > 1 ? SelectionManager.Views.map(docView => docView.props.Document) : [this.targetDoc]; + const docs = DocumentView.Selected().length > 1 ? DocumentView.Selected().map(docView => docView.props.Document) : [this.targetDoc]; return ( <span className="focus-span" title={title} onClick={() => { if (this.targetDoc && this.targetDocView && docs.length === 1) { - DocumentManager.Instance.showDocument(this.targetDoc, { willZoomCentered: true }); + DocumentView.showDocument(this.targetDoc, { willZoomCentered: true }); } }} onPointerEnter={action(() => { diff --git a/src/client/util/SnappingManager.ts b/src/client/util/SnappingManager.ts index 5cd6ecfe1..6789c2ab8 100644 --- a/src/client/util/SnappingManager.ts +++ b/src/client/util/SnappingManager.ts @@ -1,5 +1,9 @@ import { observable, action, runInAction, makeObservable } from 'mobx'; +export enum freeformScrollMode { + Pan = 'pan', + Zoom = 'zoom', +} export class SnappingManager { // eslint-disable-next-line no-use-before-define private static _manager: SnappingManager; @@ -19,6 +23,9 @@ export class SnappingManager { @observable _vertSnapLines: number[] = []; @observable _exploreMode = false; @observable _userPanned = false; + @observable _serverVersion: string = ''; + @observable _lastBtnId: string = ''; + @observable _propertyWid: number = 0; private constructor() { SnappingManager._manager = this; @@ -45,6 +52,10 @@ export class SnappingManager { public static get CanEmbed() { return this.Instance._canEmbed; } // prettier-ignore public static get ExploreMode() { return this.Instance._exploreMode; } // prettier-ignore public static get UserPanned() { return this.Instance._userPanned; } // prettier-ignore + public static get ServerVersion() { return this.Instance._serverVersion; } // prettier-ignore + public static get LastPressedBtn() { return this.Instance._lastBtnId; } // prettier-ignore + public static get PropertiesWidth(){ return this.Instance._propertyWid; } // prettier-ignore + public static SetShiftKey = (down: boolean) => runInAction(() => {this.Instance._shiftKey = down}); // prettier-ignore public static SetCtrlKey = (down: boolean) => runInAction(() => {this.Instance._ctrlKey = down}); // prettier-ignore public static SetMetaKey = (down: boolean) => runInAction(() => {this.Instance._metaKey = down}); // prettier-ignore @@ -54,7 +65,10 @@ export class SnappingManager { public static SetIsResizing = (docid?:string) => runInAction(() => {this.Instance._isResizing = docid}); // prettier-ignore public static SetCanEmbed = (embed:boolean) => runInAction(() => {this.Instance._canEmbed = embed}); // prettier-ignore public static SetExploreMode = (state:boolean) => runInAction(() => {this.Instance._exploreMode = state}); // prettier-ignore - public static TriggerUserPanned = () => runInAction(() => {this.Instance._userPanned = !this.Instance._userPanned}); // prettier-ignore + public static TriggerUserPanned = () => runInAction(() => {this.Instance._userPanned = !this.Instance._userPanned}); // prettier-ignore + public static SetServerVersion = (version:string) =>runInAction(() => {this.Instance._serverVersion = version}); // prettier-ignore + public static SetLastPressedBtn = (id:string) =>runInAction(() => {this.Instance._lastBtnId = id}); // prettier-ignore + public static SetPropertiesWidth= (wid:number) =>runInAction(() => {this.Instance._propertyWid = wid}); // prettier-ignore public static userColor: string | undefined; public static userVariantColor: string | undefined; diff --git a/src/client/views/AntimodeMenu.tsx b/src/client/views/AntimodeMenu.tsx index b1eb730fa..303672d90 100644 --- a/src/client/views/AntimodeMenu.tsx +++ b/src/client/views/AntimodeMenu.tsx @@ -1,6 +1,6 @@ import { action, makeObservable, observable, runInAction } from 'mobx'; import * as React from 'react'; -import { SettingsManager } from '../util/SettingsManager'; +import { SnappingManager } from '../util/SnappingManager'; import './AntimodeMenu.scss'; import { ObservableReactComponent } from './ObservableReactComponent'; @@ -157,7 +157,7 @@ export abstract class AntimodeMenu<T extends AntimodeMenuProps> extends Observab left: this._left, top: this._top, opacity: this._opacity, - background: SettingsManager.userBackgroundColor, + background: SnappingManager.userBackgroundColor, transitionProperty: this._transitionProperty, transitionDuration: this._transitionDuration, transitionDelay: this._transitionDelay, @@ -183,7 +183,7 @@ export abstract class AntimodeMenu<T extends AntimodeMenuProps> extends Observab height: 'inherit', width: 200, opacity: this._opacity, - background: SettingsManager.userBackgroundColor, + background: SnappingManager.userBackgroundColor, transitionProperty: this._transitionProperty, transitionDuration: this._transitionDuration, transitionDelay: this._transitionDelay, @@ -206,7 +206,7 @@ export abstract class AntimodeMenu<T extends AntimodeMenuProps> extends Observab left: this._left, top: this._top, opacity: this._opacity, - background: SettingsManager.userBackgroundColor, + background: SnappingManager.userBackgroundColor, transitionProperty: this._transitionProperty, transitionDuration: this._transitionDuration, transitionDelay: this._transitionDelay, diff --git a/src/client/views/ComponentDecorations.tsx b/src/client/views/ComponentDecorations.tsx index 64b8a8446..929b549e0 100644 --- a/src/client/views/ComponentDecorations.tsx +++ b/src/client/views/ComponentDecorations.tsx @@ -1,7 +1,7 @@ import { observer } from 'mobx-react'; import * as React from 'react'; -import { SelectionManager } from '../util/SelectionManager'; import './ComponentDecorations.scss'; +import { DocumentView } from './nodes/DocumentView'; @observer export class ComponentDecorations extends React.Component<{ boundsTop: number; boundsLeft: number }, { value: string }> { @@ -9,7 +9,7 @@ export class ComponentDecorations extends React.Component<{ boundsTop: number; b static Instance: ComponentDecorations; render() { - const seldoc = SelectionManager.Views.lastElement(); + const seldoc = DocumentView.Selected().lastElement(); return seldoc?.ComponentView?.componentUI?.(this.props.boundsLeft, this.props.boundsTop) ?? null; } } diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx index a0e3d2ddd..97ff346e4 100644 --- a/src/client/views/DocComponent.tsx +++ b/src/client/views/DocComponent.tsx @@ -60,6 +60,8 @@ export interface ViewBoxInterface { ptFromScreen?: (pt: { X: number; Y: number }) => { X: number; Y: number }; snapPt?: (pt: { X: number; Y: number }, excludeSegs?: number[]) => { nearestPt: { X: number; Y: number }; distance: number }; search?: (str: string, bwd?: boolean, clear?: boolean) => boolean; + dontRegisterView?: () => boolean; // KeyValueBox's don't want to register their views + isUnstyledView?: () => boolean; // SchemaView and KeyValue are untyled -- not tiles, no opacity } /** * DocComponent returns a React base class used by Doc views with accessors for unpacking the Document,layoutDoc, and dataDoc's diff --git a/src/client/views/DocViewUtils.ts b/src/client/views/DocViewUtils.ts new file mode 100644 index 000000000..1f5f29c7e --- /dev/null +++ b/src/client/views/DocViewUtils.ts @@ -0,0 +1,21 @@ +/* eslint-disable prefer-destructuring */ +/* eslint-disable default-param-last */ +/* eslint-disable no-use-before-define */ +import { runInAction } from 'mobx'; +import { Doc, SetActiveAudioLinker } from '../../fields/Doc'; +import { DocUtils } from '../documents/DocUtils'; +import { FieldViewProps } from './nodes/FieldView'; + +export namespace DocViewUtils { + export const ActiveRecordings: { props: FieldViewProps; getAnchor: (addAsAnnotation: boolean) => Doc }[] = []; + + export function MakeLinkToActiveAudio(getSourceDoc: () => Doc | undefined, broadcastEvent = true) { + broadcastEvent && runInAction(() => { Doc.RecordingEvent += 1; }); // prettier-ignore + return ActiveRecordings.map(audio => { + const sourceDoc = getSourceDoc(); + return sourceDoc && DocUtils.MakeLink(sourceDoc, audio.getAnchor(true) || audio.props.Document, { link_relationship: 'recording annotation:linked recording', link_description: 'recording timeline' }); + }); + } + + SetActiveAudioLinker(MakeLinkToActiveAudio); +} diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx index dd744f272..487868169 100644 --- a/src/client/views/DocumentButtonBar.tsx +++ b/src/client/views/DocumentButtonBar.tsx @@ -14,23 +14,21 @@ import { returnFalse, returnTrue, setupMoveUpEvents, simulateMouseClick } from ' import { emptyFunction } from '../../Utils'; import { Doc } from '../../fields/Doc'; import { Cast, DocCast } from '../../fields/Types'; -import { DocUtils } from '../documents/DocUtils'; +import { DocUtils, IsFollowLinkScript } from '../documents/DocUtils'; import { CalendarManager } from '../util/CalendarManager'; +import { DictationManager } from '../util/DictationManager'; import { DragManager } from '../util/DragManager'; import { dropActionType } from '../util/DropActionTypes'; -import { IsFollowLinkScript } from '../util/LinkFollower'; -import { SelectionManager } from '../util/SelectionManager'; import { SharingManager } from '../util/SharingManager'; import { UndoManager, undoBatch } from '../util/UndoManager'; import './DocumentButtonBar.scss'; import { ObservableReactComponent } from './ObservableReactComponent'; import { PinProps } from './PinFuncs'; import { TemplateMenu } from './TemplateMenu'; -import { TabDocView } from './collections/TabDocView'; import { Colors } from './global/globalEnums'; import { LinkPopup } from './linking/LinkPopup'; import { DocumentLinksButton } from './nodes/DocumentLinksButton'; -import { DocumentView, DocumentViewInternal } from './nodes/DocumentView'; +import { DocumentView } from './nodes/DocumentView'; import { OpenWhere } from './nodes/OpenWhere'; import { DashFieldView } from './nodes/formattedText/DashFieldView'; @@ -206,7 +204,7 @@ export class DocumentButtonBar extends ObservableReactComponent<{ views: () => ( .views() .filter(v => v) .map(dv => dv!.Document); - TabDocView.PinDoc(docs, { + DocumentView.PinDoc(docs, { pinAudioPlay: true, pinDocLayout: pinLayoutView, pinData: { dataview: pinContentView }, @@ -221,7 +219,7 @@ export class DocumentButtonBar extends ObservableReactComponent<{ views: () => ( ); }; return !targetDoc ? null : ( - <Tooltip title={<div className="dash-tooltip">{`Pin Document ${SelectionManager.Views.length > 1 ? 'multiple documents' : ''} to Trail`}</div>}> + <Tooltip title={<div className="dash-tooltip">{`Pin Document ${DocumentView.Selected().length > 1 ? 'multiple documents' : ''} to Trail`}</div>}> <div className="documentButtonBar-icon documentButtonBar-pin" onClick={e => { @@ -229,7 +227,7 @@ export class DocumentButtonBar extends ObservableReactComponent<{ views: () => ( .views() .filter(v => v) .map(dv => dv!.Document); - TabDocView.PinDoc(docs, { pinAudioPlay: true, pinDocLayout: e.shiftKey, pinData: { dataview: e.altKey }, activeFrame: Cast(docs.lastElement()?.activeFrame, 'number', null) }); + DocumentView.PinDoc(docs, { pinAudioPlay: true, pinDocLayout: e.shiftKey, pinData: { dataview: e.altKey }, activeFrame: Cast(docs.lastElement()?.activeFrame, 'number', null) }); e.stopPropagation(); }}> <div className="documentButtonBar-pinTypes"> @@ -299,7 +297,7 @@ export class DocumentButtonBar extends ObservableReactComponent<{ views: () => ( this._props.views().map( view => view && - DocumentViewInternal.recordAudioAnnotation( + DictationManager.recordAudioAnnotation( view.dataDoc, view.LayoutFieldKey, stopFunc => { @@ -381,7 +379,7 @@ export class DocumentButtonBar extends ObservableReactComponent<{ views: () => ( } openContextMenu = (e: PointerEvent) => { - let child = SelectionManager.Views[0].ContentDiv!.children[0]; + let child = DocumentView.Selected()[0].ContentDiv!.children[0]; while (child.children.length) { const next = Array.from(child.children).find(c => c.className?.toString().includes('SVGAnimatedString') || typeof c.className === 'string'); if (next?.className?.toString().includes(DocumentView.ROOT_DIV)) break; @@ -442,14 +440,7 @@ export class DocumentButtonBar extends ObservableReactComponent<{ views: () => ( </div> {this._showLinkPopup ? ( <div style={{ position: 'absolute', zIndex: 1000 }}> - <LinkPopup - key="popup" - linkCreated={link => { - link.link_displayLine = !IsFollowLinkScript(this._props.views().lastElement()?.Document.onClick); - }} - linkCreateAnchor={() => this._props.views().lastElement()?.ComponentView?.getAnchor?.(true)} - linkFrom={() => this._props.views().lastElement()?.Document} - /> + <LinkPopup key="popup" linkCreated={emptyFunction} linkCreateAnchor={() => this._props.views().lastElement()?.ComponentView?.getAnchor?.(true)} linkFrom={() => this._props.views().lastElement()?.Document} /> </div> ) : ( <div className="documentButtonBar-button">{this.linkButton}</div> @@ -457,7 +448,7 @@ export class DocumentButtonBar extends ObservableReactComponent<{ views: () => ( {DocumentLinksButton.StartLink && DocumentLinksButton.StartLink !== doc ? <div className="documentButtonBar-button">{this.endLinkButton} </div> : null} <div className="documentButtonBar-button">{this.templateButton}</div> - {!SelectionManager.Views?.some(v => v.allLinks.length) ? null : <div className="documentButtonBar-button">{this.followLinkButton}</div>} + {!DocumentView.Selected().some(v => v.allLinks.length) ? null : <div className="documentButtonBar-button">{this.followLinkButton}</div>} <div className="documentButtonBar-button">{this.pinButton}</div> <div className="documentButtonBar-button">{this.recordButton}</div> <div className="documentButtonBar-button">{this.calendarButton}</div> diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index ef493fb69..432b02782 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -17,9 +17,7 @@ import { BoolCast, Cast, DocCast, NumCast, StrCast } from '../../fields/Types'; import { GetEffectiveAcl } from '../../fields/util'; import { DocumentType } from '../documents/DocumentTypes'; import { Docs } from '../documents/Documents'; -import { DocumentManager } from '../util/DocumentManager'; import { DragManager } from '../util/DragManager'; -import { SelectionManager } from '../util/SelectionManager'; import { SettingsManager } from '../util/SettingsManager'; import { SnappingManager } from '../util/SnappingManager'; import { UndoManager } from '../util/UndoManager'; @@ -81,14 +79,14 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora let inputting = false; if (this._titleControlString.startsWith('$')) { const titleFieldKey = this._titleControlString.substring(1); - if (SelectionManager.Views[0]?.Document[titleFieldKey] !== this._accumulatedTitle) { + if (DocumentView.Selected()[0]?.Document[titleFieldKey] !== this._accumulatedTitle) { inputting = true; } } const center = {x: (this.Bounds.x+this.Bounds.r)/2, y: (this.Bounds.y+this.Bounds.b)/2}; const {x,y} = Utils.rotPt(e.clientX - center.x, e.clientY - center.y, - NumCast(SelectionManager.Views.lastElement()?.screenToViewTransform().Rotate)); + NumCast(DocumentView.Selected().lastElement()?.screenToViewTransform().Rotate)); (this._showNothing = !inputting && !DocumentButtonBar.Instance?._tooltipOpen && !(this.Bounds.x !== Number.MAX_VALUE && // (this.Bounds.x > center.x+x || this.Bounds.r < center.x+x || this.Bounds.y > center.y+y || this.Bounds.b < center.y+y ))); @@ -110,7 +108,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora @computed get Bounds() { return (SnappingManager.IsLinkFollowing || SnappingManager.ExploreMode) ? { x: 0, y: 0, r: 0, b: 0 } - : SelectionManager.Views + : DocumentView.Selected() .filter(dv => dv._props.renderDepth > 0) .map(dv => dv.getBounds) .reduce((bounds, rect) => !rect ? bounds @@ -127,7 +125,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora this._titleControlString = this._accumulatedTitle; } else if (this._titleControlString.startsWith('$')) { if (this._accumulatedTitle.startsWith('-->#')) { - SelectionManager.Docs.forEach(doc => { + DocumentView.SelectedDocs().forEach(doc => { doc[DocData].onViewMounted = ScriptField.MakeScript(`updateTagsCollection(this)`); }); } @@ -135,11 +133,11 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora UndoManager.RunInBatch( () => titleFieldKey && - SelectionManager.Views.forEach(d => { + DocumentView.Selected().forEach(dv => { if (titleFieldKey === 'title') { - d.dataDoc.title_custom = !this._accumulatedTitle.startsWith('-'); + dv.dataDoc.title_custom = !this._accumulatedTitle.startsWith('-'); } - KeyValueBox.SetField(d.Document, titleFieldKey, this._accumulatedTitle); + KeyValueBox.SetField(dv.Document, titleFieldKey, this._accumulatedTitle); }), 'edit title' ); @@ -154,7 +152,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora }; onContainerDown = (e: React.PointerEvent) => { - const effectiveLayoutAcl = GetEffectiveAcl(SelectionManager.Views[0].Document); + const effectiveLayoutAcl = GetEffectiveAcl(DocumentView.Selected()[0].Document); if (effectiveLayoutAcl === AclAdmin || effectiveLayoutAcl === AclEdit || effectiveLayoutAcl === AclAugment) { setupMoveUpEvents(this, e, moveEv => this.onBackgroundMove(true, moveEv), emptyFunction, emptyFunction); e.stopPropagation(); @@ -162,7 +160,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora }; onTitleDown = (e: React.PointerEvent) => { - const effectiveLayoutAcl = GetEffectiveAcl(SelectionManager.Views[0].Document); + const effectiveLayoutAcl = GetEffectiveAcl(DocumentView.SelectedDocs()[0]); if (effectiveLayoutAcl === AclAdmin || effectiveLayoutAcl === AclEdit || effectiveLayoutAcl === AclAugment) { setupMoveUpEvents( this, @@ -170,7 +168,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora moveEv => this.onBackgroundMove(true, moveEv), emptyFunction, action(() => { - const selected = SelectionManager.Views.length === 1 ? SelectionManager.Docs[0] : undefined; + const selected = DocumentView.SelectedDocs().length === 1 ? DocumentView.SelectedDocs()[0] : undefined; !this._editingTitle && (this._accumulatedTitle = this._titleControlString.startsWith('$') ? (selected && Field.toKeyValueString(selected, this._titleControlString.substring(1))) || '-unset-' : this._titleControlString); this._editingTitle = true; this._keyinput.current && setTimeout(this._keyinput.current.focus); @@ -186,19 +184,16 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora }; @action onBackgroundMove = (dragTitle: boolean, e: PointerEvent): boolean => { - const dragDocView = SelectionManager.Views[0]; + const dragDocView = DocumentView.Selected()[0]; const effectiveLayoutAcl = GetEffectiveAcl(dragDocView.Document); if (effectiveLayoutAcl !== AclAdmin && effectiveLayoutAcl !== AclEdit && effectiveLayoutAcl !== AclAugment) { return false; } const containers = new Set<Doc | undefined>(); - SelectionManager.Views.forEach(v => containers.add(DocCast(v.Document.embedContainer))); + DocumentView.Selected().forEach(v => containers.add(DocCast(v.Document.embedContainer))); if (containers.size > 1) return false; const { left, top } = dragDocView.getBounds || { left: 0, top: 0 }; - const dragData = new DragManager.DocumentDragData( - SelectionManager.Views.map(dv => dv.Document), - dragDocView._props.dropAction - ); + const dragData = new DragManager.DocumentDragData(DocumentView.SelectedDocs(), dragDocView._props.dropAction); dragData.offset = dragDocView.screenToContentsTransform().transformDirection(e.x - left, e.y - top); dragData.moveDocument = dragDocView._props.moveDocument; dragData.removeDocument = dragDocView._props.removeDocument; @@ -206,7 +201,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora dragData.canEmbed = dragTitle; this._hidden = true; DragManager.StartDocumentDrag( - SelectionManager.Views.map(dv => dv.ContentDiv!), + DocumentView.Selected().map(dv => dv.ContentDiv!), dragData, e.x, e.y, @@ -223,7 +218,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora _deleteAfterIconify = false; _iconifyBatch: UndoManager.Batch | undefined; onCloseClick = (forceDeleteOrIconify: boolean | undefined) => { - const views = SelectionManager.Views.filter(v => v && v._props.renderDepth > 0); + const views = DocumentView.Selected().filter(v => v && v._props.renderDepth > 0); if (forceDeleteOrIconify === false && this._iconifyBatch) return; this._deleteAfterIconify = !!(forceDeleteOrIconify || this._iconifyBatch); let iconifyingCount = views.length; @@ -239,7 +234,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora iconView._props.removeDocument?.(iconView.Document); } }); - views.forEach(SelectionManager.DeselectView); + views.forEach(DocumentView.DeselectView); } this._iconifyBatch?.end(); this._iconifyBatch = undefined; @@ -258,53 +253,49 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora }; onMaximizeDown = (e: React.PointerEvent) => { - setupMoveUpEvents(this, e, () => DragManager.StartWindowDrag?.(e, [SelectionManager.Views.lastElement().Document]) ?? false, emptyFunction, this.onMaximizeClick, false, false); + setupMoveUpEvents(this, e, () => DragManager.StartWindowDrag?.(e, [DocumentView.SelectedDocs().lastElement()]) ?? false, emptyFunction, this.onMaximizeClick, false, false); e.stopPropagation(); }; onMaximizeClick = (e: any): void => { - const selectedDocs = SelectionManager.Views; + const selectedDocs = DocumentView.SelectedDocs(); if (selectedDocs.length) { if (e.ctrlKey) { // open an embedding in a new tab with Ctrl Key - CollectionDockingView.AddSplit(Doc.BestEmbedding(selectedDocs[0].Document), OpenWhereMod.right); + CollectionDockingView.AddSplit(Doc.BestEmbedding(selectedDocs[0]), OpenWhereMod.right); } else if (e.shiftKey) { // open centered in a new workspace with Shift Key - const embedding = Doc.MakeEmbedding(selectedDocs[0].Document); + const embedding = Doc.MakeEmbedding(selectedDocs[0]); embedding.embedContainer = undefined; embedding.x = -NumCast(embedding._width) / 2; embedding.y = -NumCast(embedding._height) / 2; CollectionDockingView.AddSplit(Docs.Create.FreeformDocument([embedding], { title: 'Tab for ' + embedding.title }), OpenWhereMod.right); } else if (e.altKey) { // open same document in new tab - CollectionDockingView.ToggleSplit(selectedDocs[0].Document, OpenWhereMod.right); + CollectionDockingView.ToggleSplit(selectedDocs[0], OpenWhereMod.right); } else { - let openDoc = selectedDocs[0].Document; + let openDoc = selectedDocs[0]; if (openDoc.layout_fieldKey === 'layout_icon') { openDoc = Doc.GetEmbeddings(openDoc).find(embedding => !embedding.embedContainer) ?? Doc.MakeEmbedding(openDoc); Doc.deiconifyView(openDoc); } - LightboxView.Instance.SetLightboxDoc( - openDoc, - undefined, - selectedDocs.slice(1).map(view => view.Document) - ); + LightboxView.Instance.SetLightboxDoc(openDoc, undefined, selectedDocs.slice(1)); } } - SelectionManager.DeselectAll(); + DocumentView.DeselectAll(); }; onIconifyClick = (): void => { - SelectionManager.Views.forEach(dv => dv?.iconify()); - SelectionManager.DeselectAll(); + DocumentView.Selected().forEach(dv => dv?.iconify()); + DocumentView.DeselectAll(); }; - onSelectContainerDocClick = () => SelectionManager.Views?.[0]?.containerViewPath?.().lastElement()?.select(false); + onSelectContainerDocClick = () => DocumentView.Selected()?.[0]?.containerViewPath?.().lastElement()?.select(false); /** * sets up events when user clicks on the border radius editor */ @action onRadiusDown = (e: React.PointerEvent): void => { - SnappingManager.SetIsResizing(SelectionManager.Docs.lastElement()?.[Id]); + SnappingManager.SetIsResizing(DocumentView.SelectedDocs().lastElement()?.[Id]); this._isRounding = true; this._resizeUndo = UndoManager.StartBatch('DocDecs set radius'); setupMoveUpEvents( @@ -314,7 +305,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora const [x, y] = [this.Bounds.x + 3, this.Bounds.y + 3]; const maxDist = Math.min((this.Bounds.r - this.Bounds.x) / 2, (this.Bounds.b - this.Bounds.y) / 2); const dist = moveEv.clientX < x && moveEv.clientY < y ? 0 : Math.sqrt((moveEv.clientX - x) * (moveEv.clientX - x) + (moveEv.clientY - y) * (moveEv.clientY - y)); - SelectionManager.Docs.forEach(doc => { + DocumentView.SelectedDocs().forEach(doc => { const docMax = Math.min(NumCast(doc.width) / 2, NumCast(doc.height) / 2); const radius = Math.min(1, dist / maxDist) * docMax; // set radius based on ratio of drag distance to half diagonal distance of bounding box doc._layout_borderRounding = `${radius}px`; @@ -339,7 +330,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora e, returnFalse, // don't care about move or up event, emptyFunction, // just care about whether we get a click event - () => UndoManager.RunInBatch(() => SelectionManager.Docs.forEach(doc => Doc.toggleLockedPosition(doc)), 'toggleBackground') + () => UndoManager.RunInBatch(() => DocumentView.Selected().forEach(dv => Doc.toggleLockedPosition(dv.Document)), 'toggleBackground') ); e.stopPropagation(); }; @@ -356,7 +347,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora @action onRotateCenterDown = (e: React.PointerEvent): void => { this._isRotating = true; - const seldocview = SelectionManager.Views[0]; + const seldocview = DocumentView.Selected()[0]; setupMoveUpEvents( this, e, @@ -374,11 +365,11 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora this._isRotating = true; const rcScreen = { X: this.rotCenter[0], Y: this.rotCenter[1] }; const rotateUndo = UndoManager.StartBatch('drag rotation'); - const selectedInk = SelectionManager.Views.filter(i => i.ComponentView instanceof InkingStroke); + const selectedInk = DocumentView.Selected().filter(i => i.ComponentView instanceof InkingStroke); const centerPoint = this.rotCenter.slice(); const infos = new Map<Doc, { unrotatedDocPos: { x: number; y: number }; startRotCtr: { x: number; y: number }; accumRot: number }>(); - const seldocview = SelectionManager.Views[0]; - SelectionManager.Views.forEach(dv => { + const seldocview = DocumentView.Selected()[0]; + DocumentView.Selected().forEach(dv => { const accumRot = (NumCast(dv.Document._rotation) / 180) * Math.PI; const localRotCtr = dv.screenToViewTransform().transformPoint(rcScreen.X, rcScreen.Y); const localRotCtrOffset = [localRotCtr[0] - NumCast(dv.Document.width) / 2, localRotCtr[1] - NumCast(dv.Document.height) / 2]; @@ -387,7 +378,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora infos.set(dv.Document, { unrotatedDocPos, startRotCtr, accumRot }); }); const infoRot = (angle: number, isAbs = false) => { - SelectionManager.Views.forEach( + DocumentView.Selected().forEach( action(dv => { const { unrotatedDocPos, startRotCtr, accumRot } = infos.get(dv.Document)!; const endRotCtr = Utils.rotPt(startRotCtr.x, startRotCtr.y, isAbs ? angle : accumRot + angle); @@ -437,7 +428,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora @action onPointerDown = (e: React.PointerEvent): void => { - SnappingManager.SetIsResizing(SelectionManager.Docs.lastElement()?.[Id]); // turns off pointer events on things like youtube videos and web pages so that dragging doesn't get "stuck" when cursor moves over them + SnappingManager.SetIsResizing(DocumentView.Selected().lastElement()?.Document[Id]); // turns off pointer events on things like youtube videos and web pages so that dragging doesn't get "stuck" when cursor moves over them setupMoveUpEvents(this, e, this.onPointerMove, this.onPointerUp, emptyFunction); e.stopPropagation(); const id = (this._resizeHdlId = e.currentTarget.className); @@ -449,7 +440,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora }; this._resizeUndo = UndoManager.StartBatch('drag resizing'); this._snapPt = { x: e.pageX, y: e.pageY }; - SelectionManager.Views.forEach(docView => CollectionFreeFormView.from(docView)?.dragStarting(false, false)); + DocumentView.Selected().forEach(docView => CollectionFreeFormView.from(docView)?.dragStarting(false, false)); }; projectDragToAspect = (e: PointerEvent, docView: DocumentView, fixedAspect: number) => { @@ -467,7 +458,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora return project([e.clientX + this._offset.x, e.clientY + this._offset.y], tl, [tl[0] + fixedAspect, tl[1] + 1]); }; onPointerMove = (e: PointerEvent): boolean => { - const first = SelectionManager.Views[0]; + const first = DocumentView.Selected()[0]; const effectiveAcl = GetEffectiveAcl(first.Document); if (!(effectiveAcl === AclAdmin || effectiveAcl === AclEdit || effectiveAcl === AclAugment)) return false; if (!first) return false; @@ -484,10 +475,10 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora !this._interactionLock && runInAction(async () => { // resize selected docs if we're not in the middle of a resize (ie, throttle input events to frame rate) this._interactionLock = true; this._snapPt = thisPt; - e.ctrlKey && (SelectionManager.Views.forEach(docView => !Doc.NativeHeight(docView.Document) && docView.toggleNativeDimensions())); - const hasFixedAspect = SelectionManager.Docs.some(this.hasFixedAspect); + e.ctrlKey && (DocumentView.Selected().forEach(docView => !Doc.NativeHeight(docView.Document) && docView.toggleNativeDimensions())); + const hasFixedAspect = DocumentView.Selected().map(dv => dv.Document).some(this.hasFixedAspect); const scaleAspect = {x:scale.x === 1 && hasFixedAspect ? scale.y : scale.x, y: scale.x !== 1 && hasFixedAspect ? scale.x : scale.y}; - SelectionManager.Views.forEach(docView => + DocumentView.Selected().forEach(docView => this.resizeView(docView, refPt, scaleAspect, { dragHdl, ctrlKey:e.ctrlKey })); // prettier-ignore await new Promise<any>(res => { setTimeout(() => { res(this._interactionLock = undefined)})}); }); // prettier-ignore @@ -526,7 +517,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora const doc = docView.Document; if (doc.isGroup) { DocListCast(doc.data) - .map(member => DocumentManager.Instance.getDocumentView(member, docView)!) + .map(member => DocumentView.getDocumentView(member, docView)!) .forEach(member => this.resizeView(member, refPt, scale, opts)); doc.xPadding = NumCast(doc.xPadding) * scale.x; doc.yPadding = NumCast(doc.yPadding) * scale.y; @@ -607,7 +598,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora this._resizeUndo?.end(); // detect layout_autoHeight gesture and apply - SelectionManager.Views.forEach(view => { + DocumentView.Selected().forEach(view => { NumCast(view.Document._height) < 20 && (view.layoutDoc._layout_autoHeight = true); }); // need to change points for resize, or else rotation/control points will fail. @@ -626,18 +617,18 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora @computed get selectionTitle(): string { - if (SelectionManager.Views.length === 1) { - const selected = SelectionManager.Views[0]; + if (DocumentView.Selected().length === 1) { + const selected = DocumentView.Selected()[0]; if (this._titleControlString.startsWith('$')) { return Field.toJavascriptString(selected.Document[this._titleControlString.substring(1)] as FieldType) || '-unset-'; } return this._accumulatedTitle; } - return SelectionManager.Views.length > 1 ? '-multiple-' : '-unset-'; + return DocumentView.Selected().length > 1 ? '-multiple-' : '-unset-'; } @computed get rotCenter() { - const lastView = SelectionManager.Views.lastElement(); + const lastView = DocumentView.Selected().lastElement(); if (lastView) { const invXf = lastView.screenToContentsTransform().inverse(); const seldoc = lastView.layoutDoc; @@ -649,7 +640,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora render() { const { b, r, x, y } = this.Bounds; - const seldocview = SelectionManager.Views.lastElement(); + const seldocview = DocumentView.Selected().lastElement(); if (SnappingManager.IsDragging || r - x < 1 || x === Number.MAX_VALUE || !seldocview || this._hidden || isNaN(r) || isNaN(b) || isNaN(x) || isNaN(y)) { setTimeout( action(() => { @@ -678,7 +669,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora hideDecorations || seldocview._props.hideOpenButton || seldocview.Document.layout_hideOpenButton || - SelectionManager.Views.some(docView => docView.Document._dragOnlyWithinContainer || docView.Document.isGroup || docView.Document.layout_hideOpenButton) || + DocumentView.Selected().some(docView => docView.Document._dragOnlyWithinContainer || docView.Document.isGroup || docView.Document.layout_hideOpenButton) || this._isRounding || this._isRotating; const hideDeleteButton = @@ -688,7 +679,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora this._isRotating || seldocview._props.hideDeleteButton || seldocview.Document.hideDeleteButton || - SelectionManager.Views.some(docView => { + DocumentView.Selected().some(docView => { const collectionAcl = docView.containerViewPath?.()?.lastElement() ? GetEffectiveAcl(docView.containerViewPath?.().lastElement().dataDoc) : AclEdit; return collectionAcl !== AclAdmin && collectionAcl !== AclEdit && GetEffectiveAcl(docView.Document) !== AclAdmin; }); @@ -703,7 +694,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora const bounds = this.ClippedBounds; const useLock = bounds.r - bounds.x > 135; const useRotation = !hideResizers && seldocview.Document.type !== DocumentType.EQUATION && CollectionFreeFormDocumentView.from(seldocview); // when do we want an object to not rotate? - const rotation = SelectionManager.Views.length === 1 ? seldocview.screenToContentsTransform().inverse().RotateDeg : 0; + const rotation = DocumentView.Selected().length === 1 ? seldocview.screenToContentsTransform().inverse().RotateDeg : 0; // Radius constants const useRounding = seldocview.ComponentView instanceof ImageBox || seldocview.ComponentView instanceof FormattedTextBox || seldocview.ComponentView instanceof CollectionFreeFormView; @@ -773,7 +764,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora ); const centery = hideTitle ? 0 : this._titleHeight; const transformOrigin = `${50}% calc(50% + ${centery / 2}px)`; - const freeformDoc = SelectionManager.Views.some(v => CollectionFreeFormDocumentView.from(v)); + const freeformDoc = DocumentView.Selected().some(v => CollectionFreeFormDocumentView.from(v)); return ( <div className="documentDecorations" style={{ display: this._showNothing && !freeformDoc ? 'none' : undefined }}> <div @@ -786,7 +777,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora transformOrigin, background: SnappingManager.ShiftKey ? undefined : 'yellow', pointerEvents: SnappingManager.ShiftKey || SnappingManager.IsResizing ? 'none' : 'all', - display: SelectionManager.Views.length <= 1 || hideDecorations ? 'none' : undefined, + display: DocumentView.Selected().length <= 1 || hideDecorations ? 'none' : undefined, transform: `rotate(${rotation}deg)`, }} onPointerDown={this.onBackgroundDown} @@ -843,7 +834,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora style={{ transform: `translate(${-this._resizeBorderWidth / 2 + 10}px, ${this._resizeBorderWidth + bounds.b - bounds.y + this._titleHeight}px) `, }}> - <DocumentButtonBar views={() => SelectionManager.Views} /> + <DocumentButtonBar views={() => DocumentView.Selected()} /> </div> )} </div> diff --git a/src/client/views/FieldsDropdown.tsx b/src/client/views/FieldsDropdown.tsx index 3cb7848c2..0ea0ebd83 100644 --- a/src/client/views/FieldsDropdown.tsx +++ b/src/client/views/FieldsDropdown.tsx @@ -13,7 +13,7 @@ import Select from 'react-select'; import { Doc } from '../../fields/Doc'; import { DocOptions, FInfo } from '../documents/Documents'; import { SearchUtil } from '../util/SearchUtil'; -import { SettingsManager } from '../util/SettingsManager'; +import { SnappingManager } from '../util/SnappingManager'; import './FilterPanel.scss'; import { ObservableReactComponent } from './ObservableReactComponent'; @@ -68,36 +68,36 @@ export class FieldsDropdown extends ObservableReactComponent<fieldsDropdownProps ...baseStyles, minHeight: '5px', maxHeight: '30px', - color: SettingsManager.userColor, - backgroundColor: SettingsManager.userBackgroundColor, + color: SnappingManager.userColor, + backgroundColor: SnappingManager.userBackgroundColor, padding: 0, margin: 0, }), singleValue: (baseStyles /* , state */) => ({ ...baseStyles, - color: SettingsManager.userColor, - background: SettingsManager.userBackgroundColor, + color: SnappingManager.userColor, + background: SnappingManager.userBackgroundColor, }), placeholder: (baseStyles /* , state */) => ({ ...baseStyles, - color: SettingsManager.userColor, - background: SettingsManager.userBackgroundColor, + color: SnappingManager.userColor, + background: SnappingManager.userBackgroundColor, }), input: (baseStyles /* , state */) => ({ ...baseStyles, padding: 0, margin: 0, - color: SettingsManager.userColor, + color: SnappingManager.userColor, background: 'transparent', }), option: (baseStyles, state) => ({ ...baseStyles, - color: SettingsManager.userColor, - background: !state.isFocused ? SettingsManager.userBackgroundColor : SettingsManager.userVariantColor, + color: SnappingManager.userColor, + background: !state.isFocused ? SnappingManager.userBackgroundColor : SnappingManager.userVariantColor, }), menuList: (baseStyles /* , state */) => ({ ...baseStyles, - backgroundColor: SettingsManager.userBackgroundColor, + backgroundColor: SnappingManager.userBackgroundColor, }), }} placeholder={typeof this._props.placeholder === 'string' ? this._props.placeholder : this._props.placeholder?.()} diff --git a/src/client/views/FilterPanel.tsx b/src/client/views/FilterPanel.tsx index ed9fc8dfb..c97edd7f0 100644 --- a/src/client/views/FilterPanel.tsx +++ b/src/client/views/FilterPanel.tsx @@ -11,12 +11,12 @@ import { Doc, DocListCast, Field, FieldType, LinkedTo, StrListCast } from '../.. import { Id } from '../../fields/FieldSymbols'; import { List } from '../../fields/List'; import { RichTextField } from '../../fields/RichTextField'; -import { DocumentManager } from '../util/DocumentManager'; import { SearchUtil } from '../util/SearchUtil'; -import { SettingsManager } from '../util/SettingsManager'; +import { SnappingManager } from '../util/SnappingManager'; import { undoable } from '../util/UndoManager'; import { FieldsDropdown } from './FieldsDropdown'; import './FilterPanel.scss'; +import { DocumentView } from './nodes/DocumentView'; import { Handle, Tick, TooltipRail, Track } from './nodes/SliderBox-components'; import { ObservableReactComponent } from './ObservableReactComponent'; @@ -40,7 +40,7 @@ export class FilterPanel extends ObservableReactComponent<filterProps> { return this._props.Document; } @computed get targetDocChildKey() { - const targetView = DocumentManager.Instance.getFirstDocumentView(this.Document); + const targetView = DocumentView.getFirstDocumentView(this.Document); return targetView?.ComponentView?.annotationKey ?? targetView?.ComponentView?.fieldKey ?? 'data'; } @computed get targetDocChildren() { @@ -299,7 +299,7 @@ export class FilterPanel extends ObservableReactComponent<filterProps> { .find(filter => filter.split(Doc.FilterSep)[0] === facetHeader) ?.split(Doc.FilterSep)[1] } - style={{ color: SettingsManager.userColor, background: SettingsManager.userBackgroundColor }} + style={{ color: SnappingManager.userColor, background: SnappingManager.userBackgroundColor }} onBlur={undoable(e => Doc.setDocFilter(this.Document, facetHeader, e.currentTarget.value, !e.currentTarget.value ? 'remove' : 'match'), 'set text filter')} onKeyDown={e => e.key === 'Enter' && undoable(() => Doc.setDocFilter(this.Document, facetHeader, e.currentTarget.value, !e.currentTarget.value ? 'remove' : 'match'), 'set text filter')()} /> diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx index 0d1573ea3..7246f0ba0 100644 --- a/src/client/views/GestureOverlay.tsx +++ b/src/client/views/GestureOverlay.tsx @@ -65,8 +65,6 @@ export class GestureOverlay extends ObservableReactComponent<React.PropsWithChil @observable private _clipboardDoc?: JSX.Element = undefined; @observable private _possibilities: JSX.Element[] = []; - public static DownDocView: DocumentView | undefined; - @computed private get height(): number { return 2 * Math.max(this._pointerY && this._thumbY ? this._thumbY - this._pointerY : 100, 100); } @@ -136,7 +134,7 @@ export class GestureOverlay extends ObservableReactComponent<React.PropsWithChil } @action onPointerUp = () => { - GestureOverlay.DownDocView = undefined; + DocumentView.DownDocView = undefined; if (this._points.length > 1) { const B = this.svgBounds; const points = this._points.map(p => ({ X: p.X - B.left, Y: p.Y - B.top })); @@ -388,7 +386,7 @@ export class GestureOverlay extends ObservableReactComponent<React.PropsWithChil } get elements() { - const selView = GestureOverlay.DownDocView; + const selView = DocumentView.DownDocView; const width = Number(ActiveInkWidth()) * NumCast(selView?.Document._freeform_scale, 1); // * (selView?.screenToViewTransform().Scale || 1); const rect = this._overlayRef.current?.getBoundingClientRect(); const B = { left: -20000, right: 20000, top: -20000, bottom: 20000, width: 40000, height: 40000 }; // this.getBounds(this._points, true); diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts index 0f444094a..c73da35bc 100644 --- a/src/client/views/GlobalKeyHandler.ts +++ b/src/client/views/GlobalKeyHandler.ts @@ -8,7 +8,6 @@ import { Cast, PromiseValue } from '../../fields/Types'; import { GoogleAuthenticationManager } from '../apis/GoogleAuthenticationManager'; import { DragManager } from '../util/DragManager'; import { GroupManager } from '../util/GroupManager'; -import { SelectionManager } from '../util/SelectionManager'; import { SettingsManager } from '../util/SettingsManager'; import { SharingManager } from '../util/SharingManager'; import { SnappingManager } from '../util/SnappingManager'; @@ -23,6 +22,7 @@ import { CollectionStackedTimeline } from './collections/CollectionStackedTimeli import { CollectionFreeFormView } from './collections/collectionFreeForm'; import { CollectionFreeFormDocumentView } from './nodes/CollectionFreeFormDocumentView'; import { DocumentLinksButton } from './nodes/DocumentLinksButton'; +import { DocumentView } from './nodes/DocumentView'; import { OpenWhereMod } from './nodes/OpenWhere'; import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox'; import { AnchorMenu } from './pdf/AnchorMenu'; @@ -34,10 +34,12 @@ type KeyControlInfo = { }; type KeyHandler = (keycode: string, e: KeyboardEvent) => KeyControlInfo; export class KeyManager { - public static Instance = new KeyManager(); + // eslint-disable-next-line no-use-before-define + public static Instance: KeyManager; private router = new Map<string, KeyHandler>(); constructor() { + KeyManager.Instance = this; const isMac = navigator.platform.toLowerCase().indexOf('mac') >= 0; // SHIFT CONTROL ALT META @@ -46,6 +48,16 @@ export class KeyManager { this.router.set(isMac ? '0100' : '0010', this.alt); this.router.set(isMac ? '1001' : '1100', this.ctrl_shift); this.router.set('1000', this.shift); + + window.removeEventListener('keydown', KeyManager.Instance.handleModifiers, true); + window.addEventListener('keydown', KeyManager.Instance.handleModifiers, true); + window.removeEventListener('keyup', KeyManager.Instance.unhandleModifiers); + window.addEventListener('keyup', KeyManager.Instance.unhandleModifiers); + window.removeEventListener('keydown', KeyManager.Instance.handle); + window.addEventListener('keydown', KeyManager.Instance.handle); + window.removeEventListener('keyup', KeyManager.Instance.unhandle); + window.addEventListener('keyup', KeyManager.Instance.unhandle); + window.addEventListener('paste', KeyManager.Instance.paste as any); } public unhandle = action((/* e: KeyboardEvent */) => { @@ -89,8 +101,8 @@ export class KeyManager { private handleGreedy = action((/* keyname: string */) => {}); nudge = (x: number, y: number, label: string) => { - const nudgeable = SelectionManager.Views.some(dv => CollectionFreeFormDocumentView.from(dv)?.nudge); - nudgeable && UndoManager.RunInBatch(() => SelectionManager.Views.map(dv => CollectionFreeFormDocumentView.from(dv)?.nudge(x, y)), label); + const nudgeable = DocumentView.Selected().some(dv => CollectionFreeFormDocumentView.from(dv)?.nudge); + nudgeable && UndoManager.RunInBatch(() => DocumentView.Selected().map(dv => CollectionFreeFormDocumentView.from(dv)?.nudge(x, y)), label); return { stopPropagation: nudgeable, preventDefault: nudgeable }; }; @@ -98,16 +110,16 @@ export class KeyManager { switch (keyname) { case 'u': if (document.activeElement?.tagName !== 'INPUT' && document.activeElement?.tagName !== 'TEXTAREA') { - const ungroupings = SelectionManager.Views; + const ungroupings = DocumentView.Selected(); undoable(() => () => ungroupings.forEach(dv => { dv.layoutDoc.group = undefined; }), 'ungroup'); - SelectionManager.DeselectAll(); + DocumentView.DeselectAll(); } break; case 'g': if (document.activeElement?.tagName !== 'INPUT' && document.activeElement?.tagName !== 'TEXTAREA') { - const selected = SelectionManager.Views; + const selected = DocumentView.Selected(); const cv = selected.reduce((col, dv) => (!col || CollectionFreeFormView.from(dv) === col ? CollectionFreeFormView.from(dv) : undefined), undefined as undefined | CollectionFreeFormView); - cv && undoable(() => cv._marqueeViewRef.current?.collection(e, true, SelectionManager.Docs), 'grouping'); + cv && undoable(() => cv._marqueeViewRef.current?.collection(e, true, DocumentView.SelectedDocs()), 'grouping'); } break; case ' ': @@ -133,7 +145,7 @@ export class KeyManager { doDeselect = !ContextMenu.Instance.closeMenu(); } if (doDeselect) { - SelectionManager.DeselectAll(); + DocumentView.DeselectAll(); LightboxView.Instance.SetLightboxDoc(undefined); } // DictationManager.Controls.stop(); @@ -153,7 +165,7 @@ export class KeyManager { if (document.activeElement?.tagName !== 'INPUT' && document.activeElement?.tagName !== 'TEXTAREA') { if (LightboxView.LightboxDoc) { LightboxView.Instance.SetLightboxDoc(undefined); - SelectionManager.DeselectAll(); + DocumentView.DeselectAll(); } else if (!window.getSelection()?.toString()) DocumentDecorations.Instance.onCloseClick(true); return { stopPropagation: true, preventDefault: true }; } @@ -179,15 +191,15 @@ export class KeyManager { case 'arrowdown': return this.nudge(0, 10, 'nudge down'); case 'u' : if (document.activeElement?.tagName !== 'INPUT' && document.activeElement?.tagName !== 'TEXTAREA') { - UndoManager.RunInBatch(() => SelectionManager.Docs.forEach(doc => {doc.group = undefined}), 'unggroup'); - SelectionManager.DeselectAll(); + UndoManager.RunInBatch(() => DocumentView.Selected().map(dv => dv.Document).forEach(doc => {doc.group = undefined}), 'unggroup'); + DocumentView.DeselectAll(); } break; case 'g': if (document.activeElement?.tagName !== 'INPUT' && document.activeElement?.tagName !== 'TEXTAREA') { const randomGroup = random(0, 1000); - UndoManager.RunInBatch(() => SelectionManager.Docs.forEach(doc => {doc.group = randomGroup}), 'group'); - SelectionManager.DeselectAll(); + UndoManager.RunInBatch(() => DocumentView.Selected().forEach(dv => {dv.Document.group = randomGroup}), 'group'); + DocumentView.DeselectAll(); } break; default: @@ -206,7 +218,7 @@ export class KeyManager { switch (keyname) { case 'ƒ': case 'f': - UndoManager.RunInBatch(() => CollectionFreeFormDocumentView.from(SelectionManager.Views?.[0])?.float(), 'float'); + UndoManager.RunInBatch(() => CollectionFreeFormDocumentView.from(DocumentView.Selected()?.[0])?.float(), 'float'); break; default: } @@ -259,8 +271,8 @@ export class KeyManager { } break; case 'f': - if (SelectionManager.Views.length === 1 && SelectionManager.Views[0].ComponentView?.search) { - SelectionManager.Views[0].ComponentView?.search?.('', false, false); + if (DocumentView.Selected().length === 1 && DocumentView.Selected()[0].ComponentView?.search) { + DocumentView.Selected()[0].ComponentView?.search?.('', false, false); } else { const searchBtn = DocListCast(Doc.MyLeftSidebarMenu.data).find(d => d.target === Doc.MySearcher); if (searchBtn) { @@ -279,14 +291,14 @@ export class KeyManager { break; case 'y': if (Doc.ActivePage !== 'home') { - SelectionManager.DeselectAll(); + DocumentView.DeselectAll(); UndoManager.Redo(); } stopPropagation = false; break; case 'z': if (Doc.ActivePage !== 'home') { - SelectionManager.DeselectAll(); + DocumentView.DeselectAll(); UndoManager.Undo(); } stopPropagation = false; @@ -302,11 +314,17 @@ export class KeyManager { preventDefault = false; break; case 'x': - if (SelectionManager.Views.length) { + if (DocumentView.Selected().length) { const bds = DocumentDecorations.Instance.Bounds; - const pt = SelectionManager.Views[0].screenToViewTransform().transformPoint(bds.x + (bds.r - bds.x) / 2, bds.y + (bds.b - bds.y) / 2); - const text = `__DashDocId(${pt?.[0] || 0},${pt?.[1] || 0}):` + SelectionManager.Views.map(dv => dv.Document[Id]).join(':'); - SelectionManager.Views.length && navigator.clipboard.writeText(text); + const pt = DocumentView.Selected()[0] // + .screenToViewTransform() + .transformPoint(bds.x + (bds.r - bds.x) / 2, bds.y + (bds.b - bds.y) / 2); + const text = + `__DashDocId(${pt?.[0] || 0},${pt?.[1] || 0}):` + // + DocumentView.Selected() + .map(dv => dv.Document[Id]) + .join(':'); // prettier-ignore + DocumentView.Selected().length && navigator.clipboard.writeText(text); DocumentDecorations.Instance.onCloseClick(true); stopPropagation = false; preventDefault = false; @@ -315,9 +333,15 @@ export class KeyManager { case 'c': if ((document.activeElement as any)?.type !== 'text' && !AnchorMenu.Instance.Active && DocumentDecorations.Instance.Bounds.r - DocumentDecorations.Instance.Bounds.x > 2) { const bds = DocumentDecorations.Instance.Bounds; - const pt = SelectionManager.Views[0].screenToViewTransform().transformPoint(bds.x + (bds.r - bds.x) / 2, bds.y + (bds.b - bds.y) / 2); - const text = `__DashCloneId(${pt?.[0] || 0},${pt?.[1] || 0}):` + SelectionManager.Views.map(dv => dv.Document[Id]).join(':'); - SelectionManager.Views.length && navigator.clipboard.writeText(text); + const pt = DocumentView.Selected()[0] + .screenToViewTransform() + .transformPoint(bds.x + (bds.r - bds.x) / 2, bds.y + (bds.b - bds.y) / 2); // prettier-ignore + const text = + `__DashCloneId(${pt?.[0] || 0},${pt?.[1] || 0}):` + + DocumentView.SelectedDocs() + .map(doc => doc[Id]) + .join(':'); // prettier-ignore + DocumentView.Selected().length && navigator.clipboard.writeText(text); stopPropagation = false; } preventDefault = false; @@ -336,7 +360,7 @@ export class KeyManager { if (!plain) return; const clone = plain.startsWith('__DashCloneId('); const docids = plain.split(':'); // hack! docids[0] is the top left of the selection rectangle - const addDocument = SelectionManager.Views.lastElement()?.ComponentView?.addDocument; + const addDocument = DocumentView.Selected().lastElement()?.ComponentView?.addDocument; if (addDocument && (plain.startsWith('__DashDocId(') || clone)) { Doc.Paste(docids.slice(1), clone, addDocument); } diff --git a/src/client/views/InkControlPtHandles.tsx b/src/client/views/InkControlPtHandles.tsx index b4fe44733..a49923ffb 100644 --- a/src/client/views/InkControlPtHandles.tsx +++ b/src/client/views/InkControlPtHandles.tsx @@ -7,7 +7,6 @@ import { List } from '../../fields/List'; import { listSpec } from '../../fields/Schema'; import { Cast } from '../../fields/Types'; import { returnFalse, setupMoveUpEvents } from '../../ClientUtils'; -import { SelectionManager } from '../util/SelectionManager'; import { UndoManager } from '../util/UndoManager'; import { Colors } from './global/globalEnums'; import { InkingStroke } from './InkingStroke'; @@ -15,6 +14,7 @@ import { InkStrokeProperties } from './InkStrokeProperties'; import { SnappingManager } from '../util/SnappingManager'; import { ObservableReactComponent } from './ObservableReactComponent'; import { PointData } from '../../pen-gestures/GestureTypes'; +import { DocumentView } from './nodes/DocumentView'; export interface InkControlProps { inkDoc: Doc; @@ -224,8 +224,8 @@ export class InkEndPtHandles extends ObservableReactComponent<InkEndProps> { const v1n = { X: v1.X / v1len, Y: v1.Y / v1len }; const v2n = { X: v2.X / v2len, Y: v2.Y / v2len }; const angle = Math.acos(v1n.X * v2n.X + v1n.Y * v2n.Y) * Math.sign(v1.X * v2.Y - v2.X * v1.Y); - InkStrokeProperties.Instance.stretchInk(SelectionManager.Views, scaling, p2, v1n, moveEv.shiftKey); - InkStrokeProperties.Instance.rotateInk(SelectionManager.Views, angle, pt2()); // bcz: call pt2() func here because pt2 will have changed from previous stretchInk call + InkStrokeProperties.Instance.stretchInk(DocumentView.Selected(), scaling, p2, v1n, moveEv.shiftKey); + InkStrokeProperties.Instance.rotateInk(DocumentView.Selected(), angle, pt2()); // bcz: call pt2() func here because pt2 will have changed from previous stretchInk call return false; }), action(() => { diff --git a/src/client/views/InkStrokeProperties.ts b/src/client/views/InkStrokeProperties.ts index 109a9cad4..3920ecc2a 100644 --- a/src/client/views/InkStrokeProperties.ts +++ b/src/client/views/InkStrokeProperties.ts @@ -9,7 +9,6 @@ import { Cast, NumCast } from '../../fields/Types'; import { PointData } from '../../pen-gestures/GestureTypes'; import { Point } from '../../pen-gestures/ndollar'; import { DocumentType } from '../documents/DocumentTypes'; -import { DocumentManager } from '../util/DocumentManager'; import { undoBatch } from '../util/UndoManager'; import { FitOneCurve } from '../util/bezierFit'; import { InkingStroke } from './InkingStroke'; @@ -386,7 +385,7 @@ export class InkStrokeProperties { containingCollection?.childDocs .filter(doc => doc.type === DocumentType.INK) .forEach(doc => { - const testInkView = DocumentManager.Instance.getDocumentView(doc, containingDocView); + const testInkView = DocumentView.getDocumentView(doc, containingDocView); const snapped = testInkView?.ComponentView?.snapPt?.(screenDragPt, doc === inkView.Document ? this.excludeSelfSnapSegs(ink, controlIndex) : []); if (snapped && snapped.distance < snapData.distance) { const snappedInkPt = doc === inkView.Document ? snapped.nearestPt : inkView.ComponentView?.ptFromScreen?.(testInkView?.ComponentView?.ptToScreen?.(snapped.nearestPt) ?? { X: 0, Y: 0 }); // convert from snapped ink coordinate system to dragged ink coordinate system by converting to/from screen space diff --git a/src/client/views/InkTranscription.tsx b/src/client/views/InkTranscription.tsx index 7e2b334af..1ed8de1be 100644 --- a/src/client/views/InkTranscription.tsx +++ b/src/client/views/InkTranscription.tsx @@ -6,7 +6,6 @@ // import { Cast, DateCast, NumCast } from '../../fields/Types'; // import { aggregateBounds } from '../../Utils'; // import { DocumentType } from '../documents/DocumentTypes'; -// import { DocumentManager } from '../util/DocumentManager'; // import { CollectionFreeFormView } from './collections/collectionFreeForm'; // import { InkingStroke } from './InkingStroke'; // import './InkTranscription.scss'; diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx index 884512539..62fc73c78 100644 --- a/src/client/views/InkingStroke.tsx +++ b/src/client/views/InkingStroke.tsx @@ -44,7 +44,7 @@ import { InkTangentHandles } from './InkTangentHandles'; import { FieldView, FieldViewProps } from './nodes/FieldView'; import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox'; import { PinDocView, PinProps } from './PinFuncs'; -import { StyleProp } from './StyleProvider'; +import { StyleProp } from './StyleProp'; const { INK_MASK_SIZE } = require('./global/globalCssVariables.module.scss'); // prettier-ignore diff --git a/src/client/views/LightboxView.tsx b/src/client/views/LightboxView.tsx index 020525ef8..12d899388 100644 --- a/src/client/views/LightboxView.tsx +++ b/src/client/views/LightboxView.tsx @@ -13,10 +13,6 @@ import { CreateLinkToActiveAudio, Doc, DocListCast, FieldResult, Opt } from '../ import { Id } from '../../fields/FieldSymbols'; import { InkTool } from '../../fields/InkField'; import { Cast, NumCast, toList } from '../../fields/Types'; -import { DocumentManager } from '../util/DocumentManager'; -import { LinkManager } from '../util/LinkManager'; -import { SelectionManager } from '../util/SelectionManager'; -import { SettingsManager } from '../util/SettingsManager'; import { SnappingManager } from '../util/SnappingManager'; import { Transform } from '../util/Transform'; import { GestureOverlay } from './GestureOverlay'; @@ -24,8 +20,6 @@ import './LightboxView.scss'; import { ObservableReactComponent } from './ObservableReactComponent'; import { DefaultStyleProvider, wavyBorderPath } from './StyleProvider'; import { CollectionDockingView } from './collections/CollectionDockingView'; -import { CollectionStackedTimeline } from './collections/CollectionStackedTimeline'; -import { TabDocView } from './collections/TabDocView'; import { DocumentView } from './nodes/DocumentView'; import { OpenWhere, OpenWhereMod } from './nodes/OpenWhere'; @@ -90,7 +84,7 @@ export class LightboxView extends ObservableReactComponent<LightboxViewProps> { }); const l = CreateLinkToActiveAudio(() => doc).lastElement(); l && (Cast(l.link_anchor_2, Doc, null).backgroundColor = 'lightgreen'); - CollectionStackedTimeline.CurrentlyPlaying?.forEach(dv => dv.ComponentView?.Pause?.()); + DocumentView.CurrentlyPlaying?.forEach(dv => dv.ComponentView?.Pause?.()); this._history.push({ doc, target }); } else { this._future = []; @@ -98,14 +92,14 @@ export class LightboxView extends ObservableReactComponent<LightboxViewProps> { Doc.ActiveTool = InkTool.None; SnappingManager.SetExploreMode(false); } - SelectionManager.DeselectAll(); + DocumentView.DeselectAll(); if (future) { this._future.push( ...(this._doc ? [this._doc] : []), ...future .slice() .sort((a, b) => NumCast(b._timecodeToShow) - NumCast(a._timecodeToShow)) - .sort((a, b) => LinkManager.Links(a).length - LinkManager.Links(b).length) + .sort((a, b) => Doc.Links(a).length - Doc.Links(b).length) ); } this._doc = doc; @@ -134,11 +128,11 @@ export class LightboxView extends ObservableReactComponent<LightboxViewProps> { const lightDoc = this._doc; if (!lightDoc) return; const target = (this._docTarget = this._future.pop()); - const targetDocView = target && DocumentManager.Instance.getLightboxDocumentView(target); + const targetDocView = target && DocumentView.getLightboxDocumentView(target); if (targetDocView && target) { const l = CreateLinkToActiveAudio(() => targetDocView.ComponentView?.getAnchor?.(true) || target).lastElement(); l && (Cast(l.link_anchor_2, Doc, null).backgroundColor = 'lightgreen'); - DocumentManager.Instance.showDocument(target, { willZoomCentered: true, zoomScale: 0.9 }); + DocumentView.showDocument(target, { willZoomCentered: true, zoomScale: 0.9 }); if (this._history.lastElement().target !== target) this._history.push({ doc: lightDoc, target }); } else if (!target && this._path.length) { savedKeys.forEach(key => { @@ -157,10 +151,10 @@ export class LightboxView extends ObservableReactComponent<LightboxViewProps> { return; } const { doc, target } = this._history.lastElement(); - const docView = DocumentManager.Instance.getLightboxDocumentView(target || doc); + const docView = DocumentView.getLightboxDocumentView(target || doc); if (docView) { this._docTarget = target; - target && DocumentManager.Instance.showDocument(target, { willZoomCentered: true, zoomScale: 0.9 }); + target && DocumentView.showDocument(target, { willZoomCentered: true, zoomScale: 0.9 }); } else { this.SetLightboxDoc(doc, target); } @@ -178,8 +172,8 @@ export class LightboxView extends ObservableReactComponent<LightboxViewProps> { if (this._docTarget) { const fieldKey = Doc.LayoutFieldKey(this._docTarget); const contents = [...DocListCast(this._docTarget[fieldKey]), ...DocListCast(this._docTarget[fieldKey + '_annotations'])]; - const links = LinkManager.Links(this._docTarget) - .map(link => LinkManager.getOppositeAnchor(link, this._docTarget!)!) + const links = Doc.Links(this._docTarget) + .map(link => Doc.getOppositeAnchor(link, this._docTarget!)!) .filter(doc => doc); this.SetLightboxDoc(this._docTarget, undefined, contents.length ? contents : links); } @@ -220,7 +214,7 @@ export class LightboxView extends ObservableReactComponent<LightboxViewProps> { <div className="lightboxView-navBtn" title={color} - style={{ top, color: SettingsManager.userColor, background: undefined }} + style={{ top, color: SnappingManager.userColor, background: undefined }} onClick={e => { e.stopPropagation(); click(); @@ -237,8 +231,8 @@ export class LightboxView extends ObservableReactComponent<LightboxViewProps> { <div className={classname}> <Toggle tooltip={tooltip} - color={SettingsManager.userColor} - background={toggleBackground ? SettingsManager.userVariantColor : SettingsManager.userBackgroundColor} + color={SnappingManager.userColor} + background={toggleBackground ? SnappingManager.userVariantColor : SnappingManager.userBackgroundColor} toggleType={ToggleType.BUTTON} type={Type.TERT} icon={<FontAwesomeIcon icon={toggleBackground ? icon : (icon2 as IconProp) || icon} size="sm" />} @@ -252,7 +246,7 @@ export class LightboxView extends ObservableReactComponent<LightboxViewProps> { return !this._doc ? null : ( <div className="lightboxView-frame" - style={{ background: SettingsManager.userBackgroundColor }} + style={{ background: SnappingManager.userBackgroundColor }} onPointerDown={e => { downx = e.clientX; downy = e.clientY; @@ -266,7 +260,7 @@ export class LightboxView extends ObservableReactComponent<LightboxViewProps> { width: this.lightboxWidth(), height: this.lightboxHeight(), clipPath: `path('${Doc.UserDoc().renderStyle === 'comic' ? wavyBorderPath(this.lightboxWidth(), this.lightboxHeight()) : undefined}')`, - background: SettingsManager.userBackgroundColor, + background: SnappingManager.userBackgroundColor, }}> <GestureOverlay isActive> <DocumentView @@ -292,7 +286,7 @@ export class LightboxView extends ObservableReactComponent<LightboxViewProps> { removeDocument={undefined} whenChildContentsActiveChanged={emptyFunction} addDocTab={this.AddDocTab} - pinToPres={TabDocView.PinDoc} + pinToPres={DocumentView.PinDoc} focus={emptyFunction} /> </GestureOverlay> diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx index 01f3b032e..b6dfed687 100644 --- a/src/client/views/Main.tsx +++ b/src/client/views/Main.tsx @@ -8,14 +8,17 @@ import * as React from 'react'; import * as ReactDOM from 'react-dom/client'; import { AssignAllExtensions } from '../../extensions/Extensions'; import { FieldLoader } from '../../fields/FieldLoader'; +import { BranchingTrailManager } from '../util/BranchingTrailManager'; import { CurrentUserUtils } from '../util/CurrentUserUtils'; import { PingManager } from '../util/PingManager'; import { ReplayMovements } from '../util/ReplayMovements'; import { TrackMovements } from '../util/TrackMovements'; +import { KeyManager } from './GlobalKeyHandler'; +import { MainView } from './MainView'; import { CollectionView } from './collections/CollectionView'; +import { CollectionFreeFormInfoUI } from './collections/collectionFreeForm/CollectionFreeFormInfoUI'; import './global/globalScripts'; -import { MainView } from './MainView'; -import { BranchingTrailManager } from '../util/BranchingTrailManager'; +import { LinkFollower } from '../util/LinkFollower'; dotenv.config(); @@ -58,6 +61,11 @@ FieldLoader.ServerLoadStatus = { requested: 0, retrieved: 0, message: 'cache' }; new ReplayMovements(); new BranchingTrailManager({}); new PingManager(); + new KeyManager(); + + // iniitialize plugin apis + CollectionFreeFormInfoUI.Init(); + LinkFollower.Init(); root.render(<MainView />); }, 0); })(); diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index aec553baa..2c7d3d32a 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -12,7 +12,8 @@ import '../../../node_modules/browndash-components/dist/styles/global.min.css'; import { ClientUtils, lightOrDark, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, returnZero, setupMoveUpEvents } from '../../ClientUtils'; import { emptyFunction } from '../../Utils'; import { Doc, DocListCast, GetDocFromUrl, Opt } from '../../fields/Doc'; -import { DocData } from '../../fields/DocSymbols'; +import { DocData, DocViews } from '../../fields/DocSymbols'; +import { Id } from '../../fields/FieldSymbols'; import { DocCast, StrCast, toList } from '../../fields/Types'; import { DocServer } from '../DocServer'; import { GoogleAuthenticationManager } from '../apis/GoogleAuthenticationManager'; @@ -29,7 +30,6 @@ import { Hypothesis } from '../util/HypothesisUtils'; import { UPDATE_SERVER_CACHE } from '../util/LinkManager'; import { RTFMarkup } from '../util/RTFMarkup'; import { ScriptingGlobals } from '../util/ScriptingGlobals'; -import { SelectionManager } from '../util/SelectionManager'; import { ServerStats } from '../util/ServerStats'; import { SettingsManager } from '../util/SettingsManager'; import { SharingManager } from '../util/SharingManager'; @@ -42,7 +42,6 @@ import { DashboardView } from './DashboardView'; import { DictationOverlay } from './DictationOverlay'; import { DocumentDecorations } from './DocumentDecorations'; import { GestureOverlay } from './GestureOverlay'; -import { KeyManager } from './GlobalKeyHandler'; import { LightboxView } from './LightboxView'; import './MainView.scss'; import { ObservableReactComponent } from './ObservableReactComponent'; @@ -160,7 +159,7 @@ export class MainView extends ObservableReactComponent<{}> { leftMenuHeight = () => this._dashUIHeight; leftMenuFlyoutWidth = () => this._leftMenuFlyoutWidth; leftMenuFlyoutHeight = () => this._dashUIHeight; - propertiesWidth = () => Math.max(0, Math.min(this._dashUIWidth - 50, SettingsManager.Instance?.propertiesWidth || 0)); + propertiesWidth = () => Math.max(0, Math.min(this._dashUIWidth - 50, SnappingManager.PropertiesWidth || 0)); propertiesHeight = () => this._dashUIHeight; mainDocViewWidth = () => this._dashUIWidth - this.propertiesWidth() - this.leftMenuWidth() - this.leftMenuFlyoutWidth(); mainDocViewHeight = () => this._dashUIHeight - this.headerBarDocHeight(); @@ -169,7 +168,7 @@ export class MainView extends ObservableReactComponent<{}> { // Utils.TraceConsoleLog(); reaction( // when a multi-selection occurs, remove focus from all active elements to allow keyboad input to go only to global key manager to act upon selection - () => SelectionManager.Views.slice(), + () => DocumentView.Selected().slice(), views => views.length > 1 && (document.activeElement as any)?.blur !== undefined && (document.activeElement as any)!.blur() ); const scriptTag = document.createElement('script'); @@ -231,37 +230,29 @@ export class MainView extends ObservableReactComponent<{}> { tag.src = 'https://www.youtube.com/iframe_api'; const firstScriptTag = document.getElementsByTagName('script')[0]; firstScriptTag.parentNode!.insertBefore(tag, firstScriptTag); - window.removeEventListener('keydown', KeyManager.Instance.handleModifiers, true); - window.addEventListener('keydown', KeyManager.Instance.handleModifiers, true); - window.removeEventListener('keyup', KeyManager.Instance.unhandleModifiers); - window.addEventListener('keyup', KeyManager.Instance.unhandleModifiers); - window.removeEventListener('keydown', KeyManager.Instance.handle); - window.addEventListener('keydown', KeyManager.Instance.handle); - window.removeEventListener('keyup', KeyManager.Instance.unhandle); - window.addEventListener('keyup', KeyManager.Instance.unhandle); - window.addEventListener('paste', KeyManager.Instance.paste as any); document.addEventListener('dash', (e: any) => { // event used by chrome plugin to tell Dash which document to focus on const id = GetDocFromUrl(e.detail); - DocServer.GetRefField(id).then(doc => (doc instanceof Doc ? DocumentManager.Instance.showDocument(doc, { willPan: false }) : null)); + DocServer.GetRefField(id).then(doc => (doc instanceof Doc ? DocumentView.showDocument(doc, { willPan: false }) : null)); }); document.addEventListener('linkAnnotationToDash', Hypothesis.linkListener); this.initEventListeners(); } componentWillUnMount() { - window.removeEventListener('keyup', KeyManager.Instance.unhandle); - window.removeEventListener('keydown', KeyManager.Instance.handle); - window.removeEventListener('pointerdown', this.globalPointerDown, true); - window.removeEventListener('pointermove', this.globalPointerMove, true); - window.removeEventListener('pointerup', this.globalPointerClick, true); - window.removeEventListener('paste', KeyManager.Instance.paste as any); - document.removeEventListener('linkAnnotationToDash', Hypothesis.linkListener); + // window.removeEventListener('keyup', KeyManager.Instance.unhandle); + // window.removeEventListener('keydown', KeyManager.Instance.handle); + // window.removeEventListener('pointerdown', this.globalPointerDown, true); + // window.removeEventListener('pointermove', this.globalPointerMove, true); + // window.removeEventListener('pointerup', this.globalPointerClick, true); + // window.removeEventListener('paste', KeyManager.Instance.paste as any); + // document.removeEventListener('linkAnnotationToDash', Hypothesis.linkListener); } constructor(props: any) { super(props); makeObservable(this); + CollectionDockingView.setTabJSXComponent(TabDocView); DocumentViewInternal.addDocTabFunc = MainView.addDocTabFunc_impl; MainView.Instance = this; DashboardView._urlState = HistoryUtil.parseUrl(window.location) || ({} as any); @@ -592,7 +583,7 @@ export class MainView extends ObservableReactComponent<{}> { if (!e.cancelBubble) { const pathstr = (e as any)?.path?.map((p: any) => p.classList?.toString()).join(); if (pathstr?.includes('libraryFlyout')) { - SelectionManager.DeselectAll(); + DocumentView.DeselectAll(); } } }, @@ -719,14 +710,14 @@ export class MainView extends ObservableReactComponent<{}> { this, e, action(() => { - SettingsManager.Instance.propertiesWidth = Math.max(0, this._dashUIWidth - e.clientX); - return !SettingsManager.Instance.propertiesWidth; + SnappingManager.SetPropertiesWidth(Math.max(0, this._dashUIWidth - e.clientX)); + return !SnappingManager.PropertiesWidth; }), action(() => { - SettingsManager.Instance.propertiesWidth < 5 && (SettingsManager.Instance.propertiesWidth = 0); + SnappingManager.PropertiesWidth < 5 && SnappingManager.SetPropertiesWidth(0); }), action(() => { - SettingsManager.Instance.propertiesWidth = this.propertiesWidth() < 15 ? Math.min(this._dashUIWidth - 50, 250) : 0; + SnappingManager.SetPropertiesWidth(this.propertiesWidth() < 15 ? Math.min(this._dashUIWidth - 50, 250) : 0); }), false ); @@ -776,7 +767,7 @@ export class MainView extends ObservableReactComponent<{}> { Document={this._sidebarContent.proto || this._sidebarContent} addDocument={undefined} addDocTab={DocumentViewInternal.addDocTabFunc} - pinToPres={TabDocView.PinDoc} + pinToPres={DocumentView.PinDoc} containerViewPath={returnEmptyDoclist} styleProvider={this._sidebarContent.proto === Doc.MyDashboards || this._sidebarContent.proto === Doc.MyFilesystem || this._sidebarContent.proto === Doc.MyTrails ? DashboardStyleProvider : DefaultStyleProvider} removeDocument={returnFalse} @@ -799,7 +790,7 @@ export class MainView extends ObservableReactComponent<{}> { @computed get leftMenuPanel() { return ( - <div key="menu" className="mainView-leftMenuPanel" style={{ background: SettingsManager.userBackgroundColor, display: LightboxView.LightboxDoc ? 'none' : undefined }}> + <div key="menu" className="mainView-leftMenuPanel" style={{ background: SnappingManager.userBackgroundColor, display: LightboxView.LightboxDoc ? 'none' : undefined }}> <DocumentView Document={Doc.MyLeftSidebarMenu} addDocument={undefined} @@ -852,9 +843,9 @@ export class MainView extends ObservableReactComponent<{}> { {this.flyout} <div className="mainView-libraryHandle" - style={{ background: SettingsManager.userBackgroundColor, left: leftMenuFlyoutWidth - 10 /* ~half width of handle */, display: !this._leftMenuFlyoutWidth ? 'none' : undefined }} + style={{ background: SnappingManager.userBackgroundColor, left: leftMenuFlyoutWidth - 10 /* ~half width of handle */, display: !this._leftMenuFlyoutWidth ? 'none' : undefined }} onPointerDown={this.onFlyoutPointerDown}> - <FontAwesomeIcon icon="chevron-left" color={SettingsManager.userColor} style={{ opacity: '50%' }} size="sm" /> + <FontAwesomeIcon icon="chevron-left" color={SnappingManager.userColor} style={{ opacity: '50%' }} size="sm" /> </div> <div className="mainView-innerContainer" style={{ width: `calc(100% - ${width}px)` }}> {this.dockingContent} @@ -864,11 +855,11 @@ export class MainView extends ObservableReactComponent<{}> { className={`mainView-propertiesDragger${this.propertiesWidth() < 10 ? '-minified' : ''}`} key="props" onPointerDown={this.onPropertiesPointerDown} - style={{ background: SettingsManager.userVariantColor, right: this.propertiesWidth() - 1 }}> - <FontAwesomeIcon icon={this.propertiesWidth() < 10 ? 'chevron-left' : 'chevron-right'} color={SettingsManager.userColor} size="sm" /> + style={{ background: SnappingManager.userVariantColor, right: this.propertiesWidth() - 1 }}> + <FontAwesomeIcon icon={this.propertiesWidth() < 10 ? 'chevron-left' : 'chevron-right'} color={SnappingManager.userColor} size="sm" /> </div> )} - <div className="properties-container" style={{ width: this.propertiesWidth(), color: SettingsManager.userColor }}> + <div className="properties-container" style={{ width: this.propertiesWidth(), color: SnappingManager.userColor }}> <div style={{ display: this.propertiesWidth() < 10 ? 'none' : undefined }}> <PropertiesView styleProvider={DefaultStyleProvider} addDocTab={DocumentViewInternal.addDocTabFunc} width={this.propertiesWidth()} height={this.propertiesHeight()} /> </div> @@ -911,11 +902,11 @@ export class MainView extends ObservableReactComponent<{}> { // setTimeout(action(() => (this._leftMenuFlyoutWidth += 0.5))); this._sidebarContent.proto = DocCast(button.target); - SettingsManager.Instance.LastPressedBtn = button; + SnappingManager.SetLastPressedBtn(button[Id]); }); closeFlyout = action(() => { - SettingsManager.Instance.LastPressedBtn = undefined; + SnappingManager.SetLastPressedBtn(''); this._panelContent = 'none'; this._sidebarContent.proto = undefined; this._leftMenuFlyoutWidth = 0; @@ -933,7 +924,7 @@ export class MainView extends ObservableReactComponent<{}> { @computed get docButtons() { return !Doc.MyDockedBtns ? null : ( - <div className="mainView-docButtons" style={{ background: SettingsManager.userBackgroundColor, color: SettingsManager.userColor }} ref={this._docBtnRef}> + <div className="mainView-docButtons" style={{ background: SnappingManager.userBackgroundColor, color: SnappingManager.userColor }} ref={this._docBtnRef}> <CollectionLinearView Document={Doc.MyDockedBtns} docViewPath={returnEmptyDocViewList} @@ -964,8 +955,8 @@ export class MainView extends ObservableReactComponent<{}> { ); } @computed get snapLines() { - const dragged = DragManager.docsBeingDragged.lastElement() ?? SelectionManager.Docs.lastElement(); - const dragPar = dragged ? CollectionFreeFormView.from(DocumentManager.Instance.getDocumentView(dragged)) : undefined; + const dragged = DragManager.docsBeingDragged.lastElement() ?? DocumentView.SelectedDocs().lastElement(); + const dragPar = dragged ? CollectionFreeFormView.from(Array.from(dragged[DocViews]).lastElement()) : undefined; return !dragPar?.layoutDoc.freeform_snapLines ? null : ( <div className="mainView-snapLines"> <svg style={{ width: '100%', height: '100%' }}> @@ -1010,14 +1001,22 @@ export class MainView extends ObservableReactComponent<{}> { ); } + togglePropertiesFlyout = () => { + if (MainView.Instance.propertiesWidth() > 0) { + SnappingManager.SetPropertiesWidth(0); + } else { + SnappingManager.SetPropertiesWidth(300); + } + }; + lightboxMaxBorder = [200, 50]; render() { return ( <div className="mainView-container" style={{ - color: SettingsManager.userColor, - background: SettingsManager.userBackgroundColor, + color: SnappingManager.userColor, + background: SnappingManager.userBackgroundColor, }} onScroll={() => (ele => { @@ -1067,7 +1066,7 @@ export class MainView extends ObservableReactComponent<{}> { case 'dashboard': default: return (<> <div key="dashdiv" style={{ position: 'relative', display: this._hideUI || LightboxView.LightboxDoc ? 'none' : undefined, zIndex: 2001 }}> - <CollectionMenu panelWidth={this.topMenuWidth} panelHeight={this.topMenuHeight} toggleTopBar={this.toggleTopBar} topBarHeight={this.headerBarHeightFunc}/> + <CollectionMenu panelWidth={this.topMenuWidth} panelHeight={this.topMenuHeight} togglePropertiesFlyout={this.togglePropertiesFlyout} toggleTopBar={this.toggleTopBar} topBarHeight={this.headerBarHeightFunc}/> </div> {this.mainDashboardArea} </> ); @@ -1086,7 +1085,7 @@ export class MainView extends ObservableReactComponent<{}> { {/* <InkTranscription /> */} {this.snapLines} <LightboxView key="lightbox" PanelWidth={this._windowWidth} PanelHeight={this._windowHeight} maxBorder={this.lightboxMaxBorder} /> - <OverlayView /> + {LightboxView.LightboxDoc ? null : <OverlayView />} <GPTPopup key="gptpopup" /> <SchemaCSVPopUp key="schemacsvpopup" /> <GenerativeFill imageEditorOpen={ImageEditor.Open} imageEditorSource={ImageEditor.Source} imageRootDoc={ImageEditor.RootDoc} addDoc={ImageEditor.AddDoc} /> diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx index 0f13de9e5..a917a7d57 100644 --- a/src/client/views/MarqueeAnnotator.tsx +++ b/src/client/views/MarqueeAnnotator.tsx @@ -227,7 +227,6 @@ export class MarqueeAnnotator extends ObservableReactComponent<MarqueeAnnotatorP const linkDocData = dragEx.linkDocument[DocData]; linkDocData.link_relationship = 'cropped image'; linkDocData.title = 'crop: ' + this.props.Document.title; - linkDocData.link_displayLine = false; } }, }); diff --git a/src/client/views/OverlayView.tsx b/src/client/views/OverlayView.tsx index e17da2680..a7907a565 100644 --- a/src/client/views/OverlayView.tsx +++ b/src/client/views/OverlayView.tsx @@ -13,7 +13,6 @@ import { DocumentType } from '../documents/DocumentTypes'; import { DragManager } from '../util/DragManager'; import { dropActionType } from '../util/DropActionTypes'; import { Transform } from '../util/Transform'; -import { LightboxView } from './LightboxView'; import { ObservableReactComponent } from './ObservableReactComponent'; import './OverlayView.scss'; import { DefaultStyleProvider } from './StyleProvider'; @@ -94,7 +93,7 @@ export class OverlayWindow extends ObservableReactComponent<OverlayWindowProps> }; render() { - return LightboxView.LightboxDoc ? null : ( + return ( <div className="overlayWindow-outerDiv" style={{ transform: `translate(${this.x}px, ${this.y}px)`, width: this.width, height: this.height }}> <div className="overlayWindow-titleBar" onPointerDown={this.onPointerDown}> {this._props.overlayOptions.title || 'Untitled'} @@ -181,7 +180,7 @@ export class OverlayView extends ObservableReactComponent<{}> { docScreenToLocalXf = computedFn((doc: Doc) => () => new Transform(-NumCast(doc.overlayX), -NumCast(doc.overlayY), 1)); @computed get overlayDocs() { - return Doc.MyOverlayDocs.filter(d => !LightboxView.LightboxDoc || d.type === DocumentType.PRES).map(d => { + return Doc.MyOverlayDocs.map(d => { let [offsetx, offsety] = [0, 0]; const dref = React.createRef<HTMLDivElement>(); const onPointerMove = action((e: PointerEvent, down: number[]) => { diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx index cbbf48c75..edf6df2b9 100644 --- a/src/client/views/PropertiesButtons.tsx +++ b/src/client/views/PropertiesButtons.tsx @@ -20,11 +20,8 @@ import { DocData } from '../../fields/DocSymbols'; import { ScriptField } from '../../fields/ScriptField'; import { BoolCast, ScriptCast } from '../../fields/Types'; import { ImageField } from '../../fields/URLField'; -import { DocUtils } from '../documents/DocUtils'; +import { DocUtils, IsFollowLinkScript } from '../documents/DocUtils'; import { CollectionViewType, DocumentType } from '../documents/DocumentTypes'; -import { IsFollowLinkScript } from '../util/LinkFollower'; -import { LinkManager } from '../util/LinkManager'; -import { SelectionManager } from '../util/SelectionManager'; import { SettingsManager } from '../util/SettingsManager'; import { undoBatch, undoable } from '../util/UndoManager'; import { InkingStroke } from './InkingStroke'; @@ -40,13 +37,13 @@ export class PropertiesButtons extends React.Component<{}, {}> { @observable public static Instance: PropertiesButtons; @computed get selectedDoc() { - return SelectionManager.SelectedSchemaDoc || SelectionManager.Views.lastElement()?.Document; + return DocumentView.SelectedSchemaDoc() || DocumentView.Selected().lastElement()?.Document; } @computed get selectedLayoutDoc() { - return SelectionManager.SelectedSchemaDoc || SelectionManager.Views.lastElement()?.layoutDoc; + return DocumentView.SelectedSchemaDoc() || DocumentView.Selected().lastElement()?.layoutDoc; } @computed get selectedTabView() { - return !SelectionManager.SelectedSchemaDoc && SelectionManager.Views.lastElement()?.topMost; + return !DocumentView.SelectedSchemaDoc() && DocumentView.Selected().lastElement()?.topMost; } @computed get titleButton() { @@ -164,7 +161,7 @@ export class PropertiesButtons extends React.Component<{}, {}> { // on => `${on ? 'Set' : 'Remove'} lightbox flag`, // on => 'window-restore', // onClick => { - // SelectionManager.Views.forEach(dv => { + // DocumentView.Selected().forEach(dv => { // const containerDoc = dv.Document; // //containerDoc.followAllLinks = // // containerDoc.noShadow = @@ -320,14 +317,14 @@ export class PropertiesButtons extends React.Component<{}, {}> { @undoBatch handlePerspectiveChange = (e: any) => { this.selectedDoc && (this.selectedDoc._type_collection = e.target.value); - SelectionManager.Views.forEach(docView => { + DocumentView.Selected().forEach(docView => { docView.layoutDoc._type_collection = e.target.value; }); }; @computed get onClickVal() { const linkButton = IsFollowLinkScript(this.selectedDoc.onClick); const followLoc = this.selectedDoc._followLinkLocation; - const linkedToLightboxView = () => LinkManager.Links(this.selectedDoc).some(link => LinkManager.getOppositeAnchor(link, this.selectedDoc)?._isLightbox); + const linkedToLightboxView = () => Doc.Links(this.selectedDoc).some(link => Doc.getOppositeAnchor(link, this.selectedDoc)?._isLightbox); if (followLoc === OpenWhere.lightbox && !linkedToLightboxView()) return 'linkInPlace'; if (linkButton && followLoc === OpenWhere.addRight) return 'linkOnRight'; @@ -381,7 +378,7 @@ export class PropertiesButtons extends React.Component<{}, {}> { @undoBatch @action handleOptionChange = (onClick: string) => { - SelectionManager.Views.forEach(docView => { + DocumentView.Selected().forEach(docView => { const linkButton = IsFollowLinkScript(docView.Document.onClick); docView.noOnClick(); switch (onClick) { @@ -417,7 +414,7 @@ export class PropertiesButtons extends React.Component<{}, {}> { const click = () => this.handleOptionChange(value[0]); const linkButton = IsFollowLinkScript(this.selectedDoc.onClick); const followLoc = this.selectedDoc._followLinkLocation; - const linkedToLightboxView = () => LinkManager.Links(this.selectedDoc).some(link => LinkManager.getOppositeAnchor(link, this.selectedDoc)?._isLightbox); + const linkedToLightboxView = () => Doc.Links(this.selectedDoc).some(link => Doc.getOppositeAnchor(link, this.selectedDoc)?._isLightbox); let active = false; // prettier-ignore @@ -451,7 +448,7 @@ export class PropertiesButtons extends React.Component<{}, {}> { } @undoBatch editOnClickScript = () => { - if (SelectionManager.Views.length) SelectionManager.Views.forEach(dv => DocUtils.makeCustomViewClicked(dv.Document, undefined, 'onClick')); + if (DocumentView.Selected().length) DocumentView.Selected().forEach(dv => DocUtils.makeCustomViewClicked(dv.Document, undefined, 'onClick')); else this.selectedDoc && DocUtils.makeCustomViewClicked(this.selectedDoc, undefined, 'onClick'); }; @@ -472,8 +469,8 @@ export class PropertiesButtons extends React.Component<{}, {}> { fillWidth toggleType={ToggleType.BUTTON} onClick={undoable(() => { - if (SelectionManager.Views.length > 1) { - SelectionManager.Views.forEach(dv => (onClick ?? onPropToggle)(dv, dv.Document, property)); + if (DocumentView.Selected().length > 1) { + DocumentView.Selected().forEach(dv => (onClick ?? onPropToggle)(dv, dv.Document, property)); } else if (targetDoc) (onClick ?? onPropToggle)(undefined, targetDoc, property); }, property)} /> @@ -482,7 +479,7 @@ export class PropertiesButtons extends React.Component<{}, {}> { render() { const layoutField = this.selectedDoc?.[Doc.LayoutFieldKey(this.selectedDoc)]; - const isText = SelectionManager.Views.lastElement()?.ComponentView instanceof FormattedTextBox; + const isText = DocumentView.Selected().lastElement()?.ComponentView instanceof FormattedTextBox; const isInk = this.selectedDoc?.layout_isSvg; const isImage = layoutField instanceof ImageField; const isMap = this.selectedDoc?.type === DocumentType.MAP; diff --git a/src/client/views/PropertiesDocBacklinksSelector.tsx b/src/client/views/PropertiesDocBacklinksSelector.tsx index a806995f2..edb55f341 100644 --- a/src/client/views/PropertiesDocBacklinksSelector.tsx +++ b/src/client/views/PropertiesDocBacklinksSelector.tsx @@ -7,12 +7,12 @@ import { Doc } from '../../fields/Doc'; import { Cast } from '../../fields/Types'; import { DocumentType } from '../documents/DocumentTypes'; import { LinkManager } from '../util/LinkManager'; -import { SelectionManager } from '../util/SelectionManager'; import { SettingsManager } from '../util/SettingsManager'; import './PropertiesDocBacklinksSelector.scss'; import { CollectionDockingView } from './collections/CollectionDockingView'; import { LinkMenu } from './linking/LinkMenu'; import { OpenWhere } from './nodes/OpenWhere'; +import { DocumentView } from './nodes/DocumentView'; type PropertiesDocBacklinksSelectorProps = { Document: Doc; @@ -25,7 +25,7 @@ type PropertiesDocBacklinksSelectorProps = { export class PropertiesDocBacklinksSelector extends React.Component<PropertiesDocBacklinksSelectorProps> { getOnClick = action((link: Doc) => { const linkAnchor = this.props.Document; - const other = LinkManager.getOppositeAnchor(link, linkAnchor); + const other = Doc.getOppositeAnchor(link, linkAnchor); const otherdoc = !other ? undefined : other.annotationOn && other.type !== DocumentType.RTF ? Cast(other.annotationOn, Doc, null) : other; LinkManager.Instance.currentLink = link; if (otherdoc) { @@ -36,10 +36,10 @@ export class PropertiesDocBacklinksSelector extends React.Component<PropertiesDo }); render() { - return !SelectionManager.Views.length ? null : ( + return !DocumentView.Selected().length ? null : ( <div className="propertiesDocBacklinksSelector" style={{ color: SettingsManager.userColor, backgroundColor: SettingsManager.userBackgroundColor }}> {this.props.hideTitle ? null : <p key="contexts">Contexts:</p>} - <LinkMenu docView={SelectionManager.Views.lastElement()} clearLinkEditor={undefined} itemHandler={this.getOnClick} style={{ left: 0, top: 0 }} /> + <LinkMenu docView={DocumentView.Selected().lastElement()} clearLinkEditor={undefined} itemHandler={this.getOnClick} style={{ left: 0, top: 0 }} /> </div> ); } diff --git a/src/client/views/PropertiesDocContextSelector.tsx b/src/client/views/PropertiesDocContextSelector.tsx index 722b1f90a..7465c727a 100644 --- a/src/client/views/PropertiesDocContextSelector.tsx +++ b/src/client/views/PropertiesDocContextSelector.tsx @@ -7,11 +7,10 @@ import * as React from 'react'; import { Doc } from '../../fields/Doc'; import { Id } from '../../fields/FieldSymbols'; import { Cast, StrCast } from '../../fields/Types'; -import { DocFocusOrOpen } from '../util/DocumentManager'; import { ObservableReactComponent } from './ObservableReactComponent'; import './PropertiesDocContextSelector.scss'; import { CollectionDockingView } from './collections/CollectionDockingView'; -import { DocumentView } from './nodes/DocumentView'; +import { DocFocusOrOpen, DocumentView } from './nodes/DocumentView'; import { OpenWhere } from './nodes/OpenWhere'; type PropertiesDocContextSelectorProps = { diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx index ff5dcd1b8..7c16e0ddb 100644 --- a/src/client/views/PropertiesView.tsx +++ b/src/client/views/PropertiesView.tsx @@ -23,10 +23,8 @@ import { ComputedField } from '../../fields/ScriptField'; import { Cast, DocCast, NumCast, StrCast } from '../../fields/Types'; import { GetEffectiveAcl, SharingPermissions, normalizeEmail } from '../../fields/util'; import { CollectionViewType, DocumentType } from '../documents/DocumentTypes'; -import { DocumentManager } from '../util/DocumentManager'; import { GroupManager } from '../util/GroupManager'; import { LinkManager } from '../util/LinkManager'; -import { SelectionManager } from '../util/SelectionManager'; import { SettingsManager } from '../util/SettingsManager'; import { SharingManager } from '../util/SharingManager'; import { Transform } from '../util/Transform'; @@ -40,7 +38,7 @@ import { PropertiesDocBacklinksSelector } from './PropertiesDocBacklinksSelector import { PropertiesDocContextSelector } from './PropertiesDocContextSelector'; import { PropertiesSection } from './PropertiesSection'; import './PropertiesView.scss'; -import { DefaultStyleProvider } from './StyleProvider'; +import { DefaultStyleProvider, SetFilterOpener as SetPropertiesFilterOpener } from './StyleProvider'; import { DocumentView } from './nodes/DocumentView'; import { StyleProviderFuncType } from './nodes/FieldView'; import { KeyValueBox } from './nodes/KeyValueBox'; @@ -66,6 +64,12 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps super(props); makeObservable(this); PropertiesView.Instance = this; + SetPropertiesFilterOpener( + action(() => { + this.CloseAll(); + this.openFilters = true; + }) + ); } @computed get MAX_EMBED_HEIGHT() { @@ -73,7 +77,7 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps } @computed get selectedDoc() { - return SelectionManager.SelectedSchemaDoc || this.selectedDocumentView?.Document || Doc.ActiveDashboard; + return DocumentView.SelectedSchemaDoc() || this.selectedDocumentView?.Document || Doc.ActiveDashboard; } @computed get selectedLink() { @@ -81,12 +85,10 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps } @computed get selectedLayoutDoc() { - return SelectionManager.SelectedSchemaDoc || this.selectedDocumentView?.layoutDoc || Doc.ActiveDashboard; + return DocumentView.SelectedSchemaDoc() || this.selectedDocumentView?.layoutDoc || Doc.ActiveDashboard; } @computed get selectedDocumentView() { - if (SelectionManager.Views.length) return SelectionManager.Views[0]; - if (PresBox.Instance?.selectedArray.size) return DocumentManager.Instance.getDocumentView(PresBox.Instance.Document); - return undefined; + return DocumentView.Selected().lastElement(); } @computed get isPres(): boolean { return this.selectedDoc?.type === DocumentType.PRES; @@ -182,7 +184,10 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps const rows: JSX.Element[] = []; if (this.dataDoc && this.selectedDoc) { const ids = new Set<string>(reqdKeys); - const docs: Doc[] = SelectionManager.Views.length < 2 ? [this.layoutFields ? Doc.Layout(this.selectedDoc) : this.dataDoc] : SelectionManager.Views.map(dv => (this.layoutFields ? dv.layoutDoc : dv.dataDoc)); + const docs: Doc[] = + DocumentView.Selected().length < 2 // + ? [this.layoutFields ? Doc.Layout(this.selectedDoc) : this.dataDoc] + : DocumentView.Selected().map(dv => (this.layoutFields ? dv.layoutDoc : dv.dataDoc)); docs.forEach(doc => Object.keys(doc) .filter(filter) @@ -237,7 +242,12 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps @undoBatch setKeyValue = (value: string) => { - const docs = SelectionManager.Views.length < 2 && this.selectedDoc ? [this.layoutFields ? Doc.Layout(this.selectedDoc) : this.dataDoc!] : SelectionManager.Views.map(dv => (this.layoutFields ? dv.layoutDoc : dv.dataDoc)); + const docs = + DocumentView.Selected().length < 2 && this.selectedDoc + ? [this.layoutFields + ? Doc.Layout(this.selectedDoc) // + : this.dataDoc!] + : DocumentView.Selected().map(dv => (this.layoutFields ? dv.layoutDoc : dv.dataDoc)); // prettier-ignore docs.forEach(doc => { if (value.indexOf(':') !== -1) { const newVal = value[0].toUpperCase() + value.substring(1, value.length); @@ -295,13 +305,13 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps const selAnchor = this.selectedDocumentView?.anchorViewDoc ?? LinkManager.Instance.currentLinkAnchor ?? this.selectedDoc; let counter = 0; - LinkManager.Links(selAnchor).forEach(() => counter++); + Doc.Links(selAnchor).forEach(() => counter++); return counter; } @computed get layoutPreview() { - if (SelectionManager.Views.length > 1) { + if (DocumentView.Selected().length > 1) { return '-- multiple selected --'; } if (this.selectedDoc) { @@ -348,7 +358,7 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps */ @undoBatch changePermissions = (e: any, user: string) => { - const docs = SelectionManager.Views.length < 2 ? [this.selectedDoc] : SelectionManager.Views.map(dv => (this.layoutDocAcls ? dv.layoutDoc : dv.dataDoc)); + const docs = DocumentView.Selected().length < 2 ? [this.selectedDoc] : DocumentView.Selected().map(dv => (this.layoutDocAcls ? dv.layoutDoc : dv.dataDoc)); SharingManager.Instance.shareFromPropertiesSidebar(user, e.currentTarget.value as SharingPermissions, docs, this.layoutDocAcls); }; @@ -462,7 +472,7 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps */ @computed get sharingTable() { // all selected docs - const docs = SelectionManager.Views.length < 2 && this.selectedDoc ? [this.selectedDoc] : SelectionManager.Views.map(docView => docView.Document); + const docs = DocumentView.Selected().length < 2 && this.selectedDoc ? [this.selectedDoc] : DocumentView.SelectedDocs(); const target = docs[0]; const showAdmin = GetEffectiveAcl(target) === AclAdmin; @@ -580,7 +590,7 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps @computed get editableTitle() { const titles = new Set<string>(); - SelectionManager.Views.forEach(dv => titles.add(StrCast(dv.Document.title))); + DocumentView.Selected().forEach(dv => titles.add(StrCast(dv.Document.title))); const title = Array.from(titles.keys()).length > 1 ? '--multiple selected--' : StrCast(this.selectedDoc?.title); return ( <div> @@ -631,8 +641,8 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps @undoBatch setTitle = (value: string | number) => { - if (SelectionManager.Views.length > 1) { - SelectionManager.Views.map(dv => Doc.SetInPlace(dv.Document, 'title', value, true)); + if (DocumentView.Selected().length > 1) { + DocumentView.Selected().map(dv => Doc.SetInPlace(dv.Document, 'title', value, true)); } else if (this.dataDoc) { if (this.selectedDoc) Doc.SetInPlace(this.selectedDoc, 'title', value, true); else KeyValueBox.SetField(this.dataDoc, 'title', value as string, true); @@ -1365,14 +1375,14 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps @computed get destinationAnchor() { const ldoc = this.selectedLink; const lanch = this.selectedDocumentView?.anchorViewDoc ?? LinkManager.Instance.currentLinkAnchor; - if (ldoc && lanch) return LinkManager.getOppositeAnchor(ldoc, lanch) ?? lanch; + if (ldoc && lanch) return Doc.getOppositeAnchor(ldoc, lanch) ?? lanch; return ldoc ? DocCast(ldoc.link_anchor_2) : ldoc; } @computed get sourceAnchor() { const selAnchor = this.selectedDocumentView?.anchorViewDoc ?? LinkManager.Instance.currentLinkAnchor; - return selAnchor ?? (this.selectedLink && this.destinationAnchor ? LinkManager.getOppositeAnchor(this.selectedLink, this.destinationAnchor) : this.selectedLink); + return selAnchor ?? (this.selectedLink && this.destinationAnchor ? Doc.getOppositeAnchor(this.selectedLink, this.destinationAnchor) : this.selectedLink); } toggleAnchorProp = (e: React.PointerEvent, prop: string, anchor?: Doc, value: any = true, ovalue: any = false, cb: (val: any) => any = val => val) => { @@ -1435,7 +1445,7 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps const zoom = Number((NumCast(this.sourceAnchor?.followLinkZoomScale, 1) * 100).toPrecision(3)); const targZoom = this.sourceAnchor?.followLinkZoom; const indent = 30; - const hasSelectedAnchor = this.selectedLink !== this.selectedDoc && LinkManager.Links(this.sourceAnchor).includes(this.selectedLink!); + const hasSelectedAnchor = this.selectedLink !== this.selectedDoc && Doc.Links(this.sourceAnchor).includes(this.selectedLink!); return ( <> diff --git a/src/client/views/ScriptBox.tsx b/src/client/views/ScriptBox.tsx index 8de359e07..9c36e6d26 100644 --- a/src/client/views/ScriptBox.tsx +++ b/src/client/views/ScriptBox.tsx @@ -122,14 +122,14 @@ export class ScriptBox extends React.Component<ScriptBoxProps> { if (!text) { doc[DocData][fieldKey] = undefined; } else { - const script = CompileScript(text, { + const compScript = CompileScript(text, { params: { this: Doc.name, ...contextParams }, typecheck: false, editable: true, transformer: DocumentIconContainer.getTransformer(), }); - if (!script.compiled) { - onError(script.errors.map(error => error.messageText).join('\n')); + if (!compScript.compiled) { + onError(compScript.errors.map(error => error.messageText).join('\n')); return; } @@ -143,7 +143,7 @@ export class ScriptBox extends React.Component<ScriptBoxProps> { div.innerHTML = 'button'; params.length && DragManager.StartButtonDrag([div], text, doc.title + '-instance', {}, params, () => {}, clientX, clientY); - doc[DocData][fieldKey] = new ScriptField(script); + doc[DocData][fieldKey] = new ScriptField(compScript); overlayDisposer(); } }} diff --git a/src/client/views/ScriptingRepl.tsx b/src/client/views/ScriptingRepl.tsx index ba2e22b3b..1a2eb460f 100644 --- a/src/client/views/ScriptingRepl.tsx +++ b/src/client/views/ScriptingRepl.tsx @@ -5,15 +5,15 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { action, makeObservable, observable } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; -import { DocumentManager } from '../util/DocumentManager'; import { CompileScript, Transformer, ts } from '../util/Scripting'; import { ScriptingGlobals } from '../util/ScriptingGlobals'; -import { SettingsManager } from '../util/SettingsManager'; +import { SnappingManager } from '../util/SnappingManager'; import { undoable } from '../util/UndoManager'; import { ObservableReactComponent } from './ObservableReactComponent'; import { OverlayView } from './OverlayView'; import './ScriptingRepl.scss'; import { DocumentIconContainer } from './nodes/DocumentIcon'; +import { DocumentView } from './nodes/DocumentView'; interface replValueProps { scrollToBottom: () => void; @@ -169,7 +169,7 @@ export class ScriptingRepl extends ObservableReactComponent<{}> { case 'Enter': { e.stopPropagation(); const docGlobals: { [name: string]: any } = {}; - DocumentManager.Instance.DocumentViews.forEach((dv, i) => { + DocumentView.allViews().forEach((dv, i) => { docGlobals[`d${i}`] = dv.Document; }); const globals = ScriptingGlobals.makeMutableGlobalsCopy(docGlobals); @@ -262,13 +262,13 @@ export class ScriptingRepl extends ObservableReactComponent<{}> { render() { return ( <div className="scriptingRepl-outerContainer"> - <div className="scriptingRepl-commandsContainer" style={{ background: SettingsManager.userBackgroundColor }} ref={this.commandsRef}> + <div className="scriptingRepl-commandsContainer" style={{ background: SnappingManager.userBackgroundColor }} ref={this.commandsRef}> {this.commands.map(({ command, result }, i) => ( - <div className="scriptingRepl-resultContainer" style={{ background: SettingsManager.userBackgroundColor }} key={i}> - <div className="scriptingRepl-commandString" style={{ background: SettingsManager.userBackgroundColor }}> + <div className="scriptingRepl-resultContainer" style={{ background: SnappingManager.userBackgroundColor }} key={i}> + <div className="scriptingRepl-commandString" style={{ background: SnappingManager.userBackgroundColor }}> {command || <br />} </div> - <div className="scriptingRepl-commandResult" style={{ background: SettingsManager.userBackgroundColor }}> + <div className="scriptingRepl-commandResult" style={{ background: SnappingManager.userBackgroundColor }}> <ScriptingValueDisplay scrollToBottom={this.maybeScrollToBottom} value={result} /> </div> </div> @@ -276,7 +276,7 @@ export class ScriptingRepl extends ObservableReactComponent<{}> { </div> <input className="scriptingRepl-commandInput" - style={{ background: SettingsManager.userBackgroundColor }} // + style={{ background: SnappingManager.userBackgroundColor }} // onFocus={this.onFocus} onBlur={this.onBlur} value={this.commandString} diff --git a/src/client/views/SidebarAnnos.tsx b/src/client/views/SidebarAnnos.tsx index 3648e613d..9b70f1ca7 100644 --- a/src/client/views/SidebarAnnos.tsx +++ b/src/client/views/SidebarAnnos.tsx @@ -14,12 +14,11 @@ import { DocCast, NumCast, StrCast } from '../../fields/Types'; import { CollectionViewType, DocumentType } from '../documents/DocumentTypes'; import { Docs } from '../documents/Documents'; import { DocUtils } from '../documents/DocUtils'; -import { LinkManager } from '../util/LinkManager'; import { SearchUtil } from '../util/SearchUtil'; import { Transform } from '../util/Transform'; import { ObservableReactComponent } from './ObservableReactComponent'; import './SidebarAnnos.scss'; -import { StyleProp } from './StyleProvider'; +import { StyleProp } from './StyleProp'; import { CollectionStackingView } from './collections/CollectionStackingView'; import { FieldViewProps } from './nodes/FieldView'; import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox'; @@ -87,7 +86,6 @@ export class SidebarAnnos extends ObservableReactComponent<FieldViewProps & Extr Doc.SetSelectOnLoad(target); FormattedTextBox.DontSelectInitialText = true; const link = DocUtils.MakeLink(anchor, target, { link_relationship: 'inline comment:comment on' }); - link && (link.link_displayLine = false); const taggedContent = this.childFilters() .filter(data => data.split(':')[0]) @@ -187,8 +185,8 @@ export class SidebarAnnos extends ObservableReactComponent<FieldViewProps & Extr layout_showTitle = () => 'title'; setHeightCallback = (height: number) => this._props.setHeight?.(height + this.filtersHeight()); sortByLinkAnchorY = (a: Doc, b: Doc) => { - const ay = LinkManager.Links(a).length && DocCast(LinkManager.Links(a)[0].link_anchor_1).y; - const by = LinkManager.Links(b).length && DocCast(LinkManager.Links(b)[0].link_anchor_1).y; + const ay = Doc.Links(a).length && DocCast(Doc.Links(a)[0].link_anchor_1).y; + const by = Doc.Links(b).length && DocCast(Doc.Links(b)[0].link_anchor_1).y; return NumCast(ay) - NumCast(by); }; render() { diff --git a/src/client/views/StyleProp.ts b/src/client/views/StyleProp.ts new file mode 100644 index 000000000..dd5b98cfe --- /dev/null +++ b/src/client/views/StyleProp.ts @@ -0,0 +1,24 @@ +export enum StyleProp { + TreeViewIcon = 'treeView_Icon', + TreeViewSortings = 'treeView_Sortings', // options for how to sort tree view items + DocContents = 'docContents', // when specified, the JSX returned will replace the normal rendering of the document view + Opacity = 'opacity', // opacity of the document view + BoxShadow = 'boxShadow', // box shadow - used for making collections standout and for showing clusters in free form views + BorderRounding = 'borderRounding', // border radius of the document view + Color = 'color', // foreground color of Document view items + BackgroundColor = 'backgroundColor', // background color of a document view + FillColor = 'fillColor', // fill color of an ink stroke or shape + WidgetColor = 'widgetColor', // color to display UI widgets on a document view -- used for the sidebar divider dragger on a text note + PointerEvents = 'pointerEvents', // pointer events for DocumentView -- inherits pointer events if not specified + Decorations = 'decorations', // additional decoration to display above a DocumentView -- currently only used to display a Lock for making things background + HeaderMargin = 'headerMargin', // margin at top of documentview, typically for displaying a title -- doc contents will start below that + ShowCaption = 'layout_showCaption', + TitleHeight = 'titleHeight', // Height of Title area + ShowTitle = 'layout_showTitle', // whether to display a title on a Document (optional :hover suffix) + BorderPath = 'customBorder', // border path for document view + FontColor = 'fontColor', // color o tet + FontSize = 'fontSize', // size of text font + FontFamily = 'fontFamily', // font family of text + FontWeight = 'fontWeight', // font weight of text + Highlighting = 'highlighting', // border highlighting +} diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx index 6d4f69e6e..3ecfc8ba3 100644 --- a/src/client/views/StyleProvider.tsx +++ b/src/client/views/StyleProvider.tsx @@ -13,53 +13,20 @@ import { FaFilter } from 'react-icons/fa'; import { ClientUtils, DashColor, lightOrDark } from '../../ClientUtils'; import { Doc, Opt, StrListCast } from '../../fields/Doc'; import { DocViews } from '../../fields/DocSymbols'; +import { Id } from '../../fields/FieldSymbols'; +import { ScriptField } from '../../fields/ScriptField'; import { BoolCast, Cast, DocCast, ImageCast, NumCast, ScriptCast, StrCast } from '../../fields/Types'; +import { AudioAnnoState } from '../../server/SharedMediaTypes'; import { CollectionViewType, DocumentType } from '../documents/DocumentTypes'; -import { DocFocusOrOpen, DocumentManager } from '../util/DocumentManager'; -import { IsFollowLinkScript } from '../util/LinkFollower'; -import { LinkManager } from '../util/LinkManager'; -import { SettingsManager } from '../util/SettingsManager'; +import { IsFollowLinkScript } from '../documents/DocUtils'; import { SnappingManager } from '../util/SnappingManager'; import { undoBatch, UndoManager } from '../util/UndoManager'; -import { CollectionSchemaView } from './collections/collectionSchema/CollectionSchemaView'; import { TreeSort } from './collections/TreeSort'; import { Colors } from './global/globalEnums'; -import { DocumentViewProps } from './nodes/DocumentView'; +import { DocFocusOrOpen, DocumentView, DocumentViewProps } from './nodes/DocumentView'; import { FieldViewProps } from './nodes/FieldView'; -import { KeyValueBox } from './nodes/KeyValueBox'; -import { PropertiesView } from './PropertiesView'; +import { StyleProp } from './StyleProp'; import './StyleProvider.scss'; -import { ScriptField } from '../../fields/ScriptField'; - -export enum StyleProp { - TreeViewIcon = 'treeView_Icon', - TreeViewSortings = 'treeView_Sortings', // options for how to sort tree view items - DocContents = 'docContents', // when specified, the JSX returned will replace the normal rendering of the document view - Opacity = 'opacity', // opacity of the document view - BoxShadow = 'boxShadow', // box shadow - used for making collections standout and for showing clusters in free form views - BorderRounding = 'borderRounding', // border radius of the document view - Color = 'color', // foreground color of Document view items - BackgroundColor = 'backgroundColor', // background color of a document view - FillColor = 'fillColor', // fill color of an ink stroke or shape - WidgetColor = 'widgetColor', // color to display UI widgets on a document view -- used for the sidebar divider dragger on a text note - PointerEvents = 'pointerEvents', // pointer events for DocumentView -- inherits pointer events if not specified - Decorations = 'decorations', // additional decoration to display above a DocumentView -- currently only used to display a Lock for making things background - HeaderMargin = 'headerMargin', // margin at top of documentview, typically for displaying a title -- doc contents will start below that - ShowCaption = 'layout_showCaption', - TitleHeight = 'titleHeight', // Height of Title area - ShowTitle = 'layout_showTitle', // whether to display a title on a Document (optional :hover suffix) - BorderPath = 'customBorder', // border path for document view - FontColor = 'fontColor', // color o tet - FontSize = 'fontSize', // size of text font - FontFamily = 'fontFamily', // font family of text - FontWeight = 'fontWeight', // font weight of text - Highlighting = 'highlighting', // border highlighting -} - -export enum AudioAnnoState { - stopped = 'stopped', - playing = 'playing', -} function toggleLockedPosition(doc: Doc) { UndoManager.RunInBatch(() => Doc.toggleLockedPosition(doc), 'toggleBackground'); @@ -80,7 +47,7 @@ export function styleFromLayoutString(doc: Doc, props: FieldViewProps, scale: nu const divKeys = ['width', 'height', 'fontSize', 'transform', 'left', 'backgroundColor', 'left', 'right', 'top', 'bottom', 'pointerEvents', 'position']; const replacer = (match: any, expr: string) => // bcz: this executes a script to convert a property expression string: { script } into a value - ScriptField.MakeFunction(expr, { self: Doc.name, this: Doc.name, scale: 'number' })?.script.run({ this: doc, self: doc, scale }).result?.toString() ?? ''; + ScriptField.MakeFunction(expr, { this: Doc.name, scale: 'number' })?.script.run({ this: doc, scale }).result?.toString() ?? ''; divKeys.forEach((prop: string) => { const p = (props as any)[prop]; typeof p === 'string' && (style[prop] = p?.replace(/{([^.'][^}']+)}/g, replacer)); @@ -98,6 +65,11 @@ export function wavyBorderPath(pw: number, ph: number, inset: number = 0.05) { } ${ph * inset}`; } +let _filterOpener: () => void; +export function SetFilterOpener(func: () => void) { + _filterOpener = func; +} + // a preliminary implementation of a dash style sheet for setting rendering properties of documents nested within a Tab // export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps & DocumentViewProps>, property: string): any { @@ -124,7 +96,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps & childFiltersByRanges, renderDepth, docViewPath, - DocumentView, + DocumentView: docView, LayoutTemplateString, disableBrushing, NativeDimScaling, @@ -177,21 +149,20 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps & return undefined; case StyleProp.DocContents: return undefined; case StyleProp.WidgetColor: return isAnnotated ? Colors.LIGHT_BLUE : 'dimgrey'; - case StyleProp.Opacity: return LayoutTemplateString?.includes(KeyValueBox.name) ? 1 : Cast(doc?._opacity, "number", Cast(doc?.opacity, 'number', null)); + case StyleProp.Opacity: return docView?.().ComponentView?.isUnstyledView?.() ? 1 : Cast(doc?._opacity, "number", Cast(doc?.opacity, 'number', null)); case StyleProp.FontColor: return StrCast(doc?.[fieldKey + 'fontColor'], StrCast(Doc.UserDoc().fontColor, color())); case StyleProp.FontSize: return StrCast(doc?.[fieldKey + 'fontSize'], StrCast(Doc.UserDoc().fontSize)); case StyleProp.FontFamily: return StrCast(doc?.[fieldKey + 'fontFamily'], StrCast(Doc.UserDoc().fontFamily)); case StyleProp.FontWeight: return StrCast(doc?.[fieldKey + 'fontWeight'], StrCast(Doc.UserDoc().fontWeight)); case StyleProp.FillColor: return StrCast(doc?._fillColor, StrCast(doc?.fillColor, StrCast(doc?.backgroundColor, 'transparent'))); case StyleProp.ShowCaption: return hideCaptions || doc?._type_collection === CollectionViewType.Carousel ? undefined: StrCast(doc?._layout_showCaption); - case StyleProp.TitleHeight: return Math.min(4,(DocumentView?.().screenToViewTransform().Scale ?? 1)) * NumCast(Doc.UserDoc().headerHeight,30); + case StyleProp.TitleHeight: return Math.min(4,(docView?.().screenToViewTransform().Scale ?? 1)) * NumCast(Doc.UserDoc().headerHeight,30); case StyleProp.ShowTitle: return ( (doc && - !(DocumentView?.().ComponentView instanceof CollectionSchemaView) && + !docView?.().ComponentView?.isUnstyledView?.() && !LayoutTemplateString && !doc.presentation_targetDoc && - !LayoutTemplateString?.includes(KeyValueBox.name) && layout_showTitle?.() !== '' && StrCast( doc._layout_showTitle, @@ -205,9 +176,9 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps & '' ); case StyleProp.Color: { - if (SettingsManager.Instance.LastPressedBtn === doc) return SettingsManager.userBackgroundColor; - if (Doc.IsSystem(doc!)) return SettingsManager.userColor; - if (doc?.type === DocumentType.FONTICON) return SettingsManager.userColor; + if (SnappingManager.LastPressedBtn === doc?.[Id]) return SnappingManager.userBackgroundColor; + if (Doc.IsSystem(doc!)) return SnappingManager.userColor; + if (doc?.type === DocumentType.FONTICON) return SnappingManager.userColor; const docColor: Opt<string> = StrCast(doc?.[fieldKey + 'color'], StrCast(doc?._color)); if (docColor) return docColor; const backColor = backgroundCol(); @@ -243,7 +214,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps & ? titleHeight() : 0; case StyleProp.BackgroundColor: { - if (SettingsManager.Instance.LastPressedBtn === doc) return SettingsManager.userColor; // hack to indicate active menu panel item + if (SnappingManager.LastPressedBtn === doc?.[Id]) return SnappingManager.userColor; // hack to indicate active menu panel item const dataKey = doc ? Doc.LayoutFieldKey(doc) : ""; const usePath = StrCast(doc?.[dataKey + "_usePath"]); const alternate = usePath.includes(":hover") ? ( doc?.isHovering ? '_' + usePath.replace(":hover","") : "") : usePath ? "_" +usePath:usePath; @@ -268,7 +239,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps & case DocumentType.VID: docColor = docColor || (Colors.LIGHT_GRAY); break; case DocumentType.COL: docColor = docColor || (doc && Doc.IsSystem(doc) - ? SettingsManager.userBackgroundColor + ? SnappingManager.userBackgroundColor : doc?.annotationOn ? '#00000010' // faint interior for collections on PDFs, images, etc : doc?.isGroup @@ -288,7 +259,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps & case StyleProp.BoxShadow: { if (!doc || opacity() === 0 || doc.noShadow) return undefined; // if it's not visible, then no shadow) if (doc.layout_boxShadow === 'standard') return Shadows.STANDARD_SHADOW; - if (IsFollowLinkScript(doc?.onClick) && LinkManager.Links(doc).length && !layoutDoc?.layout_isSvg) return StrCast(doc?._linkButtonShadow, 'lightblue 0em 0em 1em'); + if (IsFollowLinkScript(doc?.onClick) && Doc.Links(doc).length && !layoutDoc?.layout_isSvg) return StrCast(doc?._linkButtonShadow, 'lightblue 0em 0em 1em'); switch (doc?.type) { case DocumentType.COL: return StrCast( @@ -320,8 +291,8 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps & } } case StyleProp.PointerEvents: - if (StrCast(doc?.pointerEvents) && !LayoutTemplateString?.includes(KeyValueBox.name)) return StrCast(doc!.pointerEvents); // honor pointerEvents field (set by lock button usually) if it's not a keyValue view of the Doc - if (LayoutTemplateString?.includes(KeyValueBox.name)) return 'all'; + if (docView?.().ComponentView?.dontRegisterView?.()) return 'all'; + if (StrCast(doc?.pointerEvents)) return StrCast(doc!.pointerEvents); // honor pointerEvents field (set by lock button usually) if it's not a keyValue view of the Doc if (SnappingManager.ExploreMode || doc?.layout_unrendered) return isInk() ? 'visiblePainted' : 'all'; if (pointerEvents?.() === 'none') return 'none'; if (opacity() === 0) return 'none'; @@ -336,12 +307,12 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps & </div> ); const paint = () => !doc?.onPaint ? null : ( - <div className={`styleProvider-paint${DocumentView?.().IsSelected ? "-selected":""}`} onClick={e => togglePaintView(e, doc, props)}> + <div className={`styleProvider-paint${docView?.().IsSelected ? "-selected":""}`} onClick={e => togglePaintView(e, doc, props)}> <FontAwesomeIcon icon='pen' size="lg" /> </div> ); const filter = () => { - const dashView = untracked(() => DocumentManager.Instance.getDocumentView(Doc.ActiveDashboard)); + const dashView = untracked(() => DocumentView.getDocumentView(Doc.ActiveDashboard)); const showFilterIcon = StrListCast(doc?._childFilters).length || StrListCast(doc?._childFiltersByRanges).length ? 'green' // #18c718bd' //'hasFilter' @@ -357,17 +328,11 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps & // eslint-disable-next-line react/no-unstable-nested-components iconProvider={() => <div className='styleProvider-filterShift'><FaFilter/></div>} closeOnSelect - setSelectedVal={ - action((dv) => { - (dv as any).select(false); - (SettingsManager.Instance.propertiesWidth = 250); - setTimeout(action(() => { - if (PropertiesView.Instance) { - PropertiesView.Instance.CloseAll(); - PropertiesView.Instance.openFilters = true; - } - })); - }) + setSelectedVal={((dv: DocumentView) => { + dv.select(false); + SnappingManager.SetPropertiesWidth(250); + _filterOpener?.(); + }) as any // Dropdown assumes values are strings or numbers.. } size={Size.XSMALL} width={15} @@ -375,14 +340,14 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps & title={showFilterIcon === 'green' ? "This view is filtered. Click to view/change filters": "this view inherits filters from one of its parents"} - color={SettingsManager.userColor} + color={SnappingManager.userColor} background={showFilterIcon} items={[ ...(dashView ? [dashView]: []), ...(docViewPath?.()??[])] .filter(dv => StrListCast(dv?.Document.childFilters).length || StrListCast(dv?.Document.childRangeFilters).length) .map(dv => ({ text: StrCast(dv?.Document.title), val: dv as any, - style: {color:SettingsManager.userColor, background:SettingsManager.userBackgroundColor}, + style: {color:SnappingManager.userColor, background:SnappingManager.userBackgroundColor}, } as IListItemProps)) } /> </div> @@ -395,7 +360,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps & const audioIconColors: { [key: string]: string } = { playing: 'green', stopped: 'blue' }; return ( <Tooltip title={<div>{StrListCast(doc[fieldKey + 'audioAnnotations_text']).lastElement()}</div>}> - <div className="styleProvider-audio" onPointerDown={() => DocumentManager.Instance.getFirstDocumentView(doc)?.playAnnotation()}> + <div className="styleProvider-audio" onPointerDown={() => DocumentView.getFirstDocumentView(doc)?.playAnnotation()}> <FontAwesomeIcon className="documentView-audioFont" style={{ color: audioIconColors[audioAnnoState(doc)] }} icon='file-audio' size="sm" /> </div> </Tooltip> @@ -415,7 +380,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps & } export function DashboardToggleButton(doc: Doc, field: string, onIcon: IconProp, offIcon: IconProp, clickFunc?: () => void) { - const color = SettingsManager.userColor; + const color = SnappingManager.userColor; return ( <IconButton size={Size.XSMALL} diff --git a/src/client/views/collections/CollectionCarousel3DView.tsx b/src/client/views/collections/CollectionCarousel3DView.tsx index d8232c132..7617f2a52 100644 --- a/src/client/views/collections/CollectionCarousel3DView.tsx +++ b/src/client/views/collections/CollectionCarousel3DView.tsx @@ -11,8 +11,7 @@ import { Id } from '../../../fields/FieldSymbols'; import { DocCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types'; import { DocumentType } from '../../documents/DocumentTypes'; import { DragManager } from '../../util/DragManager'; -import { SelectionManager } from '../../util/SelectionManager'; -import { StyleProp } from '../StyleProvider'; +import { StyleProp } from '../StyleProp'; import { DocumentView } from '../nodes/DocumentView'; import { FocusViewOptions } from '../nodes/FocusViewOptions'; import './CollectionCarousel3DView.scss'; @@ -101,7 +100,7 @@ export class CollectionCarousel3DView extends CollectionSubView() { } changeSlide = (direction: number) => { - SelectionManager.DeselectAll(); + DocumentView.DeselectAll(); this.layoutDoc._carousel_index = (NumCast(this.layoutDoc._carousel_index) + direction + this.carouselItems.length) % this.carouselItems.length; }; diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx index fda320077..9d3657995 100644 --- a/src/client/views/collections/CollectionCarouselView.tsx +++ b/src/client/views/collections/CollectionCarouselView.tsx @@ -11,7 +11,7 @@ import { Doc, Opt } from '../../../fields/Doc'; import { DocCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types'; import { DocumentType } from '../../documents/DocumentTypes'; import { DragManager } from '../../util/DragManager'; -import { StyleProp } from '../StyleProvider'; +import { StyleProp } from '../StyleProp'; import { DocumentView } from '../nodes/DocumentView'; import { FieldViewProps } from '../nodes/FieldView'; import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox'; diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index b98aceb16..fc9e2e39b 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -16,27 +16,33 @@ import { DocServer } from '../../DocServer'; import { Docs } from '../../documents/Documents'; import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes'; import * as GoldenLayout from '../../goldenLayout'; -import { DocumentManager } from '../../util/DocumentManager'; import { DragManager } from '../../util/DragManager'; import { InteractionUtils } from '../../util/InteractionUtils'; import { ScriptingGlobals } from '../../util/ScriptingGlobals'; -import { SelectionManager } from '../../util/SelectionManager'; import { SnappingManager } from '../../util/SnappingManager'; import { undoable, undoBatch, UndoManager } from '../../util/UndoManager'; import { DashboardView } from '../DashboardView'; import { LightboxView } from '../LightboxView'; +import { DocumentView } from '../nodes/DocumentView'; import { OpenWhere, OpenWhereMod } from '../nodes/OpenWhere'; import { OverlayView } from '../OverlayView'; import { ScriptingRepl } from '../ScriptingRepl'; import { UndoStack } from '../UndoStack'; import './CollectionDockingView.scss'; import { CollectionSubView } from './CollectionSubView'; -import { TabDocView } from './TabDocView'; const _global = (window /* browser */ || global) /* node */ as any; @observer export class CollectionDockingView extends CollectionSubView() { + static tabClass: JSX.Element | null = null; + /** + * Configure golden layout to render its documents using the specified React component + * @param ele - typically would be set to TabDocView + */ + static setTabJSXComponent(ele: any) { + this.tabClass = ele; + } // eslint-disable-next-line no-use-before-define @observable public static Instance: CollectionDockingView | undefined = undefined; @@ -295,7 +301,7 @@ export class CollectionDockingView extends CollectionSubView() { glay.on('tabCreated', this.tabCreated); glay.on('tabDestroyed', this.tabDestroyed); glay.on('stackCreated', this.stackCreated); - glay.registerComponent('DocumentFrameRenderer', TabDocView); + glay.registerComponent('DocumentFrameRenderer', CollectionDockingView.tabClass); glay.container = this._containerRef.current; glay.init(); glay.root.layoutManager.on('itemDropped', this.tabItemDropped); @@ -431,8 +437,8 @@ export class CollectionDockingView extends CollectionSubView() { } 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)) { - SelectionManager.SelectView(DocumentManager.Instance.getFirstDocumentView(map.DashDoc), false); + if (map?.DashDoc && DocumentView.getFirstDocumentView(map.DashDoc)) { + DocumentView.SelectView(DocumentView.getFirstDocumentView(map.DashDoc), false); } } } diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx index f945a7aa4..e53071584 100644 --- a/src/client/views/collections/CollectionMenu.tsx +++ b/src/client/views/collections/CollectionMenu.tsx @@ -21,13 +21,11 @@ import { BoolCast, Cast, DocCast, NumCast, StrCast } from '../../../fields/Types import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes'; import { DragManager } from '../../util/DragManager'; import { dropActionType } from '../../util/DropActionTypes'; -import { SelectionManager } from '../../util/SelectionManager'; -import { SettingsManager } from '../../util/SettingsManager'; +import { SnappingManager } from '../../util/SnappingManager'; import { Transform } from '../../util/Transform'; import { undoBatch } from '../../util/UndoManager'; import { AntimodeMenu } from '../AntimodeMenu'; import { EditableView } from '../EditableView'; -import { MainView } from '../MainView'; import { DefaultStyleProvider } from '../StyleProvider'; import { DocumentView, DocumentViewInternal, returnEmptyDocViewList } from '../nodes/DocumentView'; import './CollectionMenu.scss'; @@ -38,6 +36,7 @@ interface CollectionMenuProps { panelWidth: () => number; toggleTopBar: () => void; topBarHeight: () => number; + togglePropertiesFlyout: () => void; } @observer @@ -59,7 +58,7 @@ export class CollectionMenu extends AntimodeMenu<CollectionMenuProps> { componentDidMount() { reaction( - () => SelectionManager.Views.lastElement(), + () => DocumentView.Selected().lastElement(), view => view && this.SetSelection(view) ); } @@ -77,15 +76,6 @@ export class CollectionMenu extends AntimodeMenu<CollectionMenuProps> { } }; - @action - toggleProperties = () => { - if (MainView.Instance.propertiesWidth() > 0) { - SettingsManager.Instance.propertiesWidth = 0; - } else { - SettingsManager.Instance.propertiesWidth = 300; - } - }; - buttonBarXf = () => { if (!this._docBtnRef.current) return Transform.Identity(); const { scale, translateX, translateY } = ClientUtils.GetScreenTransform(this._docBtnRef.current); @@ -128,15 +118,15 @@ export class CollectionMenu extends AntimodeMenu<CollectionMenuProps> { render() { const headerIcon = this.props.topBarHeight() > 0 ? 'angle-double-up' : 'angle-double-down'; const headerTitle = this.props.topBarHeight() > 0 ? 'Close Header Bar' : 'Open Header Bar'; - const propIcon = SettingsManager.Instance.propertiesWidth > 0 ? 'angle-double-right' : 'angle-double-left'; - const propTitle = SettingsManager.Instance.propertiesWidth > 0 ? 'Close Properties' : 'Open Properties'; + const propIcon = SnappingManager.PropertiesWidth > 0 ? 'angle-double-right' : 'angle-double-left'; + const propTitle = SnappingManager.PropertiesWidth > 0 ? 'Close Properties' : 'Open Properties'; const hardCodedButtons = ( <div className="hardCodedButtons"> <Toggle toggleType={ToggleType.BUTTON} type={Type.PRIM} - color={SettingsManager.userColor} + color={SnappingManager.userColor} onClick={this.props.toggleTopBar} toggleStatus={this.props.topBarHeight() > 0} icon={<FontAwesomeIcon icon={headerIcon} size="lg" />} @@ -145,9 +135,9 @@ export class CollectionMenu extends AntimodeMenu<CollectionMenuProps> { <Toggle toggleType={ToggleType.BUTTON} type={Type.PRIM} - color={SettingsManager.userColor} - onClick={this.toggleProperties} - toggleStatus={SettingsManager.Instance.propertiesWidth > 0} + color={SnappingManager.userColor} + onClick={this._props.togglePropertiesFlyout} + toggleStatus={SnappingManager.PropertiesWidth > 0} icon={<FontAwesomeIcon icon={propIcon} size="lg" />} tooltip={propTitle} /> @@ -159,7 +149,7 @@ export class CollectionMenu extends AntimodeMenu<CollectionMenuProps> { <div className="collectionMenu-container" style={{ - background: SettingsManager.userBackgroundColor, + background: SnappingManager.userBackgroundColor, // borderColor: SettingsManager.userColor }}> {this.contMenuButtons} @@ -245,7 +235,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu _viewCommand = { params: ['target'], title: 'bookmark view', - script: "this.target._freeform_panX = self['target-freeform_panX']; this.target._freeform_panY = this['target-freeform_panY']; this.target._freeform_scale = this['target_freeform_scale']; gotoFrame(this.target, this['target-currentFrame']);", + script: "this.target._freeform_panX = this.target_freeform_panX; this.target._freeform_panY = this['target-freeform_panY']; this.target._freeform_scale = this['target_freeform_scale']; gotoFrame(this.target, this['target-currentFrame']);", immediate: undoBatch(() => { this.target._freeform_panX = 0; this.target._freeform_panY = 0; diff --git a/src/client/views/collections/CollectionNoteTakingView.tsx b/src/client/views/collections/CollectionNoteTakingView.tsx index e940a1aef..3f9eed1d6 100644 --- a/src/client/views/collections/CollectionNoteTakingView.tsx +++ b/src/client/views/collections/CollectionNoteTakingView.tsx @@ -27,7 +27,7 @@ import { CollectionFreeFormDocumentView } from '../nodes/CollectionFreeFormDocum import { DocumentView } from '../nodes/DocumentView'; import { FieldViewProps } from '../nodes/FieldView'; import { FocusViewOptions } from '../nodes/FocusViewOptions'; -import { StyleProp } from '../StyleProvider'; +import { StyleProp } from '../StyleProp'; import './CollectionNoteTakingView.scss'; import { CollectionNoteTakingViewColumn } from './CollectionNoteTakingViewColumn'; import { CollectionNoteTakingViewDivider } from './CollectionNoteTakingViewDivider'; diff --git a/src/client/views/collections/CollectionPileView.tsx b/src/client/views/collections/CollectionPileView.tsx index e02570d3e..5b3f625db 100644 --- a/src/client/views/collections/CollectionPileView.tsx +++ b/src/client/views/collections/CollectionPileView.tsx @@ -10,13 +10,13 @@ import { NumCast, StrCast, toList } from '../../../fields/Types'; import { emptyFunction } from '../../../Utils'; import { DocUtils } from '../../documents/DocUtils'; import { dropActionType } from '../../util/DropActionTypes'; -import { SelectionManager } from '../../util/SelectionManager'; import { undoBatch, UndoManager } from '../../util/UndoManager'; import { OpenWhere } from '../nodes/OpenWhere'; import { computePassLayout, computeStarburstLayout } from './collectionFreeForm'; import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormView'; import './CollectionPileView.scss'; import { CollectionSubView } from './CollectionSubView'; +import { DocumentView } from '../nodes/DocumentView'; @observer export class CollectionPileView extends CollectionSubView() { @@ -150,7 +150,7 @@ export class CollectionPileView extends CollectionSubView() { @undoBatch onClick = (e: React.MouseEvent) => { if (e.button === 0) { - SelectionManager.DeselectAll(); + DocumentView.DeselectAll(); this.toggleStarburst(); e.stopPropagation(); } diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx index 48d24c910..f9b123bb6 100644 --- a/src/client/views/collections/CollectionStackedTimeline.tsx +++ b/src/client/views/collections/CollectionStackedTimeline.tsx @@ -19,11 +19,8 @@ import { ImageField } from '../../../fields/URLField'; import { emptyFunction, formatTime } from '../../../Utils'; import { Docs } from '../../documents/Documents'; import { DocumentType } from '../../documents/DocumentTypes'; -import { FollowLinkScript } from '../../documents/DocUtils'; -import { DocumentManager } from '../../util/DocumentManager'; +import { FollowLinkScript, IsFollowLinkScript } from '../../documents/DocUtils'; import { DragManager } from '../../util/DragManager'; -import { IsFollowLinkScript, LinkFollower } from '../../util/LinkFollower'; -import { LinkManager } from '../../util/LinkManager'; import { ScriptingGlobals } from '../../util/ScriptingGlobals'; import { SnappingManager } from '../../util/SnappingManager'; import { Transform } from '../../util/Transform'; @@ -87,7 +84,6 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack private _timeline: HTMLDivElement | null = null; // ref to actual timeline div private _timelineWrapper: HTMLDivElement | null = null; // ref to timeline wrapper div for zooming and scrolling private _markerStart: number = 0; - @observable public static CurrentlyPlaying: DocumentView[] = []; @observable _selectingRegion = false; @observable _markerEnd: number | undefined = undefined; @observable _trimming: number = TrimScope.None; @@ -182,7 +178,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack getView = async (doc: Doc, options: FocusViewOptions): Promise<Opt<DocumentView>> => new Promise<Opt<DocumentView>>(res => { if (doc.hidden) options.didMove = !(doc.hidden = false); - const findDoc = (finish: (dv: DocumentView) => void) => DocumentManager.Instance.AddViewRenderedCb(doc, dv => finish(dv)); + const findDoc = (finish: (dv: DocumentView) => void) => DocumentView.addViewRenderedCb(doc, dv => finish(dv)); findDoc(dv => res(dv)); }); @@ -296,7 +292,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack } if (!isClick && Math.abs(movement[0]) > 15 && !this.IsTrimming) { const anchor = CollectionStackedTimeline.createAnchor(this.Document, this.dataDoc, this._props.fieldKey, this._markerStart, this._markerEnd, undefined, true); - setTimeout(() => DocumentManager.Instance.getDocumentView(anchor)?.select(false)); + setTimeout(() => DocumentView.getDocumentView(anchor)?.select(false)); } (!isClick || !wasSelecting) && (this._markerEnd = undefined); this._timelineWrapper && (this._timelineWrapper.style.cursor = ''); @@ -481,7 +477,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack @action clickAnchor = (anchorDoc: Doc, clientX: number) => { if (IsFollowLinkScript(anchorDoc.onClick)) { - LinkFollower.FollowLink(undefined, anchorDoc, false); + DocumentView.FollowLink(undefined, anchorDoc, false); } const seekTimeInSeconds = this.anchorStart(anchorDoc) - 0.05; const endTime = this.anchorEnd(anchorDoc); @@ -761,12 +757,12 @@ class StackedTimelineAnchor extends ObservableReactComponent<StackedTimelineAnch // for now, we won't follow any links when the lightbox is oepn to avoid "losing" the video. /* (isDictation || !Doc.AreProtosEqual(LightboxView.LightboxDoc, this._props.layoutDoc)) */ !this._props.layoutDoc.dontAutoFollowLinks && - LinkManager.Links(this._props.mark).length && + Doc.Links(this._props.mark).length && time > NumCast(this._props.mark[this._props.startTag]) && time < NumCast(this._props.mark[this._props.endTag]) && this._lastTimecode < NumCast(this._props.mark[this._props.startTag]) - 1e-5 ) { - LinkFollower.FollowLink(undefined, this._props.mark, false); + DocumentView.FollowLink(undefined, this._props.mark, false); } this._lastTimecode = time; } diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index 8ae0f2832..3f12a281e 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -31,7 +31,7 @@ import { CollectionFreeFormDocumentView } from '../nodes/CollectionFreeFormDocum import { DocumentView } from '../nodes/DocumentView'; import { FieldViewProps } from '../nodes/FieldView'; import { FocusViewOptions } from '../nodes/FocusViewOptions'; -import { StyleProp } from '../StyleProvider'; +import { StyleProp } from '../StyleProp'; import { CollectionMasonryViewFieldRow } from './CollectionMasonryViewFieldRow'; import './CollectionStackingView.scss'; import { CollectionStackingViewFieldColumn } from './CollectionStackingViewFieldColumn'; diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 7c08aedb1..a4708bed5 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -21,12 +21,11 @@ import { Docs, DocumentOptions } from '../../documents/Documents'; import { DragManager } from '../../util/DragManager'; import { dropActionType } from '../../util/DropActionTypes'; import { ImageUtils } from '../../util/Import & Export/ImageUtils'; -import { SelectionManager } from '../../util/SelectionManager'; import { SnappingManager } from '../../util/SnappingManager'; import { UndoManager, undoBatch } from '../../util/UndoManager'; import { ViewBoxBaseComponent } from '../DocComponent'; import { FieldViewProps } from '../nodes/FieldView'; -import { LoadingBox } from '../nodes/LoadingBox'; +import { DocumentView } from '../nodes/DocumentView'; export interface CollectionViewProps extends React.PropsWithChildren<FieldViewProps> { isAnnotationOverlay?: boolean; // is the collection an annotation overlay (eg an overlay on an image/video/etc) @@ -377,7 +376,7 @@ export function CollectionSubView<X>() { } }); } else { - const srcWeb = SelectionManager.Views.lastElement(); + const srcWeb = DocumentView.Selected().lastElement(); const srcUrl = (srcWeb?.Document.data as WebField)?.url?.href?.match(/https?:\/\/[^/]*/)?.[0]; const reg = new RegExp(ClientUtils.prepend(''), 'g'); const modHtml = srcUrl ? html.replace(reg, srcUrl) : html; @@ -386,7 +385,7 @@ export function CollectionSubView<X>() { Doc.GetProto(htmlDoc)['data-text'] = Doc.GetProto(htmlDoc).text = text; addDocument(htmlDoc); if (srcWeb) { - const iframe = SelectionManager.Views[0].ContentDiv?.getElementsByTagName('iframe')?.[0]; + const iframe = DocumentView.Selected()[0].ContentDiv?.getElementsByTagName('iframe')?.[0]; const focusNode = iframe?.contentDocument?.getSelection()?.focusNode as any; if (focusNode) { const anchor = srcWeb?.ComponentView?.getAnchor?.(true); @@ -496,13 +495,13 @@ export function CollectionSubView<X>() { if (typeof files === 'string') { const loading = Docs.Create.LoadingDocument(files, options); generatedDocuments.push(loading); - LoadingBox.addCurrentlyLoading(loading); + Doc.addCurrentlyLoading(loading); DocUtils.uploadYoutubeVideoLoading(files, {}, loading); } else { generatedDocuments.push( ...files.map(file => { const loading = Docs.Create.LoadingDocument(file, options); - LoadingBox.addCurrentlyLoading(loading); + Doc.addCurrentlyLoading(loading); DocUtils.uploadFileToDoc(file, {}, loading); return loading; }) diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx index b3760d4af..0369e4a2a 100644 --- a/src/client/views/collections/CollectionTimeView.tsx +++ b/src/client/views/collections/CollectionTimeView.tsx @@ -12,7 +12,6 @@ import { listSpec } from '../../../fields/Schema'; import { ComputedField, ScriptField } from '../../../fields/ScriptField'; import { Cast, NumCast, StrCast } from '../../../fields/Types'; import { Docs } from '../../documents/Documents'; -import { DocumentManager } from '../../util/DocumentManager'; import { ScriptingGlobals } from '../../util/ScriptingGlobals'; import { ContextMenu } from '../ContextMenu'; import { ContextMenuProps } from '../ContextMenuItem'; @@ -257,7 +256,7 @@ ScriptingGlobals.add(function pivotColumnClick(pivotDoc: Doc, bounds: ViewDefBou action(() => { const filterVals = bounds.payload as string[]; filterVals.map(filterVal => Doc.setDocFilter(pivotDoc, pivotField, filterVal, 'check')); - const pivotView = DocumentManager.Instance.getDocumentView(pivotDoc); + const pivotView = DocumentView.getDocumentView(pivotDoc); if (pivotDoc && pivotView?.ComponentView instanceof CollectionTimeView && filterVals.length === 1) { if (pivotView?.ComponentView.childDocs.length && pivotView.ComponentView.childDocs[0][filterVals[0]]) { // eslint-disable-next-line prefer-destructuring diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index d015e73ad..c1247f5b0 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -14,11 +14,9 @@ import { TraceMobx } from '../../../fields/util'; import { emptyFunction, Utils } from '../../../Utils'; import { Docs } from '../../documents/Documents'; import { DocUtils } from '../../documents/DocUtils'; -import { DocumentManager } from '../../util/DocumentManager'; import { DragManager } from '../../util/DragManager'; import { dropActionType } from '../../util/DropActionTypes'; import { ScriptingGlobals } from '../../util/ScriptingGlobals'; -import { SelectionManager } from '../../util/SelectionManager'; import { SnappingManager } from '../../util/SnappingManager'; import { Transform } from '../../util/Transform'; import { undoBatch, UndoManager } from '../../util/UndoManager'; @@ -27,10 +25,11 @@ import { ContextMenuProps } from '../ContextMenuItem'; import { EditableView } from '../EditableView'; import { DocumentView } from '../nodes/DocumentView'; import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox'; -import { StyleProp } from '../StyleProvider'; +import { StyleProp } from '../StyleProp'; import { CollectionFreeFormView } from './collectionFreeForm'; import { CollectionSubView } from './CollectionSubView'; import './CollectionTreeView.scss'; +import { TreeViewType } from './CollectionTreeViewType'; import { TreeView } from './TreeView'; const _global = (window /* browser */ || global) /* node */ as any; @@ -49,12 +48,6 @@ export type collectionTreeViewProps = { hierarchyIndex?: number[]; }; -export enum TreeViewType { - outline = 'outline', - fileSystem = 'fileSystem', - default = 'default', -} - @observer export class CollectionTreeView extends CollectionSubView<Partial<collectionTreeViewProps>>() { public static AddTreeFunc = 'addTreeFolder(this.embedContainer)'; @@ -183,7 +176,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree const targetDataDoc = this.Document[DocData]; const value = DocListCast(targetDataDoc[this._props.fieldKey]); const result = value.filter(v => !docs.includes(v)); - if (docs.some(doc => SelectionManager.Views.some(dv => Doc.AreProtosEqual(dv.Document, doc)))) SelectionManager.DeselectAll(); + if (docs.some(doc => DocumentView.Selected().some(dv => Doc.AreProtosEqual(dv.Document, doc)))) DocumentView.DeselectAll(); if (result.length !== value.length) { if (docIn instanceof Doc) { const ind = DocListCast(targetDataDoc[this._props.fieldKey]).indexOf(docIn); @@ -191,7 +184,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree this._props.removeDocument?.(docIn); if (ind > 0 && prev) { Doc.SetSelectOnLoad(prev); - DocumentManager.Instance.getDocumentView(prev, this.DocumentView?.())?.select(false); + DocumentView.getDocumentView(prev, this.DocumentView?.())?.select(false); } return true; } @@ -468,7 +461,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree minHeight: '100%', }} onWheel={e => e.stopPropagation()} - onClick={() => (!this.layoutDoc.forceActive ? this._props.select(false) : SelectionManager.DeselectAll())} + onClick={() => (!this.layoutDoc.forceActive ? this._props.select(false) : DocumentView.DeselectAll())} onDrop={this.onTreeDrop}> <ul className={`no-indent${this.outlineMode ? '-outline' : ''}`}>{this.treeViewElements}</ul> </div> diff --git a/src/client/views/collections/CollectionTreeViewType.ts b/src/client/views/collections/CollectionTreeViewType.ts new file mode 100644 index 000000000..53b88160e --- /dev/null +++ b/src/client/views/collections/CollectionTreeViewType.ts @@ -0,0 +1,5 @@ +export enum TreeViewType { + outline = 'outline', + fileSystem = 'fileSystem', + default = 'default', +} diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx index 2d900bb73..2d8b2564d 100644 --- a/src/client/views/collections/TabDocView.tsx +++ b/src/client/views/collections/TabDocView.tsx @@ -9,7 +9,7 @@ import * as ReactDOM from 'react-dom/client'; import { ClientUtils, DashColor, lightOrDark, returnEmptyDoclist, returnFalse, returnTrue, setupMoveUpEvents, simulateMouseClick } from '../../../ClientUtils'; import { emptyFunction } from '../../../Utils'; import { Doc, Opt } from '../../../fields/Doc'; -import { DocData } from '../../../fields/DocSymbols'; +import { DocData, DocViews } from '../../../fields/DocSymbols'; import { Id } from '../../../fields/FieldSymbols'; import { List } from '../../../fields/List'; import { FieldId } from '../../../fields/RefField'; @@ -18,10 +18,8 @@ import { Cast, DocCast, NumCast, StrCast, toList } from '../../../fields/Types'; import { DocServer } from '../../DocServer'; import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes'; import { Docs } from '../../documents/Documents'; -import { DocumentManager } from '../../util/DocumentManager'; import { DragManager } from '../../util/DragManager'; import { dropActionType } from '../../util/DropActionTypes'; -import { SelectionManager } from '../../util/SelectionManager'; import { SnappingManager } from '../../util/SnappingManager'; import { Transform } from '../../util/Transform'; import { UndoManager, undoable } from '../../util/UndoManager'; @@ -29,7 +27,8 @@ import { DashboardView } from '../DashboardView'; import { LightboxView } from '../LightboxView'; import { ObservableReactComponent } from '../ObservableReactComponent'; import { PinDocView, PinProps } from '../PinFuncs'; -import { DefaultStyleProvider, StyleProp } from '../StyleProvider'; +import { StyleProp } from '../StyleProp'; +import { DefaultStyleProvider } from '../StyleProvider'; import { Colors } from '../global/globalEnums'; import { DocumentView, returnEmptyDocViewList } from '../nodes/DocumentView'; import { FieldViewProps } from '../nodes/FieldView'; @@ -194,9 +193,101 @@ export class TabDocView extends ObservableReactComponent<TabDocViewProps> { _mainCont: HTMLDivElement | null = null; _tabReaction: IReactionDisposer | undefined; + /** + * Adds a document to the presentation view + * */ + @action + public static PinDoc(docIn: Doc | Doc[], pinProps: PinProps) { + const docs = toList(docIn); + + const batch = UndoManager.StartBatch('Pin doc to pres trail'); + const curPres = Doc.ActivePresentation ?? Doc.MakeCopy(Doc.UserDoc().emptyTrail as Doc, true); + + if (!Doc.ActivePresentation) { + Doc.AddDocToList(Doc.MyTrails, 'data', curPres); + Doc.ActivePresentation = curPres; + } + + docs.forEach(doc => { + // Edge Case 1: Cannot pin document to itself + if (doc === curPres) { + alert('Cannot pin presentation document to itself'); + return; + } + const anchorDoc = DocumentView.getDocumentView(doc)?.ComponentView?.getAnchor?.(false, pinProps); + const pinDoc = anchorDoc?.type === DocumentType.CONFIG ? anchorDoc : Docs.Create.ConfigDocument({}); + const targDoc = (pinDoc.presentation_targetDoc = anchorDoc ?? doc); + pinDoc.title = doc.title + ' - Slide'; + pinDoc.data = targDoc.type === DocumentType.PRES ? ComputedField.MakeFunction('copyField(this.presentation_targetDoc.data') : new List<Doc>(); // the children of the embedding's layout are the presentation slide children. the embedding's data field might be children of a collection, PDF data, etc -- in any case we don't want the tree view to "see" this data + pinDoc.presentation_movement = doc.type === DocumentType.SCRIPTING || pinProps?.pinDocLayout ? PresMovement.None : PresMovement.Zoom; + pinDoc.presentation_duration = pinDoc.presentation_duration ?? 1000; + pinDoc.presentation_groupWithUp = false; + Doc.SetContainer(pinDoc, curPres); + // these should potentially all be props passed down by the CollectionTreeView to the TreeView elements. That way the PresBox could configure all of its children at render time + pinDoc.treeView = ''; // not really needed, but makes key value pane look better + pinDoc.treeView_RenderAsBulletHeader = true; // forces a tree view to render the document next to the bullet in the header area + pinDoc.treeView_HeaderWidth = '100%'; // forces the header to grow to be the same size as its largest sibling. + pinDoc.treeView_FieldKey = 'data'; // tree view will treat the 'data' field as the field where the hierarchical children are located instead of using the document's layout string field + pinDoc.treeView_ExpandedView = 'data'; // in case the data doc has an expandedView set, this will mask that field and use the 'data' field when expanding the tree view + pinDoc.treeView_HideHeaderIfTemplate = true; // this will force the document to render itself as the tree view header + const duration = NumCast(doc[`${Doc.LayoutFieldKey(pinDoc)}_duration`], null); + + if (pinProps.pinViewport) PinDocView(pinDoc, pinProps, anchorDoc ?? doc); + if (!pinProps?.audioRange && duration !== undefined) { + pinDoc.presentation_mediaStart = 'manual'; + pinDoc.presentation_mediaStop = 'manual'; + } + if (pinProps?.activeFrame !== undefined) { + pinDoc.config_activeFrame = pinProps?.activeFrame; + pinDoc.title = doc.title + ' (move)'; + pinDoc.presentation_movement = PresMovement.Pan; + } + if (pinProps?.currentFrame !== undefined) { + pinDoc.config_currentFrame = pinProps?.currentFrame; + pinDoc.title = doc.title + ' (move)'; + pinDoc.presentation_movement = PresMovement.Pan; + } + if (pinDoc.stroke_isInkMask) { + pinDoc.presentation_hideAfter = true; + pinDoc.presentation_hideBefore = true; + pinDoc.presentation_movement = PresMovement.None; + } + if (curPres.expandBoolean) pinDoc.presentation_expandInlineButton = true; + Doc.AddDocToList(curPres, 'data', pinDoc, PresBox.Instance?.sortArray()?.lastElement()); + PresBox.Instance?.clearSelectedArray(); + pinDoc && PresBox.Instance?.addToSelectedArray(pinDoc); // Update selected array + }); + if ( + // open the presentation trail if it's not already opened + !Array.from(CollectionDockingView.Instance?.tabMap ?? []) + .map(d => d.DashDoc) + .includes(curPres) + ) { + if (Doc.IsInMyOverlay(curPres)) Doc.RemFromMyOverlay(curPres); + CollectionDockingView.AddSplit(curPres, OpenWhereMod.right); + setTimeout(() => DocumentView.showDocument(docs.lastElement(), { willPan: true }), 100); // keeps the pinned doc in view since the sidebar shifts things + } + setTimeout(batch.end, 500); // need to wait until dockingview (goldenlayout) updates all its structurs + } + + static Activate = (tabDoc: Doc) => { + const tab = Array.from(CollectionDockingView.Instance?.tabMap!).find(findTab => findTab.DashDoc === tabDoc && !findTab.contentItem.config.props.keyValue); + tab?.header.parent.setActiveContentItem(tab.contentItem); // glr: Panning does not work when this is set - (this line is for trying to make a tab that is not topmost become topmost) + return tab !== undefined; + }; + // static ActivateTabView(doc: Doc) { + // const tabView = Array.from(TabDocView._allTabs).find(view => view._document === doc); + // if (!tabView?._activated && tabView?._document) { + // TabDocView.Activate(tabView?._document); + // return tabView; + // } + // return undefined; + // } constructor(props: any) { super(props); makeObservable(this); + DocumentView.activateTabView = TabDocView.Activate; + DocumentView.PinDoc = TabDocView.PinDoc; } @observable _activated: boolean = false; @@ -205,8 +296,14 @@ export class TabDocView extends ObservableReactComponent<TabDocViewProps> { @observable _hovering = false; @observable _isActive: boolean = false; @observable _isAnyChildContentActive = false; + public static IsSelected = (doc?: Doc) => { + if (Array.from(doc?.[DocViews] ?? []).some(dv => dv?.IsSelected)) { + return true; + } + return false; + }; @computed get _isUserActivated() { - return SelectionManager.IsSelected(this._document) || this._isAnyChildContentActive; + return TabDocView.IsSelected(this._document) || this._isAnyChildContentActive; } get _isContentActive() { return this._isUserActivated || this._hovering; @@ -279,12 +376,12 @@ export class TabDocView extends ObservableReactComponent<TabDocViewProps> { returnFalse, action(clickEv => { if (this.view) { - SelectionManager.SelectView(this.view, false); + DocumentView.SelectView(this.view, false); const child = getChild(); simulateMouseClick(child, clickEv.clientX, clickEv.clientY + 30, clickEv.screenX, clickEv.screenY + 30); } else { this._activated = true; - setTimeout(() => this.view && SelectionManager.SelectView(this.view, false)); + setTimeout(() => this.view && DocumentView.SelectView(this.view, false)); } }) ); @@ -350,7 +447,7 @@ export class TabDocView extends ObservableReactComponent<TabDocViewProps> { // select the tab document when the tab is directly clicked and activate the tab whenver the tab document is selected titleEle.onpointerdown = action((e: any) => { if (e.target.className !== 'lm_iconWrap') { - if (this.view) SelectionManager.SelectView(this.view, false); + if (this.view) DocumentView.SelectView(this.view, false); else this._activated = true; if (Date.now() - titleEle.lastClick < 1000) titleEle.select(); titleEle.lastClick = Date.now(); @@ -358,7 +455,7 @@ export class TabDocView extends ObservableReactComponent<TabDocViewProps> { } }); tab._disposers.selectionDisposer = reaction( - () => SelectionManager.IsSelected(this._document), + () => TabDocView.IsSelected(this._document), action(selected => { if (selected) this._activated = true; if (selected && tab.contentItem !== tab.header.parent.getActiveContentItem()) { @@ -382,89 +479,12 @@ export class TabDocView extends ObservableReactComponent<TabDocViewProps> { .off('click') // unbind the current click handler .click(() => { Object.values(tab._disposers).forEach((disposer: any) => disposer?.()); - SelectionManager.DeselectAll(); + DocumentView.DeselectAll(); UndoManager.RunInBatch(() => tab.contentItem.remove(), 'delete tab'); }); } }; - /** - * Adds a document to the presentation view - * */ - @action - public static PinDoc(docIn: Doc | Doc[], pinProps: PinProps) { - const docs = toList(docIn); - - const batch = UndoManager.StartBatch('Pin doc to pres trail'); - const curPres = Doc.ActivePresentation ?? Doc.MakeCopy(Doc.UserDoc().emptyTrail as Doc, true); - - if (!Doc.ActivePresentation) { - Doc.AddDocToList(Doc.MyTrails, 'data', curPres); - Doc.ActivePresentation = curPres; - } - - docs.forEach(doc => { - // Edge Case 1: Cannot pin document to itself - if (doc === curPres) { - alert('Cannot pin presentation document to itself'); - return; - } - const anchorDoc = DocumentManager.Instance.getDocumentView(doc)?.ComponentView?.getAnchor?.(false, pinProps); - const pinDoc = anchorDoc?.type === DocumentType.CONFIG ? anchorDoc : Docs.Create.ConfigDocument({}); - const targDoc = (pinDoc.presentation_targetDoc = anchorDoc ?? doc); - pinDoc.title = doc.title + ' - Slide'; - pinDoc.data = targDoc.type === DocumentType.PRES ? ComputedField.MakeFunction('copyField(this.presentation_targetDoc.data') : new List<Doc>(); // the children of the embedding's layout are the presentation slide children. the embedding's data field might be children of a collection, PDF data, etc -- in any case we don't want the tree view to "see" this data - pinDoc.presentation_movement = doc.type === DocumentType.SCRIPTING || pinProps?.pinDocLayout ? PresMovement.None : PresMovement.Zoom; - pinDoc.presentation_duration = pinDoc.presentation_duration ?? 1000; - pinDoc.presentation_groupWithUp = false; - Doc.SetContainer(pinDoc, curPres); - // these should potentially all be props passed down by the CollectionTreeView to the TreeView elements. That way the PresBox could configure all of its children at render time - pinDoc.treeView = ''; // not really needed, but makes key value pane look better - pinDoc.treeView_RenderAsBulletHeader = true; // forces a tree view to render the document next to the bullet in the header area - pinDoc.treeView_HeaderWidth = '100%'; // forces the header to grow to be the same size as its largest sibling. - pinDoc.treeView_FieldKey = 'data'; // tree view will treat the 'data' field as the field where the hierarchical children are located instead of using the document's layout string field - pinDoc.treeView_ExpandedView = 'data'; // in case the data doc has an expandedView set, this will mask that field and use the 'data' field when expanding the tree view - pinDoc.treeView_HideHeaderIfTemplate = true; // this will force the document to render itself as the tree view header - const duration = NumCast(doc[`${Doc.LayoutFieldKey(pinDoc)}_duration`], null); - - if (pinProps.pinViewport) PinDocView(pinDoc, pinProps, anchorDoc ?? doc); - if (!pinProps?.audioRange && duration !== undefined) { - pinDoc.presentation_mediaStart = 'manual'; - pinDoc.presentation_mediaStop = 'manual'; - } - if (pinProps?.activeFrame !== undefined) { - pinDoc.config_activeFrame = pinProps?.activeFrame; - pinDoc.title = doc.title + ' (move)'; - pinDoc.presentation_movement = PresMovement.Pan; - } - if (pinProps?.currentFrame !== undefined) { - pinDoc.config_currentFrame = pinProps?.currentFrame; - pinDoc.title = doc.title + ' (move)'; - pinDoc.presentation_movement = PresMovement.Pan; - } - if (pinDoc.stroke_isInkMask) { - pinDoc.presentation_hideAfter = true; - pinDoc.presentation_hideBefore = true; - pinDoc.presentation_movement = PresMovement.None; - } - if (curPres.expandBoolean) pinDoc.presentation_expandInlineButton = true; - Doc.AddDocToList(curPres, 'data', pinDoc, PresBox.Instance?.sortArray()?.lastElement()); - PresBox.Instance?.clearSelectedArray(); - pinDoc && PresBox.Instance?.addToSelectedArray(pinDoc); // Update selected array - }); - if ( - // open the presentation trail if it's not already opened - !Array.from(CollectionDockingView.Instance?.tabMap ?? []) - .map(d => d.DashDoc) - .includes(curPres) - ) { - if (Doc.IsInMyOverlay(curPres)) Doc.RemFromMyOverlay(curPres); - CollectionDockingView.AddSplit(curPres, OpenWhereMod.right); - setTimeout(() => DocumentManager.Instance.showDocument(docs.lastElement(), { willPan: true }), 100); // keeps the pinned doc in view since the sidebar shifts things - } - setTimeout(batch.end, 500); // need to wait until dockingview (goldenlayout) updates all its structurs - } - componentDidMount() { new _global.ResizeObserver( action((entries: any) => { @@ -484,12 +504,12 @@ export class TabDocView extends ObservableReactComponent<TabDocViewProps> { } componentDidUpdate(prevProps: Readonly<TabDocViewProps>) { super.componentDidUpdate(prevProps); - this._view && DocumentManager.Instance.AddView(this._view); + this._view && DocumentView.addView(this._view); } componentWillUnmount() { this._tabReaction?.(); - this._view && DocumentManager.Instance.RemoveView(this._view); + this._view && DocumentView.removeView(this._view); runInAction(() => TabDocView._allTabs.delete(this)); this._props.glContainer.layoutManager.off('activeContentItemChanged', this.onActiveContentItemChanged); @@ -503,7 +523,7 @@ export class TabDocView extends ObservableReactComponent<TabDocViewProps> { private onActiveContentItemChanged(contentItem: any) { if (!contentItem || (this.stack === contentItem.parent && ((contentItem?.tab === this.tab && !this._isActive) || (contentItem?.tab !== this.tab && this._isActive)))) { this._activated = this._isActive = !contentItem || contentItem?.tab === this.tab; - if (!this._view && this.tab?.contentItem?.config?.props?.panelName !== TabDocView.DontSelectOnActivate) setTimeout(() => SelectionManager.SelectView(this._view, false)); + if (!this._view && this.tab?.contentItem?.config?.props?.panelName !== TabDocView.DontSelectOnActivate) setTimeout(() => DocumentView.SelectView(this._view, false)); !this._isActive && this._document && Doc.UnBrushDoc(this._document); // bcz: bad -- trying to simulate a pointer leave event when a new tab is opened up on top of an existing one. } } @@ -517,7 +537,7 @@ export class TabDocView extends ObservableReactComponent<TabDocViewProps> { // lightbox - will add the document to any collection along the path from the document to the docking view that has a field isLightbox. if none is found, it adds to the full screen lightbox addDocTab = (docsIn: Doc | Doc[], location: OpenWhere) => { const docs = toList(docsIn); - SelectionManager.DeselectAll(); + DocumentView.DeselectAll(); const whereFields = location.split(':'); const keyValue = whereFields.includes(OpenWhereMod.keyvalue); const whereMods = whereFields.length > 1 ? (whereFields[1] as OpenWhereMod) : OpenWhereMod.none; @@ -527,7 +547,7 @@ export class TabDocView extends ObservableReactComponent<TabDocViewProps> { switch (whereFields[0]) { case undefined: case OpenWhere.lightbox: if (this.layoutDoc?._isLightbox) { - const lightboxView = !docs[0].annotationOn && DocCast(docs[0].embedContainer) ? DocumentManager.Instance.getFirstDocumentView(DocCast(docs[0].embedContainer)) : undefined; + const lightboxView = !docs[0].annotationOn && DocCast(docs[0].embedContainer) ? DocumentView.getFirstDocumentView(DocCast(docs[0].embedContainer)) : undefined; const data = lightboxView?.dataDoc[Doc.LayoutFieldKey(lightboxView.Document)]; if (lightboxView && (!data || data instanceof List)) { lightboxView.layoutDoc[Doc.LayoutFieldKey(lightboxView.Document)] = new List<Doc>(docs); @@ -543,7 +563,7 @@ export class TabDocView extends ObservableReactComponent<TabDocViewProps> { }; remDocTab = (doc: Doc | Doc[]) => { if (doc === this._document) { - SelectionManager.DeselectAll(); + DocumentView.DeselectAll(); CollectionDockingView.CloseSplit(this._document); return true; } @@ -551,11 +571,7 @@ export class TabDocView extends ObservableReactComponent<TabDocViewProps> { }; getCurrentFrame = () => NumCast(Cast(PresBox.Instance.activeItem.presentation_targetDoc, Doc, null)._currentFrame); - static Activate = (tabDoc: Doc) => { - const tab = Array.from(CollectionDockingView.Instance?.tabMap!).find(findTab => findTab.DashDoc === tabDoc && !findTab.contentItem.config.props.keyValue); - tab?.header.parent.setActiveContentItem(tab.contentItem); // glr: Panning does not work when this is set - (this line is for trying to make a tab that is not topmost become topmost) - return tab !== undefined; - }; + @action focusFunc = () => { if (!this.tab.header.parent._activeContentItem || this.tab.header.parent._activeContentItem !== this.tab.contentItem) { @@ -586,7 +602,7 @@ export class TabDocView extends ObservableReactComponent<TabDocViewProps> { <DocumentView key={this._document[Id]} ref={action((r: DocumentView) => { - this._lastView && DocumentManager.Instance.RemoveView(this._lastView); + this._lastView && DocumentView.removeView(this._lastView); this._view = r; this._lastView = this._view; })} @@ -635,7 +651,7 @@ export class TabDocView extends ObservableReactComponent<TabDocViewProps> { this._mainCont = ref; if (this._mainCont) { if (this._lastTab) { - this._view && DocumentManager.Instance.RemoveView(this._view); + this._view && DocumentView.removeView(this._view); } this._lastTab = this.tab; (this._mainCont as any).InitTab = (tab: any) => this.init(tab, this._document); diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx index 2f5af0564..969b98d91 100644 --- a/src/client/views/collections/TreeView.tsx +++ b/src/client/views/collections/TreeView.tsx @@ -21,24 +21,23 @@ import { TraceMobx } from '../../../fields/util'; import { DocUtils } from '../../documents/DocUtils'; import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes'; import { Docs } from '../../documents/Documents'; -import { DocumentManager } from '../../util/DocumentManager'; import { DragManager } from '../../util/DragManager'; import { dropActionType } from '../../util/DropActionTypes'; -import { LinkManager } from '../../util/LinkManager'; import { SettingsManager } from '../../util/SettingsManager'; import { SnappingManager } from '../../util/SnappingManager'; import { Transform } from '../../util/Transform'; import { UndoManager, undoBatch, undoable } from '../../util/UndoManager'; import { EditableView } from '../EditableView'; import { ObservableReactComponent } from '../ObservableReactComponent'; -import { StyleProp } from '../StyleProvider'; +import { StyleProp } from '../StyleProp'; import { DocumentView, DocumentViewInternal } from '../nodes/DocumentView'; import { FieldViewProps, StyleProviderFuncType } from '../nodes/FieldView'; import { KeyValueBox } from '../nodes/KeyValueBox'; import { OpenWhere } from '../nodes/OpenWhere'; import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox'; import { RichTextMenu } from '../nodes/formattedText/RichTextMenu'; -import { CollectionTreeView, TreeViewType } from './CollectionTreeView'; +import { CollectionTreeView } from './CollectionTreeView'; +import { TreeViewType } from './CollectionTreeViewType'; import { CollectionView } from './CollectionView'; import { TreeSort } from './TreeSort'; import './TreeView.scss'; @@ -208,7 +207,7 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> { const ind = DocListCast(this.dataDoc[key]).indexOf(docs.lastElement()); const res = docs.reduce((flg, doc) => flg && Doc.RemoveDocFromList(this.dataDoc, key, doc), true); - res && ind > 0 && DocumentManager.Instance.getDocumentView(DocListCast(this.dataDoc[key])[ind - 1], this.treeView.DocumentView?.())?.select(false); + res && ind > 0 && DocumentView.getDocumentView(DocListCast(this.dataDoc[key])[ind - 1], this.treeView.DocumentView?.())?.select(false); return res; }; @@ -379,7 +378,7 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> { const bulletData = bullet[DocData]; bulletData.title = ComputedField.MakeFunction('this.text?.Text'); bulletData.data = new List<Doc>([]); - DocumentManager.Instance.AddViewRenderedCb(bullet, dv => dv.ComponentView?.setFocus?.()); + DocumentView.addViewRenderedCb(bullet, dv => dv.ComponentView?.setFocus?.()); return bullet; } @@ -823,7 +822,7 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> { @computed get validExpandViewTypes() { const annos = () => (DocListCast(this.Document[this.fieldKey + '_annotations']).length && !this.treeView.dashboardMode ? 'annotations' : ''); - const links = () => (LinkManager.Links(this.Document).length && !this.treeView.dashboardMode ? 'links' : ''); + const links = () => (Doc.Links(this.Document).length && !this.treeView.dashboardMode ? 'links' : ''); const data = () => (this.childDocs || this.treeView.dashboardMode ? this.fieldKey : ''); const embeddings = () => (this.treeView.dashboardMode ? '' : 'embeddings'); const fields = () => (Doc.noviceMode ? '' : 'fields'); @@ -1210,7 +1209,7 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> { className={`treeView-container${this._props.isContentActive() ? '-active' : ''}`} ref={this.createTreeDropTarget} onDrop={this.onTreeDrop} - // onPointerDown={e => this._props.isContentActive(true) && SelectionManager.DeselectAll()} // bcz: this breaks entering a text filter in a filterBox since it deselects the filter's target document + // onPointerDown={e => this._props.isContentActive(true) && DocumentView.DeselectAll()} // bcz: this breaks entering a text filter in a filterBox since it deselects the filter's target document // onKeyDown={this.onKeyDown} > <li className="collection-child"> 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<CollectionFreeFormInfoUIProps> { + public static Init() { + CollectionFreeFormView.SetInfoUICreator((doc: Doc, layout: Doc, childDocs: () => Doc[], close: () => void) => ( + // + <CollectionFreeFormInfoUI Document={doc} LayoutDoc={layout} childDocs={childDocs} close={close} /> + )); + } _firstDocPos = { x: 0, y: 0 }; constructor(props: any) { @@ -63,7 +69,7 @@ export class CollectionFreeFormInfoUI extends ObservableReactComponent<Collectio const linkStart = () => 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<CollectionFreeFormPannableContentsProps> { + 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<Partial<collection } // this makes mobx trace() statements more descriptive public unprocessedDocs: Doc[] = []; public static collectionsWithUnprocessedInk = new Set<CollectionFreeFormView>(); - 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<Partial<collection public static gotoKeyframe(timer: NodeJS.Timeout | undefined, docs: Doc[], duration: number) { return DocumentView.SetViewTransition(docs, 'all', duration, timer, undefined, true); } - public static updateKeyframe(timer: NodeJS.Timeout | undefined, docs: Doc[], time: number) { - const newTimer = DocumentView.SetViewTransition(docs, 'all', 1000, timer, undefined, true); - const timecode = Math.round(time); - docs.forEach(doc => { - 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<Partial<collection this._keyTimer = CollectionFreeFormView.gotoKeyframe(this._keyTimer, [...this.childDocs, this.layoutDoc], 1000); this.Document._currentFrame = Math.max(0, (currentFrame || 0) - 1); } else { - this._keyTimer = CollectionFreeFormView.updateKeyframe(this._keyTimer, [...this.childDocs, this.layoutDoc], currentFrame || 0); + this._keyTimer = CollectionFreeFormDocumentView.updateKeyframe(this._keyTimer, [...this.childDocs, this.layoutDoc], currentFrame || 0); this.Document._currentFrame = Math.max(0, (currentFrame || 0) + 1); this.Document.lastFrame = Math.max(NumCast(this.Document._currentFrame), NumCast(this.Document.lastFrame)); } @@ -292,8 +270,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection this.addDocument(newDoc); }; selectDocuments = (docs: Doc[]) => { - 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<Partial<collection retVal = this._props.addDocument?.(newBox) || false; if (retVal) { this.bringToFront(newBox); - this._clusters.updateCluster(newBox); + this._clusters.addDocument(newBox); } } else { retVal = this._props.addDocument?.(newBox) || false; @@ -416,7 +394,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection res(this.DocumentView?.()); return; } - const findDoc = (finish: (dv: DocumentView) => 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<Partial<collection !d._keepZWhenDragged && (d.zIndex = zsorted.length + 1 + i); // bringToFront } - (docDragData.droppedDocuments.length === 1 || de.shiftKey) && this._clusters.updateClusterDocs(docDragData.droppedDocuments); + (docDragData.droppedDocuments.length === 1 || de.shiftKey) && this._clusters.addDocuments(docDragData.droppedDocuments); return true; } @@ -528,7 +506,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection break; case InkTool.None: if (!(this._props.layoutEngine?.() || StrCast(this.layoutDoc._layoutEngine))) { - const hit = this._clusters.pickCluster(this.screenToFreeformContentsXf.transformPoint(e.clientX, e.clientY)); + const hit = this._clusters.handlePointerDown(this.screenToFreeformContentsXf.transformPoint(e.clientX, e.clientY)); setupMoveUpEvents(this, e, this.onPointerMove, emptyFunction, emptyFunction, hit !== -1, false); } break; @@ -654,7 +632,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection }; onPointerMove = (e: PointerEvent) => { - 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<Partial<collection const eraserMax = { X: Math.max(lastPoint.X, currPoint.X), Y: Math.max(lastPoint.Y, currPoint.Y) }; // prettier-ignore return this.childDocs - .map(doc => 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<Partial<collection this.childDocs .filter(doc => 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<Partial<collection {...OmitKeys(entry, ['replica', 'pair']).omit} key={childLayout[Id] + (entry.replica || '')} Document={childLayout} + parent={this} containerViewPath={this.DocumentView?.().docViewPath} styleProvider={this._clusters.styleProvider} TemplateDataDocument={childData} dragStarting={this.dragStarting} dragEnding={this.dragEnding} + isAnyChildContentActive={this.isAnyChildContentActive} isGroupActive={this._props.isGroupActive} renderDepth={this._props.renderDepth + 1} hideDecorations={BoolCast(childLayout._layout_isSvg && childLayout.type === DocumentType.LINK)} suppressSetHeight={!!this.layoutEngine} RenderCutoffProvider={this.renderCutoffProvider} - CollectionFreeFormView={this} LayoutTemplate={childLayout.z ? undefined : this._props.childLayoutTemplate} LayoutTemplateString={childLayout.z ? undefined : this._props.childLayoutString} rootSelected={childData ? this.rootSelected : returnFalse} @@ -1282,7 +1261,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection }) ); - this._clusters.initClusters(); + this._clusters.initLayout(); return elements; }; @@ -1308,7 +1287,14 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection childDocsFunc = () => this.childDocs; closeInfo = action(() => { Doc.IsInfoUIDisabled = true }); // prettier-ignore - infoUI = () => (Doc.IsInfoUIDisabled || this.Document.annotationOn || this._props.renderDepth ? null : <CollectionFreeFormInfoUI Document={this.Document} LayoutDoc={this.layoutDoc} childDocs={this.childDocsFunc} close={this.closeInfo} />); + 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<Partial<collection const isDocInView = (doc: Doc, rect: { left: number; top: number; width: number; height: number }) => 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<Partial<collection nudge={this.isAnnotationOverlay || this._props.renderDepth > 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<Partial<collection } // eslint-disable-next-line prefer-arrow-callback ScriptingGlobals.add(function nextKeyFrame(readOnly: boolean) { - !readOnly && (SelectionManager.Views[0].ComponentView as CollectionFreeFormView)?.changeKeyFrame(); + !readOnly && (DocumentView.Selected()[0].ComponentView as CollectionFreeFormView)?.changeKeyFrame(); }); // eslint-disable-next-line prefer-arrow-callback ScriptingGlobals.add(function prevKeyFrame(readOnly: boolean) { - !readOnly && (SelectionManager.Views[0].ComponentView as CollectionFreeFormView)?.changeKeyFrame(true); + !readOnly && (DocumentView.Selected()[0].ComponentView as CollectionFreeFormView)?.changeKeyFrame(true); }); // eslint-disable-next-line prefer-arrow-callback ScriptingGlobals.add(function curKeyFrame(readOnly: boolean) { - const selView = SelectionManager.Views; + const selView = DocumentView.Selected(); if (readOnly) return selView[0].ComponentView?.getKeyFrameEditing?.() ? Colors.MEDIUM_BLUE : 'transparent'; runInAction(() => 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<string>(['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<SubCollectionViewProps this.pasteTable(ns, x, y); })(); e.stopPropagation(); - } else if (!e.ctrlKey && !e.metaKey && SelectionManager.Views.length < 2) { + } else if (!e.ctrlKey && !e.metaKey && DocumentView.Selected().length < 2) { FormattedTextBox.SelectOnLoadChar = Doc.UserDoc().defaultTextLayout && !this._props.childLayoutString ? e.key : ''; FormattedTextBox.LiveTextUndo = UndoManager.StartBatch('type new note'); this._props.addLiveTextDocument(DocUtils.GetNewTextDoc('-typed text-', x, y, 200, 100)); @@ -249,7 +248,7 @@ export class MarqueeView extends ObservableReactComponent<SubCollectionViewProps if (this._visible) { const mselect = this.marqueeSelect(); if (!e.shiftKey) { - SelectionManager.DeselectAll(mselect.length ? undefined : this._props.Document); + DocumentView.DeselectAll(mselect.length ? undefined : this._props.Document); } const docs = mselect.length ? mselect : [this._props.Document]; this._props.selectDocuments(docs); @@ -344,7 +343,7 @@ export class MarqueeView extends ObservableReactComponent<SubCollectionViewProps @undoBatch delete = action((e?: React.PointerEvent<Element> | 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<SubCollectionViewProps @undoBatch pileup = action(() => { 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<SubCollectionViewProps }); /** - * This triggers the TabDocView.PinDoc method which is the universal method + * This triggers the DocumentView.PinDoc method which is the universal method * used to pin documents to the currently active presentation trail. * * This one is unique in that it includes the bounds associated with marquee view. diff --git a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx index e68ed0e17..eac0dc0e1 100644 --- a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx +++ b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx @@ -14,7 +14,6 @@ import { Id } from '../../../../fields/FieldSymbols'; import { BoolCast, Cast, DocCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types'; import { CollectionViewType } from '../../../documents/DocumentTypes'; import { BranchingTrailManager } from '../../../util/BranchingTrailManager'; -import { DocumentManager } from '../../../util/DocumentManager'; import { DragManager } from '../../../util/DragManager'; import { dropActionType } from '../../../util/DropActionTypes'; import { SettingsManager } from '../../../util/SettingsManager'; @@ -23,7 +22,6 @@ import { UndoStack } from '../../UndoStack'; import { DocumentLinksButton } from '../../nodes/DocumentLinksButton'; import { DocumentView } from '../../nodes/DocumentView'; import { LinkDescriptionPopup } from '../../nodes/LinkDescriptionPopup'; -import { CollectionStackedTimeline } from '../CollectionStackedTimeline'; import { CollectionSubView } from '../CollectionSubView'; import './CollectionLinearView.scss'; @@ -128,14 +126,14 @@ export class CollectionLinearView extends CollectionSubView() { </span> ); getCurrentlyPlayingUI = () => - !CollectionStackedTimeline.CurrentlyPlaying?.length ? null : ( + !DocumentView.CurrentlyPlaying?.length ? null : ( <span className="bottomPopup-background"> <span className="bottomPopup-text"> Currently playing: - {CollectionStackedTimeline.CurrentlyPlaying.map((clip, i) => ( + {DocumentView.CurrentlyPlaying.map((clip, i) => ( <> - <span className="audio-title" onPointerDown={() => DocumentManager.Instance.showDocument(clip.Document, { willZoomCentered: true })}> - {clip.Document.title + (i === CollectionStackedTimeline.CurrentlyPlaying.length - 1 ? ' ' : ',')} + <span className="audio-title" onPointerDown={() => DocumentView.showDocument(clip.Document, { willZoomCentered: true })}> + {clip.Document.title + (i === DocumentView.CurrentlyPlaying.length - 1 ? ' ' : ',')} </span> <FontAwesomeIcon icon={!clip.ComponentView?.IsPlaying?.() ? 'play' : 'pause'} size="lg" onPointerDown={() => clip.ComponentView?.TogglePause?.()} />{' '} <FontAwesomeIcon icon="times" size="lg" onPointerDown={() => clip.ComponentView?.Pause?.()} />{' '} diff --git a/src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx b/src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx index 49ba85524..931e2c5e0 100644 --- a/src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx +++ b/src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx @@ -5,7 +5,7 @@ import * as React from 'react'; import { Doc } from '../../../../fields/Doc'; import { NumCast, StrCast } from '../../../../fields/Types'; import { UndoManager } from '../../../util/UndoManager'; -import { StyleProp } from '../../StyleProvider'; +import { StyleProp } from '../../StyleProp'; import { StyleProviderFuncType } from '../../nodes/FieldView'; import { DimUnit } from './CollectionMulticolumnView'; diff --git a/src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx b/src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx index ad77c327d..cff0a8b4c 100644 --- a/src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx +++ b/src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx @@ -5,7 +5,7 @@ import * as React from 'react'; import { Doc } from '../../../../fields/Doc'; import { NumCast, StrCast } from '../../../../fields/Types'; import { UndoManager } from '../../../util/UndoManager'; -import { StyleProp } from '../../StyleProvider'; +import { StyleProp } from '../../StyleProp'; import { StyleProviderFuncType } from '../../nodes/FieldView'; import { DimUnit } from './CollectionMultirowView'; diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx index 406a7d626..b30954ffd 100644 --- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx +++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx @@ -15,16 +15,15 @@ import { ColumnType } from '../../../../fields/SchemaHeaderField'; import { BoolCast, Cast, DocCast, NumCast, StrCast } from '../../../../fields/Types'; import { DocUtils } from '../../../documents/DocUtils'; import { Docs, DocumentOptions, FInfo } from '../../../documents/Documents'; -import { DocumentManager } from '../../../util/DocumentManager'; import { DragManager } from '../../../util/DragManager'; import { dropActionType } from '../../../util/DropActionTypes'; -import { SelectionManager } from '../../../util/SelectionManager'; import { SettingsManager } from '../../../util/SettingsManager'; import { undoBatch, undoable } from '../../../util/UndoManager'; import { ContextMenu } from '../../ContextMenu'; import { EditableView } from '../../EditableView'; import { ObservableReactComponent } from '../../ObservableReactComponent'; -import { DefaultStyleProvider, StyleProp } from '../../StyleProvider'; +import { StyleProp } from '../../StyleProp'; +import { DefaultStyleProvider } from '../../StyleProvider'; import { Colors } from '../../global/globalEnums'; import { DocumentView } from '../../nodes/DocumentView'; import { FieldViewProps } from '../../nodes/FieldView'; @@ -92,12 +91,12 @@ export class CollectionSchemaView extends CollectionSubView() { } @computed get _selectedDocs() { - const selected = SelectionManager.Docs.filter(doc => Doc.AreProtosEqual(DocCast(doc.embedContainer), this.Document)); + const selected = DocumentView.SelectedDocs().filter(doc => Doc.AreProtosEqual(DocCast(doc.embedContainer), this.Document)); if (!selected.length) { // if no schema doc is directly selected, test if a child of a schema doc is selected (such as in the preview window) - const childOfSchemaDoc = SelectionManager.Docs.find(sel => DocumentManager.GetContextPath(sel, true).includes(this.Document)); + const childOfSchemaDoc = DocumentView.SelectedDocs().find(sel => DocumentView.getContextPath(sel, true).includes(this.Document)); if (childOfSchemaDoc) { - const contextPath = DocumentManager.GetContextPath(childOfSchemaDoc, true); + const contextPath = DocumentView.getContextPath(childOfSchemaDoc, true); return [contextPath[contextPath.indexOf(childOfSchemaDoc) - 1]]; // the schema doc that is "selected" by virtue of one of its children being selected } } @@ -175,6 +174,7 @@ export class CollectionSchemaView extends CollectionSubView() { document.removeEventListener('keydown', this.onKeyDown); } + isUnstyledView = returnTrue; // used by style provide via ViewBoxInterface rowIndex = (doc: Doc) => this.sortedDocs.docs.indexOf(doc); @action @@ -190,7 +190,7 @@ export class CollectionSchemaView extends CollectionSubView() { !e.shiftKey && this.clearSelection(); const newDoc = this.sortedDocs.docs[lastIndex + 1]; if (this._selectedDocs.includes(newDoc)) { - SelectionManager.DeselectView(DocumentManager.Instance.getFirstDocumentView(curDoc)); + DocumentView.DeselectView(DocumentView.getFirstDocumentView(curDoc)); } else { this.addDocToSelection(newDoc, e.shiftKey); this._selectedCell && (this._selectedCell[0] = newDoc); @@ -209,7 +209,7 @@ export class CollectionSchemaView extends CollectionSubView() { if (firstIndex > 0 && firstIndex < this.childDocs.length) { !e.shiftKey && this.clearSelection(); const newDoc = this.sortedDocs.docs[firstIndex - 1]; - if (this._selectedDocs.includes(newDoc)) SelectionManager.DeselectView(DocumentManager.Instance.getFirstDocumentView(curDoc)); + if (this._selectedDocs.includes(newDoc)) DocumentView.DeselectView(DocumentView.getFirstDocumentView(curDoc)); else { this.addDocToSelection(newDoc, e.shiftKey); this._selectedCell && (this._selectedCell[0] = newDoc); @@ -416,12 +416,12 @@ export class CollectionSchemaView extends CollectionSubView() { @action addDocToSelection = (doc: Doc, extendSelection: boolean) => { - const rowDocView = DocumentManager.Instance.getDocumentView(doc); - if (rowDocView) SelectionManager.SelectView(rowDocView, extendSelection); + const rowDocView = DocumentView.getDocumentView(doc); + if (rowDocView) DocumentView.SelectView(rowDocView, extendSelection); }; @action - clearSelection = () => SelectionManager.DeselectAll(); + clearSelection = () => DocumentView.DeselectAll(); selectRows = (doc: Doc, lastSelected: Doc) => { const index = this.rowIndex(doc); @@ -476,9 +476,9 @@ export class CollectionSchemaView extends CollectionSubView() { this.dataDoc[this.fieldKey ?? 'data'] = new List<Doc>([...removed, ...draggedDocs, ...pushedDocs]); this.clearSelection(); draggedDocs.forEach(doc => { - const draggedView = DocumentManager.Instance.getFirstDocumentView(doc); - if (draggedView) DocumentManager.Instance.RemoveView(draggedView); - DocumentManager.Instance.AddViewRenderedCb(doc, dv => dv.select(true)); + const draggedView = DocumentView.getFirstDocumentView(doc); + if (draggedView) DocumentView.removeView(draggedView); + DocumentView.addViewRenderedCb(doc, dv => dv.select(true)); }); return true; } diff --git a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx index dfef3aa48..ee6987e89 100644 --- a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx +++ b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx @@ -19,9 +19,7 @@ import { ColumnType } from '../../../../fields/SchemaHeaderField'; import { BoolCast, Cast, DateCast, DocCast, FieldValue, StrCast, toList } from '../../../../fields/Types'; import { ImageField } from '../../../../fields/URLField'; import { FInfo, FInfoFieldType } from '../../../documents/Documents'; -import { DocFocusOrOpen } from '../../../util/DocumentManager'; import { dropActionType } from '../../../util/DropActionTypes'; -import { SettingsManager } from '../../../util/SettingsManager'; import { SnappingManager } from '../../../util/SnappingManager'; import { Transform } from '../../../util/Transform'; import { undoBatch, undoable } from '../../../util/UndoManager'; @@ -29,7 +27,7 @@ import { EditableView } from '../../EditableView'; import { ObservableReactComponent } from '../../ObservableReactComponent'; import { DefaultStyleProvider } from '../../StyleProvider'; import { Colors } from '../../global/globalEnums'; -import { returnEmptyDocViewList } from '../../nodes/DocumentView'; +import { DocFocusOrOpen, returnEmptyDocViewList } from '../../nodes/DocumentView'; import { FieldViewProps } from '../../nodes/FieldView'; import { KeyValueBox } from '../../nodes/KeyValueBox'; import { FormattedTextBox } from '../../nodes/formattedText/FormattedTextBox'; @@ -300,8 +298,8 @@ export class SchemaDateCell extends ObservableReactComponent<SchemaTableCellProp icon={<FontAwesomeIcon size="sm" icon="caret-down" />} size={Size.XSMALL} type={Type.TERT} - color={SettingsManager.userColor} - background={SettingsManager.userBackgroundColor} + color={SnappingManager.userColor} + background={SnappingManager.userBackgroundColor} popup={ <div style={{ width: 'fit-content', height: '200px' }}> <DatePicker open dateFormat="Pp" selected={this.date?.date ?? Date.now()} onChange={this.handleChange} /> diff --git a/src/client/views/global/globalScripts.ts b/src/client/views/global/globalScripts.ts index e75b01ab6..c595681b7 100644 --- a/src/client/views/global/globalScripts.ts +++ b/src/client/views/global/globalScripts.ts @@ -10,7 +10,6 @@ import { Gestures } from '../../../pen-gestures/GestureTypes'; import { DocumentType } from '../../documents/DocumentTypes'; import { LinkManager } from '../../util/LinkManager'; import { ScriptingGlobals } from '../../util/ScriptingGlobals'; -import { SelectionManager } from '../../util/SelectionManager'; import { UndoManager, undoable } from '../../util/UndoManager'; import { GestureOverlay } from '../GestureOverlay'; import { InkingStroke } from '../InkingStroke'; @@ -26,14 +25,14 @@ import { RichTextMenu } from '../nodes/formattedText/RichTextMenu'; // eslint-disable-next-line prefer-arrow-callback ScriptingGlobals.add(function IsNoneSelected() { - return SelectionManager.Views.length <= 0; + return DocumentView.Selected().length <= 0; }, 'are no document selected'); // toggle: Set overlay status of selected document // eslint-disable-next-line prefer-arrow-callback ScriptingGlobals.add(function setView(view: string, getSelected: boolean) { - if (getSelected) return SelectionManager.Docs; - const selected = SelectionManager.Docs.lastElement(); + if (getSelected) return DocumentView.SelectedDocs(); + const selected = DocumentView.SelectedDocs().lastElement(); selected ? (selected._type_collection = view) : console.log('[FontIconBox.tsx] changeView failed'); return undefined; }); @@ -41,7 +40,7 @@ ScriptingGlobals.add(function setView(view: string, getSelected: boolean) { // toggle: Set overlay status of selected document // eslint-disable-next-line prefer-arrow-callback ScriptingGlobals.add(function setBackgroundColor(color?: string, checkResult?: boolean) { - const selectedViews = SelectionManager.Views; + const selectedViews = DocumentView.Selected(); if (Doc.ActiveTool !== InkTool.None) { if (checkResult) { return ActiveFillColor(); @@ -71,7 +70,7 @@ ScriptingGlobals.add(function setBackgroundColor(color?: string, checkResult?: b } }); } else { - const selected = SelectionManager.Docs.length ? SelectionManager.Docs : LinkManager.Instance.currentLink ? [LinkManager.Instance.currentLink] : []; + const selected = DocumentView.SelectedDocs().length ? DocumentView.SelectedDocs() : LinkManager.Instance.currentLink ? [LinkManager.Instance.currentLink] : []; if (checkResult) { return selected.lastElement()?._backgroundColor ?? 'transparent'; } @@ -90,10 +89,10 @@ ScriptingGlobals.add(function setDefaultTemplate(checkResult?: boolean) { // eslint-disable-next-line prefer-arrow-callback ScriptingGlobals.add(function setHeaderColor(color?: string, checkResult?: boolean) { if (checkResult) { - return SelectionManager.Views.length ? StrCast(SelectionManager.Docs.lastElement().layout_headingColor) : Doc.SharingDoc().headingColor; + return DocumentView.Selected().length ? StrCast(DocumentView.SelectedDocs().lastElement().layout_headingColor) : Doc.SharingDoc().headingColor; } - if (SelectionManager.Views.length) { - SelectionManager.Docs.forEach(doc => { + if (DocumentView.Selected().length) { + DocumentView.SelectedDocs().forEach(doc => { doc[DocData].layout_headingColor = color === 'transparent' ? undefined : color; doc.layout_showTitle = color === 'transparent' ? undefined : StrCast(doc.layout_showTitle, 'title'); }); @@ -108,7 +107,7 @@ ScriptingGlobals.add(function setHeaderColor(color?: string, checkResult?: boole // toggle: Set overlay status of selected document // eslint-disable-next-line prefer-arrow-callback ScriptingGlobals.add(function toggleOverlay(checkResult?: boolean) { - const selected = SelectionManager.Views.length ? SelectionManager.Views[0] : undefined; + const selected = DocumentView.Selected().length ? DocumentView.Selected()[0] : undefined; if (checkResult) { if (NumCast(selected?.Document.z) >= 1) return true; return false; @@ -119,7 +118,7 @@ ScriptingGlobals.add(function toggleOverlay(checkResult?: boolean) { // eslint-disable-next-line prefer-arrow-callback ScriptingGlobals.add(function showFreeform(attr: 'center' | 'grid' | 'snaplines' | 'clusters' | 'viewAll' | 'fitOnce', checkResult?: boolean, persist?: boolean) { - const selected = SelectionManager.Docs.lastElement(); + const selected = DocumentView.SelectedDocs().lastElement(); // prettier-ignore const map: Map<'center' |'grid' | 'snaplines' | 'clusters' | 'viewAll' | 'fitOnce', { waitForRender?: boolean, checkResult: (doc:Doc) => any; setDoc: (doc:Doc, dv:DocumentView) => void;}> = new Map([ ['grid', { @@ -153,7 +152,7 @@ ScriptingGlobals.add(function showFreeform(attr: 'center' | 'grid' | 'snaplines' return map.get(attr)?.checkResult(selected); } const batch = map.get(attr)?.waitForRender ? UndoManager.StartBatch('set freeform attribute') : { end: () => {} }; - SelectionManager.Views.map(dv => map.get(attr)?.setDoc(dv.layoutDoc, dv)); + DocumentView.Selected().map(dv => map.get(attr)?.setDoc(dv.layoutDoc, dv)); setTimeout(() => batch.end(), 100); return undefined; }); @@ -347,7 +346,7 @@ ScriptingGlobals.add(setActiveTool, 'sets the active ink tool mode'); // toggle: Set overlay status of selected document // eslint-disable-next-line prefer-arrow-callback ScriptingGlobals.add(function setInkProperty(option: 'inkMask' | 'labels' | 'fillColor' | 'strokeWidth' | 'strokeColor', value: any, checkResult?: boolean) { - const selected = SelectionManager.Docs.lastElement() ?? Doc.UserDoc(); + const selected = DocumentView.SelectedDocs().lastElement() ?? Doc.UserDoc(); // prettier-ignore const map: Map<'inkMask' | 'labels' | 'fillColor' | 'strokeWidth' | 'strokeColor', { checkResult: () => any; setInk: (doc: Doc) => void; setMode: () => void }> = new Map([ ['inkMask', { @@ -381,7 +380,20 @@ ScriptingGlobals.add(function setInkProperty(option: 'inkMask' | 'labels' | 'fil return map.get(option)?.checkResult(); } map.get(option)?.setMode(); - SelectionManager.Docs.filter(doc => doc._layout_isSvg).map(doc => map.get(option)?.setInk(doc)); + DocumentView.SelectedDocs() + .filter(doc => doc._layout_isSvg) + .map(doc => map.get(option)?.setInk(doc)); + return undefined; +}); + +// eslint-disable-next-line prefer-arrow-callback +ScriptingGlobals.add(function toggleRaiseOnDrag(readOnly?: boolean) { + if (readOnly) { + return DocumentView.Selected().some(dv => dv.Document.keepZWhenDragged); + } + DocumentView.Selected().forEach(dv => { + dv.Document.keepZWhenDragged = !dv.Document.keepZWhenDragged; + }); return undefined; }); @@ -390,7 +402,7 @@ ScriptingGlobals.add(function setInkProperty(option: 'inkMask' | 'labels' | 'fil * */ // eslint-disable-next-line prefer-arrow-callback ScriptingGlobals.add(function webSetURL(url: string, checkResult?: boolean) { - const selected = SelectionManager.Views.lastElement(); + const selected = DocumentView.Selected().lastElement(); if (selected?.Document.type === DocumentType.WEB) { if (checkResult) { return StrCast(selected.Document.data, Cast(selected.Document.data, WebField, null)?.url?.href); @@ -401,7 +413,7 @@ ScriptingGlobals.add(function webSetURL(url: string, checkResult?: boolean) { }); // eslint-disable-next-line prefer-arrow-callback ScriptingGlobals.add(function webForward(checkResult?: boolean) { - const selected = SelectionManager.Views.lastElement()?.ComponentView as WebBox; + const selected = DocumentView.Selected().lastElement()?.ComponentView as WebBox; if (checkResult) { return selected?.forward(checkResult) ? undefined : 'lightGray'; } @@ -410,24 +422,24 @@ ScriptingGlobals.add(function webForward(checkResult?: boolean) { }); // eslint-disable-next-line prefer-arrow-callback ScriptingGlobals.add(function webBack() { - const selected = SelectionManager.Views.lastElement()?.ComponentView as WebBox; + const selected = DocumentView.Selected().lastElement()?.ComponentView as WebBox; selected?.back(); }); // eslint-disable-next-line prefer-arrow-callback ScriptingGlobals.add(function videoSnapshot() { - const selected = SelectionManager.Views.lastElement()?.ComponentView as VideoBox; + const selected = DocumentView.Selected().lastElement()?.ComponentView as VideoBox; selected?.Snapshot(); }); // eslint-disable-next-line prefer-arrow-callback ScriptingGlobals.add(function imageSetPixelSize() { - const selected = SelectionManager.Views.lastElement()?.ComponentView as ImageBox; + const selected = DocumentView.Selected().lastElement()?.ComponentView as ImageBox; selected?.setNativeSize(); }); // eslint-disable-next-line prefer-arrow-callback ScriptingGlobals.add(function imageRotate90() { - const selected = SelectionManager.Views.lastElement()?.ComponentView as ImageBox; + const selected = DocumentView.Selected().lastElement()?.ComponentView as ImageBox; selected?.rotate(); }); @@ -436,7 +448,7 @@ ScriptingGlobals.add(function imageRotate90() { * */ // eslint-disable-next-line prefer-arrow-callback ScriptingGlobals.add(function toggleSchemaPreview(checkResult?: boolean) { - const selected = SelectionManager.Docs.lastElement(); + const selected = DocumentView.SelectedDocs().lastElement(); if (checkResult && selected) { const result: boolean = NumCast(selected.schema_previewWidth) > 0; if (result) return Colors.MEDIUM_BLUE; @@ -453,7 +465,7 @@ ScriptingGlobals.add(function toggleSchemaPreview(checkResult?: boolean) { }); // eslint-disable-next-line prefer-arrow-callback ScriptingGlobals.add(function toggleSingleLineSchema(checkResult?: boolean) { - const selected = SelectionManager.Docs.lastElement(); + const selected = DocumentView.SelectedDocs().lastElement(); if (checkResult && selected) { return NumCast(selected._schema_singleLine) > 0 ? Colors.MEDIUM_BLUE : 'transparent'; } @@ -468,7 +480,7 @@ ScriptingGlobals.add(function toggleSingleLineSchema(checkResult?: boolean) { */ // eslint-disable-next-line prefer-arrow-callback ScriptingGlobals.add(function setGroupBy(key: string, checkResult?: boolean) { - SelectionManager.Docs.forEach(doc => { doc._text_fontFamily = key; }); // prettier-ignore + DocumentView.SelectedDocs().forEach(doc => { doc._text_fontFamily = key; }); // prettier-ignore const editorView = RichTextMenu.Instance?.TextView?.EditorView; if (checkResult) { return StrCast((editorView ? RichTextMenu.Instance : Doc.UserDoc())?.fontFamily); diff --git a/src/client/views/linking/LinkMenuGroup.tsx b/src/client/views/linking/LinkMenuGroup.tsx index f99a18db2..cd735318e 100644 --- a/src/client/views/linking/LinkMenuGroup.tsx +++ b/src/client/views/linking/LinkMenuGroup.tsx @@ -8,7 +8,6 @@ import { Doc, StrListCast } from '../../../fields/Doc'; import { Id } from '../../../fields/FieldSymbols'; import { Cast, DocCast } from '../../../fields/Types'; import { DocumentType } from '../../documents/DocumentTypes'; -import { LinkManager } from '../../util/LinkManager'; import { DocumentView } from '../nodes/DocumentView'; import './LinkMenu.scss'; import { LinkMenuItem } from './LinkMenuItem'; @@ -59,8 +58,7 @@ export class LinkMenuGroup extends React.Component<LinkMenuGroupProps> { ? this.props.docView._props.LayoutTemplateString?.includes('link_anchor_1') ? DocCast(linkDoc.link_anchor_2) : DocCast(linkDoc.link_anchor_1) - : LinkManager.getOppositeAnchor(linkDoc, sourceDoc) || - LinkManager.getOppositeAnchor(linkDoc, Cast(linkDoc.link_anchor_2, Doc, null)?.annotationOn === sourceDoc ? Cast(linkDoc.link_anchor_2, Doc, null) : Cast(linkDoc.link_anchor_1, Doc, null)); + : Doc.getOppositeAnchor(linkDoc, sourceDoc) || Doc.getOppositeAnchor(linkDoc, Cast(linkDoc.link_anchor_2, Doc, null)?.annotationOn === sourceDoc ? Cast(linkDoc.link_anchor_2, Doc, null) : Cast(linkDoc.link_anchor_1, Doc, null)); return !destDoc || !sourceDoc ? null : ( <LinkMenuItem key={linkDoc[Id]} diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx index df3accb0d..9ce04ffac 100644 --- a/src/client/views/linking/LinkMenuItem.tsx +++ b/src/client/views/linking/LinkMenuItem.tsx @@ -11,13 +11,10 @@ import { Doc } from '../../../fields/Doc'; import { Cast, DocCast, StrCast } from '../../../fields/Types'; import { WebField } from '../../../fields/URLField'; import { DocumentType } from '../../documents/DocumentTypes'; -import { DocumentManager } from '../../util/DocumentManager'; import { DragManager } from '../../util/DragManager'; import { dropActionType } from '../../util/DropActionTypes'; -import { LinkFollower } from '../../util/LinkFollower'; import { LinkManager } from '../../util/LinkManager'; -import { SelectionManager } from '../../util/SelectionManager'; -import { SettingsManager } from '../../util/SettingsManager'; +import { SnappingManager } from '../../util/SnappingManager'; import { undoBatch } from '../../util/UndoManager'; import { ObservableReactComponent } from '../ObservableReactComponent'; import { DocumentView, DocumentViewInternal } from '../nodes/DocumentView'; @@ -38,7 +35,7 @@ interface LinkMenuItemProps { // drag links and drop link targets (embedding them if needed) export async function StartLinkTargetsDrag(dragEle: HTMLElement, docView: DocumentView, downX: number, downY: number, sourceDoc: Doc, specificLinks?: Doc[]) { - const draggedDocs = (specificLinks || LinkManager.Links(sourceDoc)).map(link => LinkManager.getOppositeAnchor(link, sourceDoc)).filter(l => l) as Doc[]; + const draggedDocs = (specificLinks || LinkManager.Links(sourceDoc)).map(link => Doc.getOppositeAnchor(link, sourceDoc)).filter(l => l) as Doc[]; if (draggedDocs.length) { const moddrag: Doc[] = []; @@ -81,7 +78,7 @@ export class LinkMenuItem extends ObservableReactComponent<LinkMenuItemProps> { onIconDown = (e: React.PointerEvent) => { setupMoveUpEvents(this, e, returnFalse, returnFalse, () => { - const ancestor = DocumentManager.LinkCommonAncestor(this._props.linkDoc); + const ancestor = DocumentView.linkCommonAncestor(this._props.linkDoc); if (!ancestor?.ComponentView?.removeDocument?.(this._props.linkDoc)) { ancestor?.ComponentView?.addDocument?.(this._props.linkDoc); } @@ -105,14 +102,14 @@ export class LinkMenuItem extends ObservableReactComponent<LinkMenuItemProps> { Doc.ActivePresentation = trail; DocumentViewInternal.addDocTabFunc(trail, OpenWhere.replaceRight); } else { - SelectionManager.SelectView(this._props.docView, false); + DocumentView.SelectView(this._props.docView, false); LinkManager.Instance.currentLink = this._props.linkDoc === LinkManager.Instance.currentLink ? undefined : this._props.linkDoc; LinkManager.Instance.currentLinkAnchor = LinkManager.Instance.currentLink ? this.sourceAnchor : undefined; - if ((SettingsManager.Instance.propertiesWidth ?? 0) < 100) { + if ((SnappingManager.PropertiesWidth ?? 0) < 100) { setTimeout( action(() => { - SettingsManager.Instance.propertiesWidth = 250; + SnappingManager.SetPropertiesWidth(250); }) ); } @@ -126,10 +123,12 @@ export class LinkMenuItem extends ObservableReactComponent<LinkMenuItemProps> { this, e, moveEv => { - const eleClone: any = this._drag.current!.cloneNode(true); - eleClone.style.transform = `translate(${moveEv.x}px, ${moveEv.y}px)`; - StartLinkTargetsDrag(eleClone, this._props.docView, moveEv.x, moveEv.y, this._props.sourceDoc, [this._props.linkDoc]); - this._props.clearLinkEditor?.(); + const eleClone: any = this._drag.current?.cloneNode(true); + if (eleClone) { + eleClone.style.transform = `translate(${moveEv.x}px, ${moveEv.y}px)`; + StartLinkTargetsDrag(eleClone, this._props.docView, moveEv.x, moveEv.y, this._props.sourceDoc, [this._props.linkDoc]); + this._props.clearLinkEditor?.(); + } return true; }, emptyFunction, @@ -146,13 +145,13 @@ export class LinkMenuItem extends ObservableReactComponent<LinkMenuItemProps> { : undefined; if (focusDoc) this._props.docView._props.focus(focusDoc, { instant: true }); - LinkFollower.FollowLink(this._props.linkDoc, this._props.sourceDoc, false); + DocumentView.FollowLink(this._props.linkDoc, this._props.sourceDoc, false); } } ); }; - deleteLink = (e: React.PointerEvent): void => setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoBatch(action(() => LinkManager.Instance.deleteLink(this._props.linkDoc)))); + deleteLink = (e: React.PointerEvent): void => setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoBatch(action(() => Doc.DeleteLink?.(this._props.linkDoc)))); @observable _hover = false; docView = () => this._props.docView; render() { @@ -181,7 +180,7 @@ export class LinkMenuItem extends ObservableReactComponent<LinkMenuItemProps> { style={{ fontSize: this._hover ? 'larger' : undefined, fontWeight: this._hover ? 'bold' : undefined, - background: LinkManager.Instance.currentLink === this._props.linkDoc ? SettingsManager.userVariantColor : SettingsManager.userBackgroundColor, + background: LinkManager.Instance.currentLink === this._props.linkDoc ? SnappingManager.userVariantColor : SnappingManager.userBackgroundColor, }}> <div className="linkMenu-item-content expand-two"> <div diff --git a/src/client/views/newlightbox/ButtonMenu/ButtonMenu.tsx b/src/client/views/newlightbox/ButtonMenu/ButtonMenu.tsx index 72d81e9eb..e48e993cf 100644 --- a/src/client/views/newlightbox/ButtonMenu/ButtonMenu.tsx +++ b/src/client/views/newlightbox/ButtonMenu/ButtonMenu.tsx @@ -2,7 +2,6 @@ import { action } from 'mobx'; import * as React from 'react'; import { Doc } from '../../../../fields/Doc'; import { InkTool } from '../../../../fields/InkField'; -import { SelectionManager } from '../../../util/SelectionManager'; import { SnappingManager } from '../../../util/SnappingManager'; import { CollectionDockingView } from '../../collections/CollectionDockingView'; import { OpenWhereMod } from '../../nodes/OpenWhere'; @@ -26,7 +25,7 @@ export const ButtonMenu = (props: IButtonMenu) => { onClick={e => { e.stopPropagation(); CollectionDockingView.AddSplit(NewLightboxView.LightboxDoc || NewLightboxView.LightboxDoc!, OpenWhereMod.none); - SelectionManager.DeselectAll(); + DocumentView.DeselectAll(); NewLightboxView.SetNewLightboxDoc(undefined); }}></div> <div diff --git a/src/client/views/newlightbox/NewLightboxView.tsx b/src/client/views/newlightbox/NewLightboxView.tsx index dc6bf3e9c..558ce7e38 100644 --- a/src/client/views/newlightbox/NewLightboxView.tsx +++ b/src/client/views/newlightbox/NewLightboxView.tsx @@ -9,16 +9,11 @@ import { emptyFunction } from '../../../Utils'; import { CreateLinkToActiveAudio, Doc, DocListCast, Opt } from '../../../fields/Doc'; import { InkTool } from '../../../fields/InkField'; import { Cast, NumCast, StrCast, toList } from '../../../fields/Types'; -import { DocumentManager } from '../../util/DocumentManager'; -import { LinkManager } from '../../util/LinkManager'; -import { SelectionManager } from '../../util/SelectionManager'; import { SnappingManager } from '../../util/SnappingManager'; import { Transform } from '../../util/Transform'; import { GestureOverlay } from '../GestureOverlay'; import { LightboxView } from '../LightboxView'; import { DefaultStyleProvider } from '../StyleProvider'; -import { CollectionStackedTimeline } from '../collections/CollectionStackedTimeline'; -import { TabDocView } from '../collections/TabDocView'; import { DocumentView } from '../nodes/DocumentView'; import { OpenWhere } from '../nodes/OpenWhere'; import { ExploreView } from './ExploreView'; @@ -79,7 +74,7 @@ export class NewLightboxView extends React.Component<LightboxViewProps> { } } public static AddDocTab = (docsIn: Doc | Doc[], location: OpenWhere, layoutTemplate?: Doc | string) => { - SelectionManager.DeselectAll(); + DocumentView.DeselectAll(); const doc = toList(docsIn).lastElement(); return ( doc && @@ -110,8 +105,8 @@ export class NewLightboxView extends React.Component<LightboxViewProps> { } else { const l = CreateLinkToActiveAudio(() => doc).lastElement(); l && (Cast(l.link_anchor_2, Doc, null).backgroundColor = 'lightgreen'); - CollectionStackedTimeline.CurrentlyPlaying?.forEach(dv => dv.ComponentView?.Pause?.()); - // TabDocView.PinDoc(doc, { hidePresBox: true }); + DocumentView.CurrentlyPlaying?.forEach(dv => dv.ComponentView?.Pause?.()); + // DocumentView.PinDoc(doc, { hidePresBox: true }); this._history ? this._history.push({ doc, target }) : (this._history = [{ doc, target }]); if (doc !== LightboxView.LightboxDoc) { this._savedState = { @@ -130,7 +125,7 @@ export class NewLightboxView extends React.Component<LightboxViewProps> { ...future .slice() .sort((a, b) => NumCast(b._timecodeToShow) - NumCast(a._timecodeToShow)) - .sort((a, b) => LinkManager.Links(a).length - LinkManager.Links(b).length), + .sort((a, b) => Doc.Links(a).length - Doc.Links(b).length), ]; } this._doc = doc; @@ -148,11 +143,11 @@ export class NewLightboxView extends React.Component<LightboxViewProps> { @action public static Next() { const doc = NewLightboxView._doc!; const target = (NewLightboxView._docTarget = this._future?.pop()); - const targetDocView = target && DocumentManager.Instance.getLightboxDocumentView(target); + const targetDocView = target && DocumentView.getLightboxDocumentView(target); if (targetDocView && target) { const l = CreateLinkToActiveAudio(() => targetDocView.ComponentView?.getAnchor?.(true) || target).lastElement(); l && (Cast(l.link_anchor_2, Doc, null).backgroundColor = 'lightgreen'); - DocumentManager.Instance.showDocument(target, { willZoomCentered: true, zoomScale: 0.9 }); + DocumentView.showDocument(target, { willZoomCentered: true, zoomScale: 0.9 }); if (NewLightboxView._history?.lastElement().target !== target) NewLightboxView._history?.push({ doc, target }); } else if (!target && NewLightboxView.path.length) { const saved = NewLightboxView._savedState; @@ -182,10 +177,10 @@ export class NewLightboxView extends React.Component<LightboxViewProps> { return; } const { doc, target } = NewLightboxView._history?.lastElement(); - const docView = DocumentManager.Instance.getLightboxDocumentView(target || doc); + const docView = DocumentView.getLightboxDocumentView(target || doc); if (docView) { NewLightboxView._docTarget = target; - target && DocumentManager.Instance.showDocument(target, { willZoomCentered: true, zoomScale: 0.9 }); + target && DocumentView.showDocument(target, { willZoomCentered: true, zoomScale: 0.9 }); } else { NewLightboxView.SetNewLightboxDoc(doc, target); } @@ -277,7 +272,7 @@ export class NewLightboxView extends React.Component<LightboxViewProps> { removeDocument={undefined} whenChildContentsActiveChanged={emptyFunction} addDocTab={this.addDocTab} - pinToPres={TabDocView.PinDoc} + pinToPres={DocumentView.PinDoc} focus={emptyFunction} /> </GestureOverlay> @@ -316,8 +311,8 @@ export class NewLightboxView extends React.Component<LightboxViewProps> { if (coll) { const fieldKey = Doc.LayoutFieldKey(coll); const contents = [...DocListCast(coll[fieldKey]), ...DocListCast(coll[fieldKey + '_annotations'])]; - const links = LinkManager.Links(coll) - .map(link => LinkManager.getOppositeAnchor(link, coll)) + const links = Doc.Links(coll) + .map(link => Doc.getOppositeAnchor(link, coll)) .filter(doc => doc) .map(doc => doc!); NewLightboxView.SetNewLightboxDoc(coll, undefined, contents.length ? contents : links); diff --git a/src/client/views/newlightbox/components/Recommendation/Recommendation.tsx b/src/client/views/newlightbox/components/Recommendation/Recommendation.tsx index 23027808f..375408d01 100644 --- a/src/client/views/newlightbox/components/Recommendation/Recommendation.tsx +++ b/src/client/views/newlightbox/components/Recommendation/Recommendation.tsx @@ -1,12 +1,12 @@ import * as React from 'react'; -import { IRecommendation } from './utils'; -import './Recommendation.scss'; -import { getType } from '../../utils'; import { FaEyeSlash } from 'react-icons/fa'; -import { NewLightboxView } from '../../NewLightboxView'; -import { DocumentManager } from '../../../../util/DocumentManager'; import { Doc } from '../../../../../fields/Doc'; import { Docs } from '../../../../documents/Documents'; +import { DocumentView } from '../../../nodes/DocumentView'; +import { NewLightboxView } from '../../NewLightboxView'; +import { getType } from '../../utils'; +import './Recommendation.scss'; +import { IRecommendation } from './utils'; export const Recommendation = (props: IRecommendation) => { const { title, data, type, text, transcript, loading, source, previewUrl, related_concepts, distance, docId } = props; @@ -17,7 +17,7 @@ export const Recommendation = (props: IRecommendation) => { onClick={() => { let doc: Doc | null = null; if (source == 'Dash' && docId) { - const docView = DocumentManager.Instance.getDocumentViewsById(docId).lastElement(); + const docView = DocumentView.getDocumentViewsById(docId).lastElement(); if (docView) { doc = docView.Document; } diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx index 4697491e0..9deed4de4 100644 --- a/src/client/views/nodes/AudioBox.tsx +++ b/src/client/views/nodes/AudioBox.tsx @@ -18,14 +18,15 @@ import { DocumentType } from '../../documents/DocumentTypes'; import { DocUtils } from '../../documents/DocUtils'; import { Networking } from '../../Network'; import { DragManager } from '../../util/DragManager'; -import { LinkManager } from '../../util/LinkManager'; import { undoBatch } from '../../util/UndoManager'; import { CollectionStackedTimeline, TrimScope } from '../collections/CollectionStackedTimeline'; import { ContextMenu } from '../ContextMenu'; import { ContextMenuProps } from '../ContextMenuItem'; import { ViewBoxAnnotatableComponent } from '../DocComponent'; +import { DocViewUtils } from '../DocViewUtils'; import { PinDocView, PinProps } from '../PinFuncs'; import './AudioBox.scss'; +import { DocumentView } from './DocumentView'; import { FieldView, FieldViewProps } from './FieldView'; import { OpenWhere } from './OpenWhere'; @@ -99,7 +100,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { return this._props.PanelHeight() < 50; } // used to collapse timeline when node is shrunk @computed get links() { - return LinkManager.Links(this.dataDoc); + return Doc.Links(this.dataDoc); } @computed get mediaState() { return this.dataDoc.mediaState as mediaState; @@ -218,9 +219,9 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { @action removeCurrentlyPlaying = () => { const docView = this.DocumentView?.(); - if (CollectionStackedTimeline.CurrentlyPlaying && docView) { - const index = CollectionStackedTimeline.CurrentlyPlaying.indexOf(docView); - index !== -1 && CollectionStackedTimeline.CurrentlyPlaying.splice(index, 1); + if (DocumentView.CurrentlyPlaying && docView) { + const index = DocumentView.CurrentlyPlaying.indexOf(docView); + index !== -1 && DocumentView.CurrentlyPlaying.splice(index, 1); } }; @@ -228,11 +229,11 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { @action addCurrentlyPlaying = () => { const docView = this.DocumentView?.(); - if (!CollectionStackedTimeline.CurrentlyPlaying) { - CollectionStackedTimeline.CurrentlyPlaying = []; + if (!DocumentView.CurrentlyPlaying) { + DocumentView.CurrentlyPlaying = []; } - if (docView && CollectionStackedTimeline.CurrentlyPlaying.indexOf(docView) === -1) { - CollectionStackedTimeline.CurrentlyPlaying.push(docView); + if (docView && DocumentView.CurrentlyPlaying.indexOf(docView) === -1) { + DocumentView.CurrentlyPlaying.push(docView); } }; @@ -251,7 +252,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { this._stream = await navigator.mediaDevices.getUserMedia({ audio: true }); this._recorder = new MediaRecorder(this._stream); this.dataDoc[this.fieldKey + '_recordingStart'] = new DateField(); - DocUtils.ActiveRecordings.push(this); + DocViewUtils.ActiveRecordings.push(this); this._recorder.ondataavailable = async (e: any) => { const [{ result }] = await Networking.UploadFilesToServer({ file: e.data }); if (!(result instanceof Error)) { @@ -278,8 +279,8 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { this.dataDoc[this.fieldKey + '_duration'] = (now - this._recordStart - this._pausedTime) / 1000; this.mediaState = mediaState.Paused; this._stream?.getAudioTracks()[0].stop(); - const ind = DocUtils.ActiveRecordings.indexOf(this); - ind !== -1 && DocUtils.ActiveRecordings.splice(ind, 1); + const ind = DocViewUtils.ActiveRecordings.indexOf(this); + ind !== -1 && DocViewUtils.ActiveRecordings.splice(ind, 1); } }; diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index 691d07e31..685a5aca4 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -5,18 +5,16 @@ import { OmitKeys } from '../../../ClientUtils'; import { numberRange } from '../../../Utils'; import { Doc, DocListCast, Opt } from '../../../fields/Doc'; import { TransitionTimer } from '../../../fields/DocSymbols'; +import { InkField } from '../../../fields/InkField'; import { List } from '../../../fields/List'; import { listSpec } from '../../../fields/Schema'; import { ComputedField } from '../../../fields/ScriptField'; import { Cast, NumCast, StrCast } from '../../../fields/Types'; import { TraceMobx } from '../../../fields/util'; -import { DocumentManager } from '../../util/DocumentManager'; import { DragManager } from '../../util/DragManager'; import { ScriptingGlobals } from '../../util/ScriptingGlobals'; -import { SelectionManager } from '../../util/SelectionManager'; import { DocComponent } from '../DocComponent'; -import { StyleProp } from '../StyleProvider'; -import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView'; +import { StyleProp } from '../StyleProp'; import './CollectionFreeFormDocumentView.scss'; import { DocumentView, DocumentViewProps } from './DocumentView'; import { FieldViewProps } from './FieldView'; @@ -43,7 +41,8 @@ interface freeFormProps { export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps { RenderCutoffProvider: (doc: Doc) => boolean; - CollectionFreeFormView: CollectionFreeFormView; + isAnyChildContentActive: () => boolean; + parent: any; } @observer export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeFormDocumentViewProps & freeFormProps>() { @@ -66,7 +65,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF ]; // fields that are configured to be animatable using animation frames public static animStringFields = ['backgroundColor', 'color', 'fillColor']; // fields that are configured to be animatable using animation frames public static animDataFields = (doc: Doc) => (Doc.LayoutFieldKey(doc) ? [Doc.LayoutFieldKey(doc)] : []); // fields that are configured to be animatable using animation frames - public static from(dv?: DocumentView) { + public static from(dv?: DocumentView): CollectionFreeFormDocumentView | undefined { return dv?._props.parent instanceof CollectionFreeFormDocumentView ? dv._props.parent : undefined; } @@ -120,7 +119,6 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF ); } - CollectionFreeFormView = this.props.CollectionFreeFormView; // needed for type checking // this way, downstream code only invalidates when it uses a specific prop, not when any prop changes DataTransition = () => this.Transition || StrCast(this.Document.dataTransition); // prettier-ignore RenderCutoffProvider = this.props.RenderCutoffProvider; // needed for type checking @@ -185,13 +183,32 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF const currentFrame = Cast(doc._currentFrame, 'number', null); if (currentFrame === undefined) { doc._currentFrame = 0; - CollectionFreeFormDocumentView.setupKeyframes(childDocs, 0); + this.setupKeyframes(childDocs, 0); } - CollectionFreeFormView.updateKeyframe(undefined, [...childDocs, doc], currentFrame || 0); + this.updateKeyframe(undefined, [...childDocs, doc], currentFrame || 0); doc._currentFrame = newFrame === undefined ? 0 : Math.max(0, newFrame); } } + public static updateKeyframe(timer: NodeJS.Timeout | undefined, docs: Doc[], time: number) { + const newTimer = DocumentView.SetViewTransition(docs, 'all', 1000, timer, undefined, true); + const timecode = Math.round(time); + docs.forEach(doc => { + this.animFields.forEach(val => { + const findexed = Cast(doc[`${val.key}_indexed`], listSpec('number'), null); + findexed?.length <= timecode + 1 && findexed.push(undefined as any as number); + }); + this.animStringFields.forEach(val => { + const findexed = Cast(doc[`${val}_indexed`], listSpec('string'), null); + findexed?.length <= timecode + 1 && findexed.push(undefined as any as string); + }); + this.animDataFields(doc).forEach(val => { + const findexed = Cast(doc[`${val}_indexed`], listSpec(InkField), null); + findexed?.length <= timecode + 1 && findexed.push(undefined as any); + }); + }); + return newTimer; + } public static setupKeyframes(docs: Doc[], currTimecode: number, makeAppear: boolean = false) { docs.forEach(doc => { if (doc.appearFrame === undefined) doc.appearFrame = currTimecode; @@ -220,7 +237,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF const containerDocView = this._props.containerViewPath?.().lastElement(); const screenXf = containerDocView?.screenToContentsTransform(); if (screenXf) { - SelectionManager.DeselectAll(); + DocumentView.DeselectAll(); if (topDoc.z) { const spt = screenXf.inverse().transformPoint(NumCast(topDoc.x), NumCast(topDoc.y)); topDoc.z = 0; @@ -233,7 +250,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF topDoc.z = 1; [topDoc.x, topDoc.y] = fpt; } - setTimeout(() => SelectionManager.SelectView(DocumentManager.Instance.getDocumentView(topDoc, containerDocView), false), 0); + setTimeout(() => DocumentView.SelectView(DocumentView.getDocumentView(topDoc, containerDocView), false), 0); } }; @@ -255,7 +272,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF // 'inactive' - this is a group child but it is not active // undefined - this is not activated by a group isGroupActive = () => { - if (this.CollectionFreeFormView.isAnyChildContentActive()) return undefined; + if (this._props.isAnyChildContentActive()) return undefined; const backColor = this.BackgroundColor; const isGroup = this.dataDoc.isGroup && (!backColor || backColor === 'transparent'); return isGroup ? (this._props.isDocumentActive?.() ? 'group' : this._props.isGroupActive?.() ? 'child' : 'inactive') : this._props.isGroupActive?.() ? 'child' : undefined; diff --git a/src/client/views/nodes/ComparisonBox.tsx b/src/client/views/nodes/ComparisonBox.tsx index c42ca4468..6ae6bb228 100644 --- a/src/client/views/nodes/ComparisonBox.tsx +++ b/src/client/views/nodes/ComparisonBox.tsx @@ -15,7 +15,7 @@ import { dropActionType } from '../../util/DropActionTypes'; import { undoBatch } from '../../util/UndoManager'; import { ViewBoxAnnotatableComponent, ViewBoxInterface } from '../DocComponent'; import { PinDocView, PinProps } from '../PinFuncs'; -import { StyleProp } from '../StyleProvider'; +import { StyleProp } from '../StyleProp'; import './ComparisonBox.scss'; import { DocumentView } from './DocumentView'; import { FieldView, FieldViewProps } from './FieldView'; diff --git a/src/client/views/nodes/DataVizBox/DataVizBox.tsx b/src/client/views/nodes/DataVizBox/DataVizBox.tsx index ecfdcc229..15187b4e4 100644 --- a/src/client/views/nodes/DataVizBox/DataVizBox.tsx +++ b/src/client/views/nodes/DataVizBox/DataVizBox.tsx @@ -16,7 +16,6 @@ import { TraceMobx } from '../../../../fields/util'; import { DocUtils } from '../../../documents/DocUtils'; import { DocumentType } from '../../../documents/DocumentTypes'; import { Docs } from '../../../documents/Documents'; -import { DocumentManager } from '../../../util/DocumentManager'; import { UndoManager, undoable } from '../../../util/UndoManager'; import { ContextMenu } from '../../ContextMenu'; import { ViewBoxAnnotatableComponent, ViewBoxInterface } from '../../DocComponent'; @@ -254,7 +253,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() im this.toggleSidebar(); } return new Promise<Opt<DocumentView>>(res => { - DocumentManager.Instance.AddViewRenderedCb(doc, dv => res(dv)); + DocumentView.addViewRenderedCb(doc, dv => res(dv)); }); }; @computed get sidebarWidthPercent() { diff --git a/src/client/views/nodes/DataVizBox/components/LineChart.tsx b/src/client/views/nodes/DataVizBox/components/LineChart.tsx index 1c3134185..bc35ab8c8 100644 --- a/src/client/views/nodes/DataVizBox/components/LineChart.tsx +++ b/src/client/views/nodes/DataVizBox/components/LineChart.tsx @@ -8,7 +8,6 @@ import { List } from '../../../../../fields/List'; import { listSpec } from '../../../../../fields/Schema'; import { Cast, DocCast, StrCast } from '../../../../../fields/Types'; import { Docs } from '../../../../documents/Documents'; -import { DocumentManager } from '../../../../util/DocumentManager'; import { undoable } from '../../../../util/UndoManager'; import {} from '../../../DocComponent'; import { ObservableReactComponent } from '../../../ObservableReactComponent'; @@ -16,6 +15,7 @@ import { PinProps, PinDocView } from '../../../PinFuncs'; import { DataVizBox } from '../DataVizBox'; import { createLineGenerator, drawLine, minMaxRange, scaleCreatorNumerical, xAxisCreator, xGrid, yAxisCreator, yGrid } from '../utils/D3Utils'; import './Chart.scss'; +import { DocumentView } from '../../DocumentView'; export interface DataPoint { x: number; @@ -71,7 +71,7 @@ export class LineChart extends ObservableReactComponent<LineChartProps> { } @computed get parentViz() { return DocCast(this._props.Document.dataViz_parentViz); - // return LinkManager.Instance.getAllRelatedLinks(this._props.Document) // out of all links + // return LinkManager.Links(this._props.Document) // out of all links // .filter(link => { // return link.link_anchor_1 == this._props.Document.dataViz_parentViz; // }) // get links where this chart doc is the target of the link @@ -80,7 +80,7 @@ export class LineChart extends ObservableReactComponent<LineChartProps> { @computed get incomingHighlited() { // return selected x and y axes // otherwise, use the selection of whatever is linked to us - const incomingVizBox = DocumentManager.Instance.getFirstDocumentView(this.parentViz)?.ComponentView as DataVizBox; + const incomingVizBox = DocumentView.getFirstDocumentView(this.parentViz)?.ComponentView as DataVizBox; const highlitedRowIds = NumListCast(incomingVizBox?.layoutDoc?.dataViz_highlitedRows); return this._tableData.filter((record, i) => highlitedRowIds.includes(this._tableDataIds[i])); // get all the datapoints they have selected field set by incoming anchor } diff --git a/src/client/views/nodes/DataVizBox/components/PieChart.tsx b/src/client/views/nodes/DataVizBox/components/PieChart.tsx index ffb2f528a..ef6d1d412 100644 --- a/src/client/views/nodes/DataVizBox/components/PieChart.tsx +++ b/src/client/views/nodes/DataVizBox/components/PieChart.tsx @@ -84,7 +84,7 @@ export class PieChart extends ObservableReactComponent<PieChartProps> { @computed get parentViz() { return DocCast(this._props.Document.dataViz_parentViz); - // return LinkManager.Instance.getAllRelatedLinks(this._props.Document) // out of all links + // return LinkManager.Links(this._props.Document) // out of all links // .filter(link => link.link_anchor_1 == this._props.Document.dataViz_parentViz) // get links where this chart doc is the target of the link // .map(link => DocCast(link.link_anchor_1)); // then return the source of the link } diff --git a/src/client/views/nodes/DataVizBox/components/TableBox.tsx b/src/client/views/nodes/DataVizBox/components/TableBox.tsx index 8038b2cd4..5cd77e274 100644 --- a/src/client/views/nodes/DataVizBox/components/TableBox.tsx +++ b/src/client/views/nodes/DataVizBox/components/TableBox.tsx @@ -7,6 +7,7 @@ import * as React from 'react'; import { ClientUtils, setupMoveUpEvents } from '../../../../../ClientUtils'; import { emptyFunction } from '../../../../../Utils'; import { Doc, Field, NumListCast } from '../../../../../fields/Doc'; +import { DocData } from '../../../../../fields/DocSymbols'; import { List } from '../../../../../fields/List'; import { listSpec } from '../../../../../fields/Schema'; import { Cast, DocCast } from '../../../../../fields/Types'; @@ -152,11 +153,9 @@ export class TableBox extends ObservableReactComponent<TableBoxProps> { DragManager.StartAnchorAnnoDrag(moveEv.target instanceof HTMLElement ? [moveEv.target] : [], new DragManager.AnchorAnnoDragData(this._props.docView()!, sourceAnchorCreator, targetCreator), downX, downY, { dragComplete: completeEv => { if (!completeEv.aborted && completeEv.annoDragData && completeEv.annoDragData.linkSourceDoc && completeEv.annoDragData.dropDocument && completeEv.linkDocument) { - completeEv.linkDocument.link_displayLine = true; - completeEv.linkDocument.link_matchEmbeddings = true; - completeEv.linkDocument.link_displayArrow = true; - // e.annoDragData.linkSourceDoc.followLinkToggle = e.annoDragData.dropDocument.annotationOn === this._props.Document; - // e.annoDragData.linkSourceDoc.followLinkZoom = false; + completeEv.linkDocument[DocData].link_matchEmbeddings = true; + completeEv.linkDocument[DocData].stroke_startMarker = true; + this._props.docView?.()?._props.addDocument?.(completeEv.linkDocument); } }, }); diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx index e902d1792..ec9db8480 100644 --- a/src/client/views/nodes/DocumentContentsView.tsx +++ b/src/client/views/nodes/DocumentContentsView.tsx @@ -31,7 +31,6 @@ import { FunctionPlotBox } from './FunctionPlotBox'; import { ImageBox } from './ImageBox'; import { KeyValueBox } from './KeyValueBox'; import { LabelBox } from './LabelBox'; -import { LinkAnchorBox } from './LinkAnchorBox'; import { LinkBox } from './LinkBox'; import { LoadingBox } from './LoadingBox'; import { MapBox } from './MapBox/MapBox'; @@ -87,18 +86,18 @@ interface HTMLtagProps { export class HTMLtag extends React.Component<HTMLtagProps> { click = () => { const clickScript = (this.props as any).onClick as Opt<ScriptField>; - clickScript?.script.run({ this: this.props.Document, self: this.props.Document, scale: this.props.scaling }); + clickScript?.script.run({ this: this.props.Document, scale: this.props.scaling }); }; onInput = (e: React.FormEvent<HTMLDivElement>) => { const onInputScript = (this.props as any).onInput as Opt<ScriptField>; - onInputScript?.script.run({ this: this.props.Document, self: this.props.Document, value: (e.target as any).textContent }); + onInputScript?.script.run({ this: this.props.Document, value: (e.target as any).textContent }); }; render() { const style: { [key: string]: any } = {}; const divKeys = OmitKeys(this.props, ['children', 'dragStarting', 'dragEnding', 'htmltag', 'scaling', 'Document', 'key', 'onInput', 'onClick', '__proto__']).omit; const replacer = (match: any, expr: string) => // bcz: this executes a script to convert a property expression string: { script } into a value - (ScriptField.MakeFunction(expr, { self: Doc.name, this: Doc.name, scale: 'number' })?.script.run({ self: this.props.Document, this: this.props.Document, scale: this.props.scaling }).result as string) || ''; + (ScriptField.MakeFunction(expr, { this: Doc.name, scale: 'number' })?.script.run({ this: this.props.Document, scale: this.props.scaling }).result as string) || ''; Object.keys(divKeys).forEach((prop: string) => { const p = (this.props as any)[prop] as string; style[prop] = p?.replace(/{([^.'][^}']+)}/g, replacer); @@ -121,7 +120,6 @@ export class DocumentContentsView extends ObservableReactComponent<DocumentConte super(props); makeObservable(this); } - @computed get layout(): string { TraceMobx(); if (this._props.LayoutTemplateString) return this._props.LayoutTemplateString; @@ -186,7 +184,7 @@ export class DocumentContentsView extends ObservableReactComponent<DocumentConte let layoutFrame = this.layout; // replace code content with a script >{content}< as in <HTMLdiv>{this.title}</HTMLdiv> - const replacer = (match: any, prefix: string, expr: string, postfix: string) => prefix + ((ScriptField.MakeFunction(expr, { self: Doc.name, this: Doc.name })?.script.run({ this: this._props.Document }).result as string) || '') + postfix; + const replacer = (match: any, prefix: string, expr: string, postfix: string) => prefix + ((ScriptField.MakeFunction(expr, { this: Doc.name })?.script.run({ this: this._props.Document }).result as string) || '') + postfix; layoutFrame = layoutFrame.replace(/(>[^{]*)[^=]\{([^.'][^<}]+)\}([^}]*<)/g, replacer); // replace HTML<tag> with corresponding HTML tag as in: <HTMLdiv> becomes <HTMLtag Document={props.Document} htmltag='div'> @@ -205,7 +203,7 @@ export class DocumentContentsView extends ObservableReactComponent<DocumentConte const code = XRegExp.matchRecursive(splits[1], '{', '}', '', { valueNames: ['between', 'left', 'match', 'right', 'between'] }); layoutFrame = splits[0] + ` ${func}={props.${func}} ` + splits[1].substring(code[1].end + 1); const script = code[1].value.replace(/^‘/, '').replace(/’$/, ''); // ‘’ are not valid quotes in javascript so get rid of them -- they may be present to make it easier to write complex scripts - see headerTemplate in currentUserUtils.ts - return ScriptField.MakeScript(script, { this: Doc.name, self: Doc.name, scale: 'number', value: 'string' }); + return ScriptField.MakeScript(script, { this: Doc.name, scale: 'number', value: 'string' }); } return undefined; // add input function to props @@ -247,7 +245,6 @@ export class DocumentContentsView extends ObservableReactComponent<DocumentConte PresElementBox, SearchBox, FunctionPlotBox, - LinkAnchorBox, InkingStroke, LinkBox, ScriptingBox, diff --git a/src/client/views/nodes/DocumentIcon.tsx b/src/client/views/nodes/DocumentIcon.tsx index 364406197..23d934edb 100644 --- a/src/client/views/nodes/DocumentIcon.tsx +++ b/src/client/views/nodes/DocumentIcon.tsx @@ -6,10 +6,8 @@ import { factory } from 'typescript'; import { FieldType } from '../../../fields/Doc'; import { Id } from '../../../fields/FieldSymbols'; import { StrCast } from '../../../fields/Types'; -import { DocumentManager } from '../../util/DocumentManager'; import { Transformer, ts } from '../../util/Scripting'; import { SnappingManager } from '../../util/SnappingManager'; -import { LightboxView } from '../LightboxView'; import { ObservableReactComponent } from '../ObservableReactComponent'; import { DocumentView } from './DocumentView'; @@ -25,9 +23,6 @@ export class DocumentIcon extends ObservableReactComponent<DocumentIconProps> { makeObservable(this); } - static get DocViews() { - return LightboxView.LightboxDoc ? DocumentManager.Instance.DocumentViews.filter(v => LightboxView.Contains(v)) : DocumentManager.Instance.DocumentViews; - } render() { const { view } = this._props; const { left, top, right } = view.getBounds || { left: 0, top: 0, right: 0, bottom: 0 }; @@ -69,7 +64,7 @@ export class DocumentIconContainer extends React.Component { const match = node.text.match(/d([0-9]+)/); if (match) { const m = parseInt(match[1]); - const doc = DocumentIcon.DocViews[m].Document; + const doc = DocumentView.allViews()[m].Document; usedDocuments.add(m); return factory.createIdentifier(`idToDoc("${doc[Id]}")`); } @@ -81,7 +76,7 @@ export class DocumentIconContainer extends React.Component { return ts.visitNode(root, visit); }, getVars() { - const docs = DocumentIcon.DocViews; + const docs = DocumentView.allViews(); const capturedVariables: { [name: string]: FieldType } = {}; usedDocuments.forEach(index => { capturedVariables[`d${index}`] = docs.length > index ? docs[index].Document : `d${index}`; @@ -91,6 +86,6 @@ export class DocumentIconContainer extends React.Component { }; } render() { - return DocumentIcon.DocViews.map((dv, i) => <DocumentIcon key={dv.DocUniqueId} index={i} view={dv} />); + return DocumentView.allViews().map((dv, i) => <DocumentIcon key={dv.DocUniqueId} index={i} view={dv} />); } } diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx index 977899589..0c5156339 100644 --- a/src/client/views/nodes/DocumentLinksButton.tsx +++ b/src/client/views/nodes/DocumentLinksButton.tsx @@ -8,11 +8,8 @@ import * as React from 'react'; import { StopEvent, returnFalse, setupMoveUpEvents } from '../../../ClientUtils'; import { emptyFunction } from '../../../Utils'; import { Doc } from '../../../fields/Doc'; -import { DocData } from '../../../fields/DocSymbols'; -import { StrCast } from '../../../fields/Types'; import { DocUtils } from '../../documents/DocUtils'; import { DragManager } from '../../util/DragManager'; -import { Hypothesis } from '../../util/HypothesisUtils'; import { LinkManager } from '../../util/LinkManager'; import { UndoManager, undoBatch } from '../../util/UndoManager'; import { ObservableReactComponent } from '../ObservableReactComponent'; @@ -169,16 +166,6 @@ export class DocumentLinksButton extends ObservableReactComponent<DocumentLinksB LinkManager.Instance.currentLink = linkDoc; if (linkDoc) { - if (DocumentLinksButton.AnnotationId && DocumentLinksButton.AnnotationUri) { - // if linking from a Hypothes.is annotation - const linkDocData = linkDoc[DocData]; - linkDocData.linksToAnnotation = true; - linkDocData.annotationId = DocumentLinksButton.AnnotationId; - linkDocData.annotationUri = DocumentLinksButton.AnnotationUri; - const dashHyperlink = Doc.globalServerPath(startIsAnnotation ? endLink : startLink); - Hypothesis.makeLink(StrCast(startIsAnnotation ? endLink.title : startLink.title), dashHyperlink, DocumentLinksButton.AnnotationId, startIsAnnotation ? startLink : endLink); // edit annotation to add a Dash hyperlink to the linked doc - } - TaskCompletionBox.textDisplayed = 'Link Created'; TaskCompletionBox.popupX = screenX; TaskCompletionBox.popupY = screenY - 133; diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 1e7f1015a..f8ce50c98 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -7,10 +7,10 @@ import { IReactionDisposer, action, computed, makeObservable, observable, reacti import { observer } from 'mobx-react'; import * as React from 'react'; import { Bounce, Fade, Flip, JackInTheBox, Roll, Rotate, Zoom } from 'react-awesome-reveal'; -import { ClientUtils, DivWidth, isTargetChildOf as isParentOf, lightOrDark, returnEmptyString, returnFalse, returnTrue, returnVal, simulateMouseClick } from '../../../ClientUtils'; +import { ClientUtils, DivWidth, isTargetChildOf as isParentOf, lightOrDark, returnFalse, returnVal, simulateMouseClick } from '../../../ClientUtils'; import { Utils, emptyFunction, emptyPath } from '../../../Utils'; import { Doc, DocListCast, Field, FieldType, Opt, StrListCast } from '../../../fields/Doc'; -import { AclPrivate, Animation, AudioPlay, DocData, DocViews } from '../../../fields/DocSymbols'; +import { AclAdmin, AclEdit, AclPrivate, Animation, AudioPlay, DocData, DocViews } from '../../../fields/DocSymbols'; import { Id } from '../../../fields/FieldSymbols'; import { InkTool } from '../../../fields/InkField'; import { List } from '../../../fields/List'; @@ -20,20 +20,17 @@ import { ScriptField } from '../../../fields/ScriptField'; import { BoolCast, Cast, DocCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types'; import { AudioField } from '../../../fields/URLField'; import { GetEffectiveAcl, TraceMobx } from '../../../fields/util'; +import { AudioAnnoState } from '../../../server/SharedMediaTypes'; import { DocServer } from '../../DocServer'; -import { Networking } from '../../Network'; import { DocUtils, FollowLinkScript } from '../../documents/DocUtils'; import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes'; import { Docs } from '../../documents/Documents'; -import { DictationManager } from '../../util/DictationManager'; -import { DocumentManager } from '../../util/DocumentManager'; import { DragManager } from '../../util/DragManager'; import { dropActionType } from '../../util/DropActionTypes'; import { MakeTemplate, makeUserTemplateButton } from '../../util/DropConverter'; -import { LinkManager, UPDATE_SERVER_CACHE } from '../../util/LinkManager'; +import { UPDATE_SERVER_CACHE } from '../../util/LinkManager'; import { ScriptingGlobals } from '../../util/ScriptingGlobals'; import { SearchUtil } from '../../util/SearchUtil'; -import { SelectionManager } from '../../util/SelectionManager'; import { SharingManager } from '../../util/SharingManager'; import { SnappingManager } from '../../util/SnappingManager'; import { UndoManager, undoBatch, undoable } from '../../util/UndoManager'; @@ -42,16 +39,15 @@ import { ContextMenuProps } from '../ContextMenuItem'; import { DocComponent, ViewBoxInterface } from '../DocComponent'; import { EditableView } from '../EditableView'; import { FieldsDropdown } from '../FieldsDropdown'; -import { GestureOverlay } from '../GestureOverlay'; import { LightboxView } from '../LightboxView'; -import { AudioAnnoState, StyleProp } from '../StyleProvider'; +import { PinProps } from '../PinFuncs'; +import { StyleProp } from '../StyleProp'; import { DocumentContentsView, ObserverJsxParser } from './DocumentContentsView'; import { DocumentLinksButton } from './DocumentLinksButton'; import './DocumentView.scss'; import { FieldViewProps, FieldViewSharedProps } from './FieldView'; import { FocusViewOptions } from './FocusViewOptions'; import { KeyValueBox } from './KeyValueBox'; -import { LinkAnchorBox } from './LinkAnchorBox'; import { OpenWhere } from './OpenWhere'; import { FormattedTextBox } from './formattedText/FormattedTextBox'; import { PresEffect, PresEffectDirection } from './trails'; @@ -90,6 +86,7 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document // this makes mobx trace() statements more descriptive public get displayName() { return 'DocumentViewInternal(' + this.Document.title + ')'; } // prettier-ignore public static SelectAfterContextMenu = true; // whether a document should be selected after it's contextmenu is triggered. + /** * This function is filled in by MainView to allow non-viewBox views to add Docs as tabs without * needing to know about/reference MainView @@ -183,11 +180,10 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document // anchors that are not rendered as DocumentViews (marked as 'layout_unrendered' with their 'annotationOn' set to this document). e.g., // - PDF text regions are rendered as an Annotations without generating a DocumentView, ' // - RTF selections are rendered via Prosemirror and have a mark which contains the Document ID for the annotation link - // - and links to PDF/Web docs at a certain scroll location never create an explicit view. - // For each of these, we create LinkAnchorBox's on the border of the DocumentView. + // - and links to PDF/Web docs at a certain scroll location never create an explicit anchor view. @computed get directLinks() { TraceMobx(); - return LinkManager.Instance.getAllRelatedLinks(this.Document).filter( + return Doc.Links(this.Document).filter( link => (link.link_matchEmbeddings ? link.link_anchor_1 === this.Document : Doc.AreProtosEqual(link.link_anchor_1 as Doc, this.Document)) || (link.link_matchEmbeddings ? link.link_anchor_2 === this.Document : Doc.AreProtosEqual(link.link_anchor_2 as Doc, this.Document)) || @@ -197,11 +193,11 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document } @computed get _allLinks(): Doc[] { TraceMobx(); - return LinkManager.Instance.getAllRelatedLinks(this.Document).filter(link => !link.link_matchEmbeddings || link.link_anchor_1 === this.Document || link.link_anchor_2 === this.Document); + return Doc.Links(this.Document).filter(link => !link.link_matchEmbeddings || link.link_anchor_1 === this.Document || link.link_anchor_2 === this.Document); } @computed get filteredLinks() { - return DocUtils.FilterDocs(this.directLinks, this._props.childFilters?.() ?? [], []).filter(d => d.link_displayLine || Doc.UserDoc().showLinkLines); + return DocUtils.FilterDocs(this.directLinks, this._props.childFilters?.() ?? [], []); } componentWillUnmount() { @@ -259,7 +255,7 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document startDragging(x: number, y: number, dropAction: dropActionType, hideSource = false) { const docView = this._docView; if (this._mainCont.current && docView) { - const views = SelectionManager.Views.filter(dv => dv.ContentDiv); + const views = DocumentView.Selected().filter(dv => dv.ContentDiv); const selected = views.length > 1 && views.some(dv => dv.Document === this.Document) ? views : [docView]; const dragData = new DragManager.DocumentDragData(selected.map(dv => dv.Document)); const screenXf = docView.screenToViewTransform(); @@ -289,8 +285,8 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document }; onBrowseClick = (e: React.MouseEvent) => { const browseTransitionTime = 500; - SelectionManager.DeselectAll(); - DocumentManager.Instance.showDocument(this.Document, { zoomScale: 0.8, willZoomCentered: true }, (focused: boolean) => { + DocumentView.DeselectAll(); + DocumentView.showDocument(this.Document, { zoomScale: 0.8, willZoomCentered: true }, (focused: boolean) => { const options: FocusViewOptions = { pointFocus: { X: e.clientX, Y: e.clientY }, zoomTime: browseTransitionTime }; if (!focused && this._docView) { this._docView @@ -327,7 +323,7 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document UndoManager.RunInBatch(() => this.onDoubleClickHandler.script.run(scriptProps, console.log).result?.select && this._props.select(false), 'on double click: ' + this.Document.title); } else if (!Doc.IsSystem(this.Document) && defaultDblclick !== 'ignore') { UndoManager.RunInBatch(() => LightboxView.Instance.AddDocTab(this.Document, OpenWhere.lightbox), 'double tap'); - SelectionManager.DeselectAll(); + DocumentView.DeselectAll(); Doc.UnBrushDoc(this.Document); } else { this._singleClickFunc?.(); @@ -377,7 +373,7 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document if (this._props.isGroupActive?.() === 'child' && !this._props.isDocumentActive?.()) return; // eslint-disable-next-line no-use-before-define this._longPressSelector = setTimeout(() => DocumentView.LongPress && this._props.select(false), 1000); - if (!GestureOverlay.DownDocView) GestureOverlay.DownDocView = this._docView; + if (!DocumentView.DownDocView) DocumentView.DownDocView = this._docView; this._downX = e.clientX; this._downY = e.clientY; @@ -463,7 +459,7 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document }, 'set toggle detail'); drop = undoable((e: Event, de: DragManager.DropEvent) => { - if (this._props.dontRegisterView || this._props.LayoutTemplateString?.includes(LinkAnchorBox.name)) return false; + if (this._props.dontRegisterView) return false; if (this.Document === Doc.ActiveDashboard) { e.stopPropagation(); e.preventDefault(); @@ -484,7 +480,7 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document if (linkDoc) { de.complete.linkDocument = linkDoc; linkDoc.layout_isSvg = true; - DocumentManager.LinkCommonAncestor(linkDoc)?.ComponentView?.addDocument?.(linkDoc); + DocumentView.linkCommonAncestor(linkDoc)?.ComponentView?.addDocument?.(linkDoc); } } e.stopPropagation(); @@ -516,7 +512,7 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document if (e && this.layoutDoc.layout_hideContextMenu && Doc.noviceMode) { e.preventDefault(); e.stopPropagation(); - // !this._props.isSelected(true) && SelectionManager.SelectView(this.DocumentView(), false); + // !this._props.isSelected(true) && DocumentView.SelectView(this.DocumentView(), false); } // the touch onContextMenu is button 0, the pointer onContextMenu is button 2 if (e) { @@ -576,8 +572,8 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document if (this._props.bringToFront) { const zorders = cm.findByDescription('ZOrder...'); const zorderItems: ContextMenuProps[] = zorders && 'subitems' in zorders ? zorders.subitems : []; - zorderItems.push({ description: 'Bring to Front', event: () => SelectionManager.Views.forEach(dv => dv._props.bringToFront?.(dv.Document, false)), icon: 'arrow-up' }); - zorderItems.push({ description: 'Send to Back', event: () => SelectionManager.Views.forEach(dv => dv._props.bringToFront?.(dv.Document, true)), icon: 'arrow-down' }); + zorderItems.push({ description: 'Bring to Front', event: () => DocumentView.Selected().forEach(dv => dv._props.bringToFront?.(dv.Document, false)), icon: 'arrow-up' }); + zorderItems.push({ description: 'Send to Back', event: () => DocumentView.Selected().forEach(dv => dv._props.bringToFront?.(dv.Document, true)), icon: 'arrow-down' }); zorderItems.push({ description: !this.layoutDoc._keepZDragged ? 'Keep ZIndex when dragged' : 'Allow ZIndex to change when dragged', event: undoBatch( @@ -601,7 +597,7 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document onClicks.push({ description: this.onClickHandler ? 'Remove Click Behavior' : 'Follow Link', event: () => this.toggleFollowLink(false, false), icon: 'link' }); !Doc.noviceMode && onClicks.push({ description: 'Edit onClick Script', event: () => UndoManager.RunInBatch(() => DocUtils.makeCustomViewClicked(this.Document, undefined, 'onClick'), 'edit onClick'), icon: 'terminal' }); !existingOnClick && cm.addItem({ description: 'OnClick...', noexpand: true, subitems: onClicks, icon: 'mouse-pointer' }); - } else if (LinkManager.Links(this.Document).length) { + } else if (Doc.Links(this.Document).length) { onClicks.push({ description: 'Restore On Click default', event: () => this.noOnClick(), icon: 'link' }); onClicks.push({ description: 'Follow Link on Click', event: () => this.followLinkOnClick(), icon: 'link' }); !existingOnClick && cm.addItem({ description: 'OnClick...', subitems: onClicks, icon: 'mouse-pointer' }); @@ -718,7 +714,7 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document case StyleProp.PointerEvents: return 'none'; case StyleProp.Highlighting: return undefined; case StyleProp.Opacity: { - const filtered = DocUtils.FilterDocs(this.directLinks, this._props.childFilters?.() ?? [], []).filter(d => d.link_displayLine || Doc.UserDoc().showLinkLines); + const filtered = DocUtils.FilterDocs(this.directLinks, this._props.childFilters?.() ?? [], []); return filtered.some(link => link._link_displayArrow) ? 0 : undefined; } default: @@ -726,34 +722,6 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document return this._props.styleProvider?.(doc, props, property); }; - // eslint-disable-next-line no-return-assign - removeLinkByHiding = (link: Doc) => () => link.link_displayLine = false; // prettier-ignore - @computed get allLinkEndpoints() { - // the small blue dots that mark the endpoints of links - if (this._componentView instanceof KeyValueBox || this._props.hideLinkAnchors || this.layoutDoc.layout_hideLinkAnchors || this._props.dontRegisterView || this.layoutDoc.layout_unrendered) return null; - return this.filteredLinks.map(link => ( - <div className="documentView-anchorCont" key={link[Id]}> - <DocumentView - {...this._props} - isContentActive={returnFalse} - Document={link} - containerViewPath={this._props.docViewPath} - PanelWidth={this.anchorPanelWidth} - PanelHeight={this.anchorPanelHeight} - dontRegisterView={false} - layout_showTitle={returnEmptyString} - hideCaptions - hideLinkAnchors - layout_fitWidth={returnTrue} - removeDocument={this.removeLinkByHiding(link)} - styleProvider={this.anchorStyleProvider} - LayoutTemplate={undefined} - LayoutTemplateString={LinkAnchorBox.LayoutString(`link_anchor_${LinkManager.anchorIndex(link, this.Document)}`)} - /> - </div> - )); - } - @computed get viewBoxContents() { TraceMobx(); const isInk = this.layoutDoc._layout_isSvg && !this._props.LayoutTemplateString; @@ -780,7 +748,6 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document setTitleFocus={this.setTitleFocus} hideClickBehaviors={BoolCast(this.Document.hideClickBehaviors)} /> - {this.layoutDoc.layout_hideAllLinks ? null : this.allLinkEndpoints} </div> ); } @@ -1008,50 +975,45 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document default: return renderDoc; } } - public static recordAudioAnnotation(dataDoc: Doc, field: string, onRecording?: (stop: () => void) => void, onEnd?: () => void) { - let gumStream: any; - let recorder: any; - navigator.mediaDevices.getUserMedia({ audio: true }).then(stream => { - let audioTextAnnos = Cast(dataDoc[field + '_audioAnnotations_text'], listSpec('string'), null); - if (audioTextAnnos) audioTextAnnos.push(''); - else audioTextAnnos = dataDoc[field + '_audioAnnotations_text'] = new List<string>(['']); - DictationManager.Controls.listen({ - interimHandler: value => { audioTextAnnos[audioTextAnnos.length - 1] = value; }, // prettier-ignore - continuous: { indefinite: false }, - }).then(results => { - if (results && [DictationManager.Controls.Infringed].includes(results)) { - DictationManager.Controls.stop(); - } - onEnd?.(); - }); - - gumStream = stream; - recorder = new MediaRecorder(stream); - recorder.ondataavailable = async (e: any) => { - const [{ result }] = await Networking.UploadFilesToServer({ file: e.data }); - if (!(result instanceof Error)) { - const audioField = new AudioField(result.accessPaths.agnostic.client); - const audioAnnos = Cast(dataDoc[field + '_audioAnnotations'], listSpec(AudioField), null); - if (audioAnnos) audioAnnos.push(audioField); - else dataDoc[field + '_audioAnnotations'] = new List([audioField]); - } - }; - recorder.start(); - const stopFunc = () => { - recorder.stop(); - DictationManager.Controls.stop(/* false */); - dataDoc.audioAnnoState = AudioAnnoState.stopped; - gumStream.getAudioTracks()[0].stop(); - }; - if (onRecording) onRecording(stopFunc); - else setTimeout(stopFunc, 5000); - }); - } } @observer export class DocumentView extends DocComponent<DocumentViewProps>() { public static ROOT_DIV = 'documentView-effectsWrapper'; + // LinkFollower + public static FollowLink: (linkDoc: Opt<Doc>, sourceDoc: Doc, altKey: boolean) => boolean; + // selection funcs + public static DeselectAll: (except?: Doc) => void | undefined; + public static DeselectView: (dv: DocumentView | undefined) => void | undefined; + public static SelectView: (dv: DocumentView | undefined, extendSelection: boolean) => void | undefined; + public static Selected: () => DocumentView[]; + public static SelectedDocs: () => Doc[]; + public static SelectSchemaDoc: (doc: Doc, deselectAllFirst?: boolean) => void; + public static SelectedSchemaDoc: () => Opt<Doc>; + // view mgr funcs + public static activateTabView: (tabDoc: Doc) => boolean; + public static allViews: () => DocumentView[]; + public static addView: (dv: DocumentView) => void | undefined; + public static removeView: (dv: DocumentView) => void | undefined; + public static addViewRenderedCb: (doc: Opt<Doc>, func: (dv: DocumentView) => any) => boolean; + public static getFirstDocumentView: (toFind: Doc) => DocumentView | undefined; + public static getDocumentView: (target: Doc | undefined, preferredCollection?: DocumentView) => Opt<DocumentView>; + public static getContextPath: (doc: Opt<Doc>, includeExistingViews?: boolean) => Doc[]; + public static getLightboxDocumentView: (toFind: Doc) => Opt<DocumentView>; + public static showDocumentView: (targetDocView: DocumentView, options: FocusViewOptions) => Promise<void>; + public static showDocument: ( + targetDoc: Doc, // document to display + optionsIn: FocusViewOptions, // options for how to navigate to target + finished?: (changed: boolean) => void // func called after focusing on target with flag indicating whether anything needed to be done. + ) => Promise<void>; + public static linkCommonAncestor: (link: Doc) => DocumentView | undefined; + // pin func + public static PinDoc: (docIn: Doc | Doc[], pinProps: PinProps) => void; + // gesture + public static DownDocView: DocumentView | undefined; // the first DocView that receives a pointerdown event. used by GestureOverlay to determine the doc a gesture should apply to. + // media playing + @observable public static CurrentlyPlaying: DocumentView[] = []; + public get displayName() { return 'DocumentView(' + (this.Document?.title??"") + ')'; } // prettier-ignore public ContentRef = React.createRef<HTMLDivElement>(); private _htmlOverlayEffect: Opt<Doc>; @@ -1138,14 +1100,14 @@ export class DocumentView extends DocComponent<DocumentViewProps>() { componentDidMount() { runInAction(() => this.Document[DocViews].add(this)); this._disposers.onViewMounted = reaction(() => ScriptCast(this.Document.onViewMounted)?.script?.run({ this: this.Document }).result, emptyFunction); - !BoolCast(this.Document.dontRegisterView, this._props.dontRegisterView) && DocumentManager.Instance.AddView(this); + !BoolCast(this.Document.dontRegisterView, this._props.dontRegisterView) && DocumentView.addView(this); } componentWillUnmount() { this._viewTimer && clearTimeout(this._viewTimer); runInAction(() => this.Document[DocViews].delete(this)); Object.values(this._disposers).forEach(disposer => disposer?.()); - !BoolCast(this.Document.dontRegisterView, this._props.dontRegisterView) && DocumentManager.Instance.RemoveView(this); + !BoolCast(this.Document.dontRegisterView, this._props.dontRegisterView) && DocumentView.removeView(this); } public set IsSelected(val) { runInAction(() => { this._selected = val; }); } // prettier-ignore @@ -1176,10 +1138,6 @@ export class DocumentView extends DocComponent<DocumentViewProps>() { const xf = this.screenToContentsTransform().scale(this.nativeScaling).inverse(); const [[left, top], [right, bottom]] = [xf.transformPoint(0, 0), xf.transformPoint(this.panelWidth, this.panelHeight)]; - if (this._props.LayoutTemplateString?.includes(LinkAnchorBox.name)) { - const docuBox = this.ContentDiv.getElementsByClassName('linkAnchorBox-cont'); - if (docuBox.length) return { ...docuBox[0].getBoundingClientRect(), transition: undefined }; - } // transition is returned so that the bounds will 'update' at the end of an animated transition. This is needed by xAnchor in LinkBox const transition = this.docViewPath().find((parent: DocumentView) => parent.DataTransition?.() || parent.ComponentView?.viewTransition?.()); return { left, top, right, bottom, transition: transition?.DataTransition?.() || transition?.ComponentView?.viewTransition?.() }; @@ -1289,7 +1247,7 @@ export class DocumentView extends DocComponent<DocumentViewProps>() { if (checkResult) { return Doc.UserDoc().defaultTextLayout; } - const view = SelectionManager.Views[0]?._props.renderDepth > 0 ? SelectionManager.Views[0] : undefined; + const view = DocumentView.Selected()[0]?._props.renderDepth > 0 ? DocumentView.Selected()[0] : undefined; undoable(() => { let tempDoc: Opt<Doc>; if (view) { @@ -1359,11 +1317,11 @@ export class DocumentView extends DocComponent<DocumentViewProps>() { screenToLocalScale = () => this._props.ScreenToLocalTransform().Scale; isSelected = () => this.IsSelected; select = (extendSelection: boolean, focusSelection?: boolean) => { - if (this.IsSelected && SelectionManager.Views.length > 1) SelectionManager.DeselectView(this); + if (this.IsSelected && DocumentView.Selected().length > 1) DocumentView.DeselectView(this); else { - SelectionManager.SelectView(this, extendSelection); + DocumentView.SelectView(this, extendSelection); if (focusSelection) { - DocumentManager.Instance.showDocument(this.Document, { + DocumentView.showDocument(this.Document, { willZoomCentered: true, zoomScale: 0.9, zoomTime: 500, @@ -1506,6 +1464,37 @@ export function returnEmptyDocViewList() { return emptyPath; } +// eslint-disable-next-line default-param-last +export function DocFocusOrOpen(docIn: Doc, optionsIn: FocusViewOptions = { willZoomCentered: true, zoomScale: 0, openLocation: OpenWhere.toggleRight }, containingDoc?: Doc) { + let doc = docIn; + const options = optionsIn; + const func = () => { + const cv = DocumentView.getDocumentView(containingDoc); + const dv = DocumentView.getDocumentView(doc, cv); + if (dv && (!containingDoc || dv.containerViewPath?.().lastElement()?.Document === containingDoc)) { + DocumentView.showDocumentView(dv, options).then(() => dv && Doc.linkFollowHighlight(dv.Document)); + } else { + const container = DocCast(containingDoc ?? doc.embedContainer ?? Doc.BestEmbedding(doc)); + const showDoc = !Doc.IsSystem(container) && !cv ? container : doc; + options.toggleTarget = undefined; + DocumentView.showDocument(showDoc, options, () => DocumentView.showDocument(doc, { ...options, openLocation: undefined })).then(() => { + const cvFound = DocumentView.getDocumentView(containingDoc); + const dvFound = DocumentView.getDocumentView(doc, cvFound); + dvFound && Doc.linkFollowHighlight(dvFound.Document); + }); + } + }; + if (Doc.IsDataProto(doc) && Doc.GetEmbeddings(doc).some(embed => embed.hidden && [AclAdmin, AclEdit].includes(GetEffectiveAcl(embed)))) { + doc = Doc.GetEmbeddings(doc).find(embed => embed.hidden && [AclAdmin, AclEdit].includes(GetEffectiveAcl(embed)))!; + } + if (doc.hidden) { + doc.hidden = false; + options.toggleTarget = false; + setTimeout(func); + } else func(); +} +ScriptingGlobals.add(DocFocusOrOpen); + // eslint-disable-next-line prefer-arrow-callback ScriptingGlobals.add(function deiconifyView(documentView: DocumentView) { documentView.iconify(); @@ -1527,9 +1516,9 @@ ScriptingGlobals.add(function updateLinkCollection(linkCollection: Doc, linkSour const collectedLinks = DocListCast(linkCollection[DocData].data); let wid = NumCast(linkSource._width); let embedding: Doc | undefined; - const links = LinkManager.Links(linkSource); + const links = Doc.Links(linkSource); links.forEach(link => { - const other = LinkManager.getOppositeAnchor(link, linkSource); + const other = Doc.getOppositeAnchor(link, linkSource); const otherdoc = DocCast(other?.annotationOn ?? other); if (otherdoc && !collectedLinks?.some(d => Doc.AreProtosEqual(d, otherdoc))) { embedding = Doc.MakeEmbedding(otherdoc); diff --git a/src/client/views/nodes/FontIconBox/FontIconBox.tsx b/src/client/views/nodes/FontIconBox/FontIconBox.tsx index 8d1617e66..5e3bb9fec 100644 --- a/src/client/views/nodes/FontIconBox/FontIconBox.tsx +++ b/src/client/views/nodes/FontIconBox/FontIconBox.tsx @@ -17,7 +17,7 @@ import { ContextMenu } from '../../ContextMenu'; import { ViewBoxBaseComponent } from '../../DocComponent'; import { EditableView } from '../../EditableView'; import { SelectedDocView } from '../../selectedDoc'; -import { StyleProp } from '../../StyleProvider'; +import { StyleProp } from '../../StyleProp'; import { FieldView, FieldViewProps } from '../FieldView'; import { OpenWhere } from '../OpenWhere'; import './FontIconBox.scss'; @@ -116,7 +116,7 @@ export class FontIconBox extends ViewBoxBaseComponent<ButtonProps>() { default: type = 'slider'; break; } // prettier-ignore - const numScript = (value?: number) => ScriptCast(this.Document.script).script.run({ this: this.Document, self: this.Document, value, _readOnly_: value === undefined }); + const numScript = (value?: number) => ScriptCast(this.Document.script).script.run({ this: this.Document, value, _readOnly_: value === undefined }); const color = this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.Color); // Script for checking the outcome of the toggle const checkResult = Number(Number(numScript().result ?? 0).toPrecision(NumCast(this.dataDoc.numPrecision, 3))); @@ -142,7 +142,7 @@ export class FontIconBox extends ViewBoxBaseComponent<ButtonProps>() { setupMoveUpEvents( this, e, - () => ScriptCast(this.Document.onDragScript)?.script.run({ this: this.Document, self: this.Document, value: { doc: value, e } }).result, + () => ScriptCast(this.Document.onDragScript)?.script.run({ this: this.Document, value: { doc: value, e } }).result, emptyFunction, emptyFunction ); // prettier-ignore @@ -162,7 +162,7 @@ export class FontIconBox extends ViewBoxBaseComponent<ButtonProps>() { const isViewDropdown = script?.script.originalScript.startsWith('{ return setView'); if (isViewDropdown) { const selected = Array.from(script?.script.run({ _readOnly_: true }).result) as Doc[]; - // const selected = SelectionManager.Docs; + // const selected = DocumentView.SelectedDocs(); if (selected.lastElement()) { if (StrCast(selected.lastElement().type) === DocumentType.COL) { text = StrCast(selected.lastElement()._type_collection); @@ -190,7 +190,7 @@ export class FontIconBox extends ViewBoxBaseComponent<ButtonProps>() { } noviceList = [CollectionViewType.Freeform, CollectionViewType.Schema, CollectionViewType.Carousel3D, CollectionViewType.Stacking, CollectionViewType.NoteTaking]; } else { - text = script?.script.run({ this: this.Document, self: this.Document, value: '', _readOnly_: true }).result; + text = script?.script.run({ this: this.Document, value: '', _readOnly_: true }).result; // text = StrCast((RichTextMenu.Instance?.TextView?.EditorView ? RichTextMenu.Instance : Doc.UserDoc()).fontFamily); getStyle = (val: string) => ({ fontFamily: val }); } @@ -208,7 +208,7 @@ export class FontIconBox extends ViewBoxBaseComponent<ButtonProps>() { return ( <Dropdown selectedVal={text} - setSelectedVal={undoable(value => script.script.run({ this: this.Document, self: this.Document, value }), `dropdown select ${this.label}`)} + setSelectedVal={undoable(value => script.script.run({ this: this.Document, value }), `dropdown select ${this.label}`)} color={SnappingManager.userColor} background={SnappingManager.userVariantColor} type={Type.TERT} @@ -232,17 +232,17 @@ export class FontIconBox extends ViewBoxBaseComponent<ButtonProps>() { */ @computed get colorButton() { const color = this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.Color); - const curColor = this.colorScript?.script.run({ this: this.Document, self: this.Document, value: undefined, _readOnly_: true }).result ?? 'transparent'; + const curColor = this.colorScript?.script.run({ this: this.Document, value: undefined, _readOnly_: true }).result ?? 'transparent'; const tooltip: string = StrCast(this.Document.toolTip); return ( <ColorPicker setSelectedColor={value => { if (!this.colorBatch) this.colorBatch = UndoManager.StartBatch(`Set ${tooltip} color`); - this.colorScript?.script.run({ this: this.Document, self: this.Document, value: value, _readOnly_: false }); + this.colorScript?.script.run({ this: this.Document, value: value, _readOnly_: false }); }} setFinalColor={value => { - this.colorScript?.script.run({ this: this.Document, self: this.Document, value: value, _readOnly_: false }); + this.colorScript?.script.run({ this: this.Document, value: value, _readOnly_: false }); this.colorBatch?.end(); this.colorBatch = undefined; }} @@ -262,7 +262,7 @@ export class FontIconBox extends ViewBoxBaseComponent<ButtonProps>() { const tooltip: string = StrCast(this.Document.toolTip); // const script = ScriptCast(this.Document.onClick); - // const toggleStatus = script ? script.script.run({ this: this.Document, self: this.Document, value: undefined, _readOnly_: true }).result : false; + // const toggleStatus = script ? script.script.run({ this: this.Document, value: undefined, _readOnly_: true }).result : false; // Colors const color = this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.Color); const items = DocListCast(this.dataDoc.data); @@ -278,10 +278,10 @@ export class FontIconBox extends ViewBoxBaseComponent<ButtonProps>() { tooltip: StrCast(item.toolTip), val: StrCast(item.toolType), }))} - selectedVal={StrCast(items.find(itemDoc => ScriptCast(itemDoc.onClick).script.run({ this: itemDoc, self: itemDoc, value: undefined, _readOnly_: true }).result)?.toolType)} + selectedVal={StrCast(items.find(itemDoc => ScriptCast(itemDoc.onClick).script.run({ this: itemDoc, value: undefined, _readOnly_: true }).result)?.toolType)} setSelectedVal={(val: string | number) => { const itemDoc = items.find(item => item.toolType === val); - itemDoc && ScriptCast(itemDoc.onClick).script.run({ this: itemDoc, self: itemDoc, value: val, _readOnly_: false }); + itemDoc && ScriptCast(itemDoc.onClick).script.run({ this: itemDoc, value: val, _readOnly_: false }); }} /> ); @@ -296,7 +296,7 @@ export class FontIconBox extends ViewBoxBaseComponent<ButtonProps>() { const script = ScriptCast(this.Document.onClick); const double = ScriptCast(this.Document.onDoubleClick); - const toggleStatus = script?.script.run({ this: this.Document, self: this.Document, value: undefined, _readOnly_: true }).result ?? false; + const toggleStatus = script?.script.run({ this: this.Document, value: undefined, _readOnly_: true }).result ?? false; // Colors const color = this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.Color); // const backgroundColor = this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.BackgroundColor); @@ -319,8 +319,8 @@ export class FontIconBox extends ViewBoxBaseComponent<ButtonProps>() { returnTrue, emptyFunction, action((clickEv, doubleTap) => { - (!doubleTap || !double) && script?.script.run({ this: this.Document, self: this.Document, value: !toggleStatus, _readOnly_: false }); - doubleTap && double?.script.run({ this: this.Document, self: this.Document, value: !toggleStatus, _readOnly_: false }); + (!doubleTap || !double) && script?.script.run({ this: this.Document, value: !toggleStatus, _readOnly_: false }); + doubleTap && double?.script.run({ this: this.Document, value: !toggleStatus, _readOnly_: false }); this._hackToRecompute += 1; }) ) @@ -341,15 +341,15 @@ export class FontIconBox extends ViewBoxBaseComponent<ButtonProps>() { @computed get editableText() { const script = ScriptCast(this.Document.script); - const checkResult = script?.script.run({ this: this.Document, self: this.Document, value: '', _readOnly_: true }).result; + const checkResult = script?.script.run({ this: this.Document, value: '', _readOnly_: true }).result; - const setValue = (value: string): boolean => script?.script.run({ this: this.Document, self: this.Document, value, _readOnly_: false }).result; + const setValue = (value: string): boolean => script?.script.run({ this: this.Document, value, _readOnly_: false }).result; return ( <div className="menuButton editableText"> <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon="lock" /> <div style={{ width: 'calc(100% - .875em)', paddingLeft: '4px' }}> - <EditableView GetValue={() => script?.script.run({ this: this.Document, self: this.Document, value: '', _readOnly_: true }).result} SetValue={setValue} oneLine contents={checkResult} /> + <EditableView GetValue={() => script?.script.run({ this: this.Document, value: '', _readOnly_: true }).result} SetValue={setValue} oneLine contents={checkResult} /> </div> </div> ); @@ -358,7 +358,7 @@ export class FontIconBox extends ViewBoxBaseComponent<ButtonProps>() { renderButton = () => { const color = this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.Color); const tooltip = StrCast(this.Document.toolTip); - const scriptFunc = () => ScriptCast(this.Document.onClick)?.script.run({ this: this.Document, self: this.Document, _readOnly_: false }); + const scriptFunc = () => ScriptCast(this.Document.onClick)?.script.run({ this: this.Document, _readOnly_: false }); const btnProps = { tooltip, icon: this.Icon(color)!, label: this.label }; // prettier-ignore switch (this.type) { diff --git a/src/client/views/nodes/FunctionPlotBox.tsx b/src/client/views/nodes/FunctionPlotBox.tsx index f32d39aaf..3d1bd7563 100644 --- a/src/client/views/nodes/FunctionPlotBox.tsx +++ b/src/client/views/nodes/FunctionPlotBox.tsx @@ -2,7 +2,7 @@ import functionPlot from 'function-plot'; import { computed, makeObservable, reaction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; -import { DocListCast } from '../../../fields/Doc'; +import { Doc, DocListCast } from '../../../fields/Doc'; import { List } from '../../../fields/List'; import { listSpec } from '../../../fields/Schema'; import { Cast, StrCast } from '../../../fields/Types'; @@ -11,7 +11,6 @@ import { DocUtils } from '../../documents/DocUtils'; import { DocumentType } from '../../documents/DocumentTypes'; import { Docs } from '../../documents/Documents'; import { DragManager } from '../../util/DragManager'; -import { LinkManager } from '../../util/LinkManager'; import { undoBatch } from '../../util/UndoManager'; import { ViewBoxAnnotatableComponent } from '../DocComponent'; import { PinDocView, PinProps } from '../PinFuncs'; @@ -49,8 +48,8 @@ export class FunctionPlotBox extends ViewBoxAnnotatableComponent<FieldViewProps> return anchor; }; @computed get graphFuncs() { - const links = LinkManager.Instance.getAllRelatedLinks(this.Document) - .map(d => LinkManager.getOppositeAnchor(d, this.Document)) + const links = Doc.Links(this.Document) + .map(d => Doc.getOppositeAnchor(d, this.Document)) .filter(d => d) .map(d => d!); const funcs = links.concat(DocListCast(this.dataDoc[this.fieldKey])).map(doc => diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index d317f46bb..3945da104 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -19,7 +19,6 @@ import { Docs } from '../../documents/Documents'; import { DocumentType } from '../../documents/DocumentTypes'; import { DocUtils } from '../../documents/DocUtils'; import { Networking } from '../../Network'; -import { DocumentManager } from '../../util/DocumentManager'; import { DragManager } from '../../util/DragManager'; import { undoBatch } from '../../util/UndoManager'; import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView'; @@ -30,7 +29,8 @@ import { MarqueeAnnotator } from '../MarqueeAnnotator'; import { OverlayView } from '../OverlayView'; import { AnchorMenu } from '../pdf/AnchorMenu'; import { PinDocView, PinProps } from '../PinFuncs'; -import { StyleProp } from '../StyleProvider'; +import { StyleProp } from '../StyleProp'; +import { DocumentView } from './DocumentView'; import { FieldView, FieldViewProps } from './FieldView'; import { FocusViewOptions } from './FocusViewOptions'; import './ImageBox.scss'; @@ -254,7 +254,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() impl cropping.y = NumCast(this.Document.y); this._props.addDocTab(cropping, OpenWhere.inParent); } - DocumentManager.Instance.AddViewRenderedCb(cropping, dv => setTimeout(() => (dv.ComponentView as ImageBox).setNativeSize(), 200)); + DocumentView.addViewRenderedCb(cropping, dv => setTimeout(() => (dv.ComponentView as ImageBox).setNativeSize(), 200)); this._props.bringToFront?.(cropping); return cropping; }; diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx index 46bb16e50..cd591fa42 100644 --- a/src/client/views/nodes/KeyValueBox.tsx +++ b/src/client/views/nodes/KeyValueBox.tsx @@ -59,6 +59,8 @@ export class KeyValueBox extends ObservableReactComponent<FieldViewProps> { get fieldDocToLayout() { return DocCast(this._props.Document); } + isUnstyledView = returnTrue; // used by style provider via ViewBoxInterface + dontRegisterView = returnTrue; // used by ViewBoxInterface @action onEnterKey = (e: React.KeyboardEvent): void => { @@ -111,7 +113,7 @@ export class KeyValueBox extends ObservableReactComponent<FieldViewProps> { if (setResult) setResult?.(value); else target[key] = field; }; - const res = script.run({ this: Doc.Layout(doc), self: doc, _setCacheResult_ }, console.log); + const res = script.run({ this: Doc.Layout(doc), _setCacheResult_ }, console.log); if (!res.success) { if (key) target[key] = script.originalScript; return false; diff --git a/src/client/views/nodes/LabelBox.tsx b/src/client/views/nodes/LabelBox.tsx index 4dec3506c..f80ff5f94 100644 --- a/src/client/views/nodes/LabelBox.tsx +++ b/src/client/views/nodes/LabelBox.tsx @@ -13,7 +13,7 @@ import { ContextMenu } from '../ContextMenu'; import { ContextMenuProps } from '../ContextMenuItem'; import { ViewBoxBaseComponent } from '../DocComponent'; import { PinDocView, PinProps } from '../PinFuncs'; -import { StyleProp } from '../StyleProvider'; +import { StyleProp } from '../StyleProp'; import { FieldView, FieldViewProps } from './FieldView'; import BigText from './LabelBigText'; import './LabelBox.scss'; diff --git a/src/client/views/nodes/LinkAnchorBox.scss b/src/client/views/nodes/LinkAnchorBox.scss deleted file mode 100644 index caff369df..000000000 --- a/src/client/views/nodes/LinkAnchorBox.scss +++ /dev/null @@ -1,34 +0,0 @@ -.linkAnchorBox-cont, -.linkAnchorBox-cont-small { - cursor: default; - position: absolute; - width: 15; - height: 15; - border-radius: 20px; - user-select: none; - pointer-events: all; - - .linkAnchorBox-linkCloser { - position: absolute; - width: 18; - height: 18; - background: rgb(219, 21, 21); - top: -1px; - left: -1px; - border-radius: 5px; - display: flex; - justify-content: center; - align-items: center; - padding-left: 2px; - padding-top: 1px; - } - .linkAnchorBox-button { - position: relative; - display: inline-block; - } -} - -.linkAnchorBox-cont-small { - width: 5px; - height: 5px; -}
\ No newline at end of file diff --git a/src/client/views/nodes/LinkAnchorBox.tsx b/src/client/views/nodes/LinkAnchorBox.tsx deleted file mode 100644 index d43241de0..000000000 --- a/src/client/views/nodes/LinkAnchorBox.tsx +++ /dev/null @@ -1,118 +0,0 @@ -import { action, computed, makeObservable } from 'mobx'; -import { observer } from 'mobx-react'; -import * as React from 'react'; -import { setupMoveUpEvents } from '../../../ClientUtils'; -import { Utils, emptyFunction } from '../../../Utils'; -import { Doc } from '../../../fields/Doc'; -import { NumCast, StrCast } from '../../../fields/Types'; -import { TraceMobx } from '../../../fields/util'; -import { DragManager } from '../../util/DragManager'; -import { dropActionType } from '../../util/DropActionTypes'; -import { LinkFollower } from '../../util/LinkFollower'; -import { SelectionManager } from '../../util/SelectionManager'; -import { ViewBoxBaseComponent } from '../DocComponent'; -import { StyleProp } from '../StyleProvider'; -import { FieldView, FieldViewProps } from './FieldView'; -import './LinkAnchorBox.scss'; -import { LinkInfo } from './LinkDocPreview'; - -const { MEDIUM_GRAY } = require('../global/globalCssVariables.module.scss'); // prettier-ignore - -@observer -export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps>() { - public static LayoutString(fieldKey: string) { - return FieldView.LayoutString(LinkAnchorBox, fieldKey); - } - _doubleTap = false; - _lastTap: number = 0; - _ref = React.createRef<HTMLDivElement>(); - _isOpen = false; - _timeout: NodeJS.Timeout | undefined; - - constructor(props: FieldViewProps) { - super(props); - makeObservable(this); - } - - componentDidMount() { - this._props.setContentViewBox?.(this); - } - - @computed get linkSource() { - return this.DocumentView?.().containerViewPath?.().lastElement().Document; // this._props.styleProvider?.(this.dataDoc, this._props, StyleProp.LinkSource); - } - - onPointerDown = (e: React.PointerEvent) => { - const { linkSource } = this; - linkSource && - setupMoveUpEvents(this, e, this.onPointerMove, emptyFunction, (clickEv, doubleTap) => { - if (doubleTap) LinkFollower.FollowLink(this.Document, linkSource, false); - else this._props.select(false); - }); - }; - onPointerMove = action((e: PointerEvent) => { - const cdiv = this._ref?.current?.parentElement; - if (!this._isOpen && cdiv) { - const bounds = cdiv.getBoundingClientRect(); - const pt = Utils.getNearestPointInPerimeter(bounds.left, bounds.top, bounds.width, bounds.height, e.clientX, e.clientY); - const separation = Math.sqrt((pt[0] - e.clientX) * (pt[0] - e.clientX) + (pt[1] - e.clientY) * (pt[1] - e.clientY)); - if (separation > 100) { - const dragData = new DragManager.DocumentDragData([this.Document]); - dragData.dropAction = dropActionType.embed; - dragData.dropPropertiesToRemove = ['link_anchor_1_x', 'link_anchor_1_y', 'link_anchor_2_x', 'link_anchor_2_y', 'onClick']; - DragManager.StartDocumentDrag([this._ref.current!], dragData, pt[0], pt[1]); - return true; - } - this.layoutDoc[this.fieldKey + '_x'] = ((pt[0] - bounds.left) / bounds.width) * 100; - this.layoutDoc[this.fieldKey + '_y'] = ((pt[1] - bounds.top) / bounds.height) * 100; - this.layoutDoc.link_autoMoveAnchors = false; - } - return false; - }); - - specificContextMenu = (): void => {}; - - render() { - TraceMobx(); - const small = this._props.PanelWidth() <= 1; // this happens when rendered in a treeView - const x = NumCast(this.layoutDoc[this.fieldKey + '_x'], 100); - const y = NumCast(this.layoutDoc[this.fieldKey + '_y'], 100); - const background = this._props.styleProvider?.(this.dataDoc, this._props, StyleProp.BackgroundColor + ':anchor'); - const anchor = this.fieldKey === 'link_anchor_1' ? 'link_anchor_2' : 'link_anchor_1'; - const anchorScale = !this.dataDoc[this.fieldKey + '_useSmallAnchor'] && (x === 0 || x === 100 || y === 0 || y === 100) ? 1 : 0.25; - const targetTitle = StrCast((this.dataDoc[anchor] as Doc)?.title); - const selView = SelectionManager.Views.lastElement()?._props.LayoutTemplateString?.includes('link_anchor_1') - ? 'link_anchor_1' - : SelectionManager.Views.lastElement()?._props.LayoutTemplateString?.includes('link_anchor_2') - ? 'link_anchor_2' - : ''; - return ( - <div - ref={this._ref} - title={targetTitle} - className={`linkAnchorBox-cont${small ? '-small' : ''}`} - onPointerEnter={e => - LinkInfo.SetLinkInfo({ - DocumentView: this.DocumentView, - styleProvider: this._props.styleProvider, - linkSrc: this.linkSource, - linkDoc: this.Document, - showHeader: true, - location: [e.clientX, e.clientY + 20], - noPreview: false, - }) - } - onPointerDown={this.onPointerDown} - onContextMenu={this.specificContextMenu} - style={{ - border: selView && this.dataDoc[selView] === this.dataDoc[this.fieldKey] ? `solid ${MEDIUM_GRAY} 2px` : undefined, - background, - left: `calc(${x}% - ${small ? 2.5 : 7.5}px)`, - top: `calc(${y}% - ${small ? 2.5 : 7.5}px)`, - transform: `scale(${anchorScale})`, - cursor: 'grab', - }} - /> - ); - } -} diff --git a/src/client/views/nodes/LinkBox.tsx b/src/client/views/nodes/LinkBox.tsx index 559b1fcae..6caa38a7f 100644 --- a/src/client/views/nodes/LinkBox.tsx +++ b/src/client/views/nodes/LinkBox.tsx @@ -13,12 +13,11 @@ import { TraceMobx } from '../../../fields/util'; import { emptyFunction } from '../../../Utils'; import { Docs } from '../../documents/Documents'; import { DocumentType } from '../../documents/DocumentTypes'; -import { DocumentManager } from '../../util/DocumentManager'; import { SnappingManager } from '../../util/SnappingManager'; import { ViewBoxBaseComponent } from '../DocComponent'; import { EditableView } from '../EditableView'; import { LightboxView } from '../LightboxView'; -import { StyleProp } from '../StyleProvider'; +import { StyleProp } from '../StyleProp'; import { ComparisonBox } from './ComparisonBox'; import { DocumentView } from './DocumentView'; import { FieldView, FieldViewProps } from './FieldView'; @@ -43,7 +42,7 @@ export class LinkBox extends ViewBoxBaseComponent<FieldViewProps>() { anchor = (which: number) => { const anch = DocCast(this.dataDoc['link_anchor_' + which]); const anchor = anch?.layout_unrendered ? DocCast(anch.annotationOn) : anch; - return DocumentManager.Instance.getDocumentView(anchor, this.DocumentView?.().containerViewPath?.().lastElement()); + return DocumentView.getDocumentView(anchor, this.DocumentView?.().containerViewPath?.().lastElement()); }; _hackToSeeIfDeleted: any; componentWillUnmount() { diff --git a/src/client/views/nodes/LinkDescriptionPopup.tsx b/src/client/views/nodes/LinkDescriptionPopup.tsx index 23cb25962..ff95f8547 100644 --- a/src/client/views/nodes/LinkDescriptionPopup.tsx +++ b/src/client/views/nodes/LinkDescriptionPopup.tsx @@ -2,16 +2,17 @@ import { action, makeObservable, observable, reaction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; import { DocData } from '../../../fields/DocSymbols'; +import { StrCast } from '../../../fields/Types'; import { LinkManager } from '../../util/LinkManager'; import './LinkDescriptionPopup.scss'; import { TaskCompletionBox } from './TaskCompletedBox'; -import { StrCast } from '../../../fields/Types'; @observer export class LinkDescriptionPopup extends React.Component<{}> { // eslint-disable-next-line no-use-before-define public static Instance: LinkDescriptionPopup; @observable public display: boolean = false; + // eslint-disable-next-line react/no-unused-class-component-methods @observable public showDescriptions: string = 'ON'; @observable public popupX: number = 700; @observable public popupY: number = 350; diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx index 0936acc15..508008569 100644 --- a/src/client/views/nodes/LinkDocPreview.tsx +++ b/src/client/views/nodes/LinkDocPreview.tsx @@ -11,12 +11,10 @@ import { Cast, DocCast, NumCast, PromiseValue, StrCast } from '../../../fields/T import { DocServer } from '../../DocServer'; import { DocumentType } from '../../documents/DocumentTypes'; import { Docs } from '../../documents/Documents'; -import { DocumentManager } from '../../util/DocumentManager'; import { DragManager } from '../../util/DragManager'; -import { LinkFollower } from '../../util/LinkFollower'; import { LinkManager } from '../../util/LinkManager'; import { SearchUtil } from '../../util/SearchUtil'; -import { SettingsManager } from '../../util/SettingsManager'; +import { SnappingManager } from '../../util/SnappingManager'; import { Transform } from '../../util/Transform'; import { ObservableReactComponent } from '../ObservableReactComponent'; import { DocumentView } from './DocumentView'; @@ -134,17 +132,17 @@ export class LinkDocPreview extends ObservableReactComponent<LinkDocPreviewProps const anchorDoc = anchorDocId ? PromiseValue(DocCast(DocServer.GetCachedRefField(anchorDocId) ?? DocServer.GetRefField(anchorDocId))) : undefined; anchorDoc?.then?.( action(anchor => { - if (anchor instanceof Doc && LinkManager.Links(anchor).length) { - this._linkDoc = this._linkDoc ?? LinkManager.Links(anchor)[0]; + if (anchor instanceof Doc && Doc.Links(anchor).length) { + this._linkDoc = this._linkDoc ?? Doc.Links(anchor)[0]; const automaticLink = this._linkDoc.link_relationship === LinkManager.AutoKeywords; if (automaticLink) { // automatic links specify the target in the link info, not the source const linkTarget = anchor; - this._linkSrc = LinkManager.getOppositeAnchor(this._linkDoc, linkTarget); + this._linkSrc = Doc.getOppositeAnchor(this._linkDoc, linkTarget); this._markerTargetDoc = this._targetDoc = linkTarget; } else { this._linkSrc = anchor; - const linkTarget = LinkManager.getOppositeAnchor(this._linkDoc, this._linkSrc); + const linkTarget = Doc.getOppositeAnchor(this._linkDoc, this._linkSrc); this._markerTargetDoc = linkTarget; this._targetDoc = /* linkTarget?.type === DocumentType.MARKER && */ linkTarget?.annotationOn ? Cast(linkTarget.annotationOn, Doc, null) ?? linkTarget : linkTarget; } @@ -169,8 +167,8 @@ export class LinkDocPreview extends ObservableReactComponent<LinkDocPreviewProps LinkManager.Instance.currentLink = this._linkDoc; LinkManager.Instance.currentLinkAnchor = this._linkSrc; this._props.DocumentView?.().select(false); - if ((SettingsManager.Instance.propertiesWidth ?? 0) < 100) { - SettingsManager.Instance.propertiesWidth = 250; + if ((SnappingManager.PropertiesWidth ?? 0) < 100) { + SnappingManager.SetPropertiesWidth(250); } }) ); @@ -196,13 +194,13 @@ export class LinkDocPreview extends ObservableReactComponent<LinkDocPreviewProps followLink = () => { LinkInfo.Clear(); if (this._linkDoc && this._linkSrc) { - LinkFollower.FollowLink(this._linkDoc, this._linkSrc, false); + DocumentView.FollowLink(this._linkDoc, this._linkSrc, false); } else if (this._props.hrefs?.length) { const webDoc = Array.from(SearchUtil.SearchCollection(Doc.MyFilesystem, this._props.hrefs[0], false).keys()) .filter(doc => doc.type === DocumentType.WEB) .lastElement() ?? Docs.Create.WebDocument(this._props.hrefs[0], { title: this._props.hrefs[0], _nativeWidth: 850, _width: 200, _height: 400, data_useCors: true }); - DocumentManager.Instance.showDocument(webDoc, { + DocumentView.showDocument(webDoc, { openLocation: OpenWhere.lightbox, willPan: true, zoomTime: 500, @@ -229,7 +227,7 @@ export class LinkDocPreview extends ObservableReactComponent<LinkDocPreviewProps }; @computed get previewHeader() { return !this._linkDoc || !this._markerTargetDoc || !this._targetDoc || !this._linkSrc ? null : ( - <div className="linkDocPreview-info" style={{ background: SettingsManager.userBackgroundColor }}> + <div className="linkDocPreview-info" style={{ background: SnappingManager.userBackgroundColor }}> <div className="linkDocPreview-buttonBar" style={{ float: 'left' }}> <Tooltip title={<div className="dash-tooltip">Edit Link</div>} placement="top"> <div className="linkDocPreview-button" onPointerDown={this.editLink}> @@ -282,7 +280,7 @@ export class LinkDocPreview extends ObservableReactComponent<LinkDocPreviewProps ) : ( <DocumentView ref={r => { - const targetanchor = this._linkDoc && this._linkSrc && LinkManager.getOppositeAnchor(this._linkDoc, this._linkSrc); + const targetanchor = this._linkDoc && this._linkSrc && Doc.getOppositeAnchor(this._linkDoc, this._linkSrc); targetanchor && this._targetDoc !== targetanchor && r?._props.focus?.(targetanchor, {}); }} Document={this._targetDoc!} @@ -325,7 +323,7 @@ export class LinkDocPreview extends ObservableReactComponent<LinkDocPreviewProps className="linkDocPreview" ref={this._linkDocRef} onPointerDown={this.followLinkPointerDown} - style={{ borderColor: SettingsManager.userColor, left: this._props.location[0], top: this._props.location[1], width: this.width() + borders, height: this.height() + borders + (this._props.showHeader ? 37 : 0) }}> + style={{ borderColor: SnappingManager.userColor, left: this._props.location[0], top: this._props.location[1], width: this.width() + borders, height: this.height() + borders + (this._props.showHeader ? 37 : 0) }}> {this.docPreview} </div> ); diff --git a/src/client/views/nodes/LoadingBox.tsx b/src/client/views/nodes/LoadingBox.tsx index aa89398f3..5f343bdfe 100644 --- a/src/client/views/nodes/LoadingBox.tsx +++ b/src/client/views/nodes/LoadingBox.tsx @@ -6,12 +6,11 @@ import { Doc } from '../../../fields/Doc'; import { Id } from '../../../fields/FieldSymbols'; import { StrCast } from '../../../fields/Types'; import { Networking } from '../../Network'; -import { DocumentManager } from '../../util/DocumentManager'; +import { DocumentType } from '../../documents/DocumentTypes'; +import { Docs } from '../../documents/Documents'; import { ViewBoxAnnotatableComponent } from '../DocComponent'; import { FieldView, FieldViewProps } from './FieldView'; import './LoadingBox.scss'; -import { DocumentType } from '../../documents/DocumentTypes'; -import { Docs } from '../../documents/Documents'; /** * LoadingBox Class represents a placeholder doc for documents that are currently @@ -39,25 +38,11 @@ export class LoadingBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { public static LayoutString(fieldKey: string) { return FieldView.LayoutString(LoadingBox, fieldKey); } - // removes from currently loading display - public static removeCurrentlyLoading(doc: Doc) { - if (DocumentManager.Instance.CurrentlyLoading) { - const index = DocumentManager.Instance.CurrentlyLoading.indexOf(doc); - runInAction(() => index !== -1 && DocumentManager.Instance.CurrentlyLoading.splice(index, 1)); - } - } - - // adds doc to currently loading display - public static addCurrentlyLoading(doc: Doc) { - if (DocumentManager.Instance.CurrentlyLoading.indexOf(doc) === -1) { - runInAction(() => DocumentManager.Instance.CurrentlyLoading.push(doc)); - } - } _timer: any; @observable progress = ''; componentDidMount() { - if (!DocumentManager.Instance.CurrentlyLoading?.includes(this.Document)) { + if (!Doc.CurrentlyLoading?.includes(this.Document)) { this.Document.loadingError = 'Upload interrupted, please try again'; } else { const updateFunc = async () => { diff --git a/src/client/views/nodes/MapBox/DirectionsAnchorMenu.tsx b/src/client/views/nodes/MapBox/DirectionsAnchorMenu.tsx index f176317d2..b8fd8ac6a 100644 --- a/src/client/views/nodes/MapBox/DirectionsAnchorMenu.tsx +++ b/src/client/views/nodes/MapBox/DirectionsAnchorMenu.tsx @@ -8,9 +8,9 @@ import { returnFalse } from '../../../../ClientUtils'; import { unimplementedFunction } from '../../../../Utils'; import { Doc, Opt } from '../../../../fields/Doc'; import { NumCast, StrCast } from '../../../../fields/Types'; -import { SelectionManager } from '../../../util/SelectionManager'; import { SettingsManager } from '../../../util/SettingsManager'; import { AntimodeMenu, AntimodeMenuProps } from '../../AntimodeMenu'; +import { DocumentView } from '../DocumentView'; @observer export class DirectionsAnchorMenu extends AntimodeMenu<AntimodeMenuProps> { @@ -56,7 +56,7 @@ export class DirectionsAnchorMenu extends AntimodeMenu<AntimodeMenuProps> { componentDidMount() { this._disposer = reaction( - () => SelectionManager.Views.slice(), + () => DocumentView.Selected().slice(), () => DirectionsAnchorMenu.Instance.fadeOut(true) ); } diff --git a/src/client/views/nodes/MapBox/MapAnchorMenu.tsx b/src/client/views/nodes/MapBox/MapAnchorMenu.tsx index 174511e1a..103a35434 100644 --- a/src/client/views/nodes/MapBox/MapAnchorMenu.tsx +++ b/src/client/views/nodes/MapBox/MapAnchorMenu.tsx @@ -13,9 +13,9 @@ import { unimplementedFunction } from '../../../../Utils'; import { Doc, Opt } from '../../../../fields/Doc'; import { NumCast, StrCast } from '../../../../fields/Types'; import { CalendarManager } from '../../../util/CalendarManager'; -import { SelectionManager } from '../../../util/SelectionManager'; import { SettingsManager } from '../../../util/SettingsManager'; import { AntimodeMenu, AntimodeMenuProps } from '../../AntimodeMenu'; +import { DocumentView } from '../DocumentView'; import './MapAnchorMenu.scss'; import { MapboxApiUtility, TransportationType } from './MapboxApiUtility'; import { MarkerIcons } from './MarkerIcons'; @@ -126,7 +126,7 @@ export class MapAnchorMenu extends AntimodeMenu<AntimodeMenuProps> { componentDidMount() { this._disposer = reaction( - () => SelectionManager.Views.slice(), + () => DocumentView.Selected().slice(), () => MapAnchorMenu.Instance.fadeOut(true) ); } diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx index 50831f8ea..ac8010f11 100644 --- a/src/client/views/nodes/MapBox/MapBox.tsx +++ b/src/client/views/nodes/MapBox/MapBox.tsx @@ -21,9 +21,7 @@ import { DocCast, NumCast, StrCast, toList } from '../../../../fields/Types'; import { DocUtils } from '../../../documents/DocUtils'; import { DocumentType } from '../../../documents/DocumentTypes'; import { Docs } from '../../../documents/Documents'; -import { DocumentManager } from '../../../util/DocumentManager'; import { DragManager } from '../../../util/DragManager'; -import { LinkManager } from '../../../util/LinkManager'; import { UndoManager, undoable } from '../../../util/UndoManager'; import { ViewBoxAnnotatableComponent, ViewBoxInterface } from '../../DocComponent'; import { PinDocView, PinProps } from '../../PinFuncs'; @@ -249,7 +247,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implem if (existingPin) { setTimeout(() => { // we use a timeout in case this is called from the sidebar which may have just added a link that hasn't made its way into th elink manager yet - if (!LinkManager.Instance.getAllRelatedLinks(doc).some(link => DocCast(link.link_anchor_1)?.mapPin === existingPin || DocCast(link.link_anchor_2)?.mapPin === existingPin)) { + if (!Doc.Links(doc).some(link => DocCast(link.link_anchor_1)?.mapPin === existingPin || DocCast(link.link_anchor_2)?.mapPin === existingPin)) { const anchor = this.getAnchor(true, undefined, existingPin); anchor && DocUtils.MakeLink(anchor, doc, { link_relationship: 'link to map location' }); doc.latitude = existingPin?.latitude; @@ -438,7 +436,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implem options.didMove = true; } return new Promise<Opt<DocumentView>>(res => { - DocumentManager.Instance.AddViewRenderedCb(doc, dv => res(dv)); + DocumentView.addViewRenderedCb(doc, dv => res(dv)); }); }; diff --git a/src/client/views/nodes/MapBox/MapBox2.tsx b/src/client/views/nodes/MapBox/MapBox2.tsx index 9825824bd..7697fd295 100644 --- a/src/client/views/nodes/MapBox/MapBox2.tsx +++ b/src/client/views/nodes/MapBox/MapBox2.tsx @@ -509,9 +509,9 @@ // // TODO: auto center on select a document in the sidebar // private handleMapCenter = (map: google.maps.Map) => { -// // console.log("print the selected views in selectionManager:") -// // if (SelectionManager.Views.lastElement()) { -// // console.log(SelectionManager.Views.lastElement()); +// // console.log("print the selected views in Document.Selected:") +// // if (DocumentView.Selected().lastElement()) { +// // console.log(DocumentView.Selected().lastElement()); // // } // }; diff --git a/src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx b/src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx index 3b4ffd4bd..bfd40692b 100644 --- a/src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx +++ b/src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx @@ -13,9 +13,7 @@ import { DocCast, NumCast, StrCast, toList } from '../../../../fields/Types'; import { DocUtils } from '../../../documents/DocUtils'; import { DocumentType } from '../../../documents/DocumentTypes'; import { Docs } from '../../../documents/Documents'; -import { DocumentManager } from '../../../util/DocumentManager'; import { DragManager } from '../../../util/DragManager'; -import { LinkManager } from '../../../util/LinkManager'; import { Transform } from '../../../util/Transform'; import { UndoManager, undoable } from '../../../util/UndoManager'; import { ViewBoxAnnotatableComponent } from '../../DocComponent'; @@ -130,7 +128,7 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent<FieldViewProps> if (existingPin) { setTimeout(() => { // we use a timeout in case this is called from the sidebar which may have just added a link that hasn't made its way into th elink manager yet - if (!LinkManager.Instance.getAllRelatedLinks(doc).some(link => DocCast(link.link_anchor_1)?.mapPin === existingPin || DocCast(link.link_anchor_2)?.mapPin === existingPin)) { + if (!Doc.Links(doc).some(link => DocCast(link.link_anchor_1)?.mapPin === existingPin || DocCast(link.link_anchor_2)?.mapPin === existingPin)) { const anchor = this.getAnchor(true, undefined, existingPin); anchor && DocUtils.MakeLink(anchor, doc, { link_relationship: 'link to map location' }); doc.latitude = existingPin?.latitude; @@ -390,7 +388,7 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent<FieldViewProps> options.didMove = true; } return new Promise<Opt<DocumentView>>(res => { - DocumentManager.Instance.AddViewRenderedCb(doc, dv => res(dv)); + DocumentView.addViewRenderedCb(doc, dv => res(dv)); }); }; /* diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index 5d1874aca..9038ed27a 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -19,9 +19,7 @@ import { emptyFunction } from '../../../Utils'; import { Docs } from '../../documents/Documents'; import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes'; import { DocUtils } from '../../documents/DocUtils'; -import { DocumentManager } from '../../util/DocumentManager'; import { KeyCodes } from '../../util/KeyCodes'; -import { SelectionManager } from '../../util/SelectionManager'; import { undoBatch, UndoManager } from '../../util/UndoManager'; import { CollectionFreeFormView } from '../collections/collectionFreeForm'; import { CollectionStackingView } from '../collections/CollectionStackingView'; @@ -251,7 +249,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implem this.toggleSidebar(false); } return new Promise<Opt<DocumentView>>(res => { - DocumentManager.Instance.AddViewRenderedCb(doc, dv => res(dv)); + DocumentView.addViewRenderedCb(doc, dv => res(dv)); }); }; @@ -561,7 +559,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implem removeDocument={this.removeDocument} /> ) : ( - <div onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, emptyFunction, () => SelectionManager.SelectView(this.DocumentView?.()!, false), true)}> + <div onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, emptyFunction, () => DocumentView.SelectView(this.DocumentView?.()!, false), true)}> <ComponentTag // eslint-disable-next-line react/jsx-props-no-spreading {...this._props} diff --git a/src/client/views/nodes/RecordingBox/RecordingBox.tsx b/src/client/views/nodes/RecordingBox/RecordingBox.tsx index ae6caf9f4..e46e40bfe 100644 --- a/src/client/views/nodes/RecordingBox/RecordingBox.tsx +++ b/src/client/views/nodes/RecordingBox/RecordingBox.tsx @@ -3,6 +3,7 @@ import { observer } from 'mobx-react'; import * as React from 'react'; import { DateField } from '../../../../fields/DateField'; import { Doc, DocListCast } from '../../../../fields/Doc'; +import { DocData } from '../../../../fields/DocSymbols'; import { Id } from '../../../../fields/FieldSymbols'; import { List } from '../../../../fields/List'; import { BoolCast, DocCast } from '../../../../fields/Types'; @@ -10,19 +11,18 @@ import { VideoField } from '../../../../fields/URLField'; import { Upload } from '../../../../server/SharedMediaTypes'; import { DocumentType } from '../../../documents/DocumentTypes'; import { Docs } from '../../../documents/Documents'; -import { DocumentManager } from '../../../util/DocumentManager'; import { DragManager } from '../../../util/DragManager'; +import { dropActionType } from '../../../util/DropActionTypes'; import { ScriptingGlobals } from '../../../util/ScriptingGlobals'; import { Presentation } from '../../../util/TrackMovements'; import { undoBatch } from '../../../util/UndoManager'; import { ViewBoxBaseComponent } from '../../DocComponent'; import { CollectionFreeFormView } from '../../collections/collectionFreeForm/CollectionFreeFormView'; import { mediaState } from '../AudioBox'; +import { DocumentView } from '../DocumentView'; import { FieldView, FieldViewProps } from '../FieldView'; import { VideoBox } from '../VideoBox'; import { RecordingView } from './RecordingView'; -import { DocData } from '../../../../fields/DocSymbols'; -import { dropActionType } from '../../../util/DropActionTypes'; @observer export class RecordingBox extends ViewBoxBaseComponent<FieldViewProps>() { @@ -102,7 +102,7 @@ export class RecordingBox extends ViewBoxBaseComponent<FieldViewProps>() { screengrabber.overlayY = 590; // was 0 screengrabber[DocData][Doc.LayoutFieldKey(screengrabber) + '_trackScreen'] = true; Doc.AddToMyOverlay(screengrabber); // just adds doc to overlay - DocumentManager.Instance.AddViewRenderedCb(screengrabber, docView => { + DocumentView.addViewRenderedCb(screengrabber, docView => { RecordingBox.screengrabber = docView.ComponentView as RecordingBox; RecordingBox.screengrabber.Record?.(); }); @@ -119,7 +119,7 @@ export class RecordingBox extends ViewBoxBaseComponent<FieldViewProps>() { value.overlayX = 70; value.overlayY = window.innerHeight - 180; Doc.AddToMyOverlay(value); - DocumentManager.Instance.AddViewRenderedCb(value, docView => { + DocumentView.addViewRenderedCb(value, docView => { Doc.UserDoc().currentRecording = docView.Document; docView.select(false); RecordingBox.resumeWorkspaceReplaying(value); @@ -132,7 +132,7 @@ export class RecordingBox extends ViewBoxBaseComponent<FieldViewProps>() { */ @undoBatch public static addRecToWorkspace(value: RecordingBox) { - const ffView = Array.from(DocumentManager.Instance.DocumentViews).find(view => view.ComponentView instanceof CollectionFreeFormView); + const ffView = DocumentView.allViews().find(view => view.ComponentView instanceof CollectionFreeFormView); (ffView?.ComponentView as CollectionFreeFormView)._props.addDocument?.(value.Document); Doc.RemoveDocFromList(Doc.UserDoc(), 'workspaceRecordings', value.Document); Doc.RemFromMyOverlay(value.Document); @@ -142,7 +142,7 @@ export class RecordingBox extends ViewBoxBaseComponent<FieldViewProps>() { } public static resumeWorkspaceReplaying(doc: Doc) { - const docView = DocumentManager.Instance.getDocumentView(doc); + const docView = DocumentView.getDocumentView(doc); if (docView?.ComponentView instanceof VideoBox) { docView.ComponentView.Play(); } @@ -150,7 +150,7 @@ export class RecordingBox extends ViewBoxBaseComponent<FieldViewProps>() { } public static pauseWorkspaceReplaying(doc: Doc) { - const docView = DocumentManager.Instance.getDocumentView(doc); + const docView = DocumentView.getDocumentView(doc); const videoBox = docView?.ComponentView as VideoBox; if (videoBox) { videoBox.Pause(); diff --git a/src/client/views/nodes/ScreenshotBox.tsx b/src/client/views/nodes/ScreenshotBox.tsx index 8eb55b2f8..3be50f5e6 100644 --- a/src/client/views/nodes/ScreenshotBox.tsx +++ b/src/client/views/nodes/ScreenshotBox.tsx @@ -24,6 +24,7 @@ import { SettingsManager } from '../../util/SettingsManager'; import { TrackMovements } from '../../util/TrackMovements'; import { ContextMenu } from '../ContextMenu'; import { ViewBoxAnnotatableComponent } from '../DocComponent'; +import { DocViewUtils } from '../DocViewUtils'; import { CollectionStackedTimeline } from '../collections/CollectionStackedTimeline'; import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView'; import { mediaState } from './AudioBox'; @@ -161,8 +162,8 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<FieldViewProps>() // }); } componentWillUnmount() { - const ind = DocUtils.ActiveRecordings.indexOf(this); - ind !== -1 && DocUtils.ActiveRecordings.splice(ind, 1); + const ind = DocViewUtils.ActiveRecordings.indexOf(this); + ind !== -1 && DocViewUtils.ActiveRecordings.splice(ind, 1); } specificContextMenu = (): void => { @@ -265,7 +266,7 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<FieldViewProps>() this._screenCapture = true; this.dataDoc.mediaState = 'recording'; }); - DocUtils.ActiveRecordings.push(this); + DocViewUtils.ActiveRecordings.push(this); } else { this._audioRec?.stop(); this._videoRec?.stop(); @@ -273,8 +274,8 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<FieldViewProps>() this._screenCapture = false; this.dataDoc.mediaState = 'paused'; }); - const ind = DocUtils.ActiveRecordings.indexOf(this); - ind !== -1 && DocUtils.ActiveRecordings.splice(ind, 1); + const ind = DocViewUtils.ActiveRecordings.indexOf(this); + ind !== -1 && DocViewUtils.ActiveRecordings.splice(ind, 1); CaptureManager.Instance.open(this.Document); } diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index f32d00386..afd73cfe8 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -16,9 +16,7 @@ import { emptyFunction, formatTime } from '../../../Utils'; import { Docs } from '../../documents/Documents'; import { DocumentType } from '../../documents/DocumentTypes'; import { DocUtils, FollowLinkScript } from '../../documents/DocUtils'; -import { DocumentManager } from '../../util/DocumentManager'; import { dropActionType } from '../../util/DropActionTypes'; -import { LinkManager } from '../../util/LinkManager'; import { ReplayMovements } from '../../util/ReplayMovements'; import { undoBatch } from '../../util/UndoManager'; import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView'; @@ -26,16 +24,16 @@ import { CollectionStackedTimeline, TrimScope } from '../collections/CollectionS import { ContextMenu } from '../ContextMenu'; import { ContextMenuProps } from '../ContextMenuItem'; import { ViewBoxAnnotatableComponent, ViewBoxInterface } from '../DocComponent'; +import { VideoThumbnails } from '../global/globalEnums'; import { MarqueeAnnotator } from '../MarqueeAnnotator'; import { AnchorMenu } from '../pdf/AnchorMenu'; import { PinDocView, PinProps } from '../PinFuncs'; -import { StyleProp } from '../StyleProvider'; +import { StyleProp } from '../StyleProp'; import { DocumentView } from './DocumentView'; import { FieldView, FieldViewProps } from './FieldView'; import { FocusViewOptions } from './FocusViewOptions'; import { RecordingBox } from './RecordingBox'; import './VideoBox.scss'; -import { VideoThumbnails } from '../global/globalEnums'; /** * VideoBox @@ -88,7 +86,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps>() impl @observable _scrubbing: boolean = false; @computed get links() { - return LinkManager.Links(this.dataDoc); + return Doc.Links(this.dataDoc); } @computed get heightPercent() { return NumCast(this.layoutDoc._layout_timelineHeightPercent, 100); @@ -347,7 +345,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps>() impl this._props.addDocument?.(imageSnapshot); DocUtils.MakeLink(imageSnapshot, this.getAnchor(true), { link_relationship: 'video snapshot' }); // link && (DocCast(link.link_anchor_2)[DocData].timecodeToHide = NumCast(DocCast(link.link_anchor_2).timecodeToShow) + 3); // do we need to set an end time? should default to +0.1 - setTimeout(() => downX !== undefined && downY !== undefined && DocumentManager.Instance.getFirstDocumentView(imageSnapshot)?.startDragging(downX, downY, dropActionType.move, true)); + setTimeout(() => downX !== undefined && downY !== undefined && DocumentView.getFirstDocumentView(imageSnapshot)?.startDragging(downX, downY, dropActionType.move, true)); }; getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => { @@ -392,7 +390,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps>() impl return this._stackedTimeline.getView(doc, options); } return new Promise<Opt<DocumentView>>(res => { - DocumentManager.Instance.AddViewRenderedCb(doc, dv => res(dv)); + DocumentView.addViewRenderedCb(doc, dv => res(dv)); }); }; @@ -446,7 +444,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps>() impl { fireImmediately: true } ); - (!this.dataDoc[this.fieldKey + '_thumbnails'] || StrListCast(this.dataDoc[this.fieldKey + '_thumbnails']).length !== VideoBox.numThumbnails) && this.getVideoThumbnails(); + (!this.dataDoc[this.fieldKey + '_thumbnails'] || StrListCast(this.dataDoc[this.fieldKey + '_thumbnails']).length !== VideoThumbnails.DENSE) && this.getVideoThumbnails(); } }; @@ -652,20 +650,20 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps>() impl @action removeCurrentlyPlaying = () => { const docView = this.DocumentView?.(); - if (CollectionStackedTimeline.CurrentlyPlaying && docView) { - const index = CollectionStackedTimeline.CurrentlyPlaying.indexOf(docView); - index !== -1 && CollectionStackedTimeline.CurrentlyPlaying.splice(index, 1); + if (DocumentView.CurrentlyPlaying && docView) { + const index = DocumentView.CurrentlyPlaying.indexOf(docView); + index !== -1 && DocumentView.CurrentlyPlaying.splice(index, 1); } }; // adds doc to currently playing display @action addCurrentlyPlaying = () => { const docView = this.DocumentView?.(); - if (!CollectionStackedTimeline.CurrentlyPlaying) { - CollectionStackedTimeline.CurrentlyPlaying = []; + if (!DocumentView.CurrentlyPlaying) { + DocumentView.CurrentlyPlaying = []; } - if (docView && CollectionStackedTimeline.CurrentlyPlaying.indexOf(docView) === -1) { - CollectionStackedTimeline.CurrentlyPlaying.push(docView); + if (docView && DocumentView.CurrentlyPlaying.indexOf(docView) === -1) { + DocumentView.CurrentlyPlaying.push(docView); } }; diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx index 54f246b20..1003bc473 100644 --- a/src/client/views/nodes/WebBox.tsx +++ b/src/client/views/nodes/WebBox.tsx @@ -21,7 +21,6 @@ import { emptyFunction, stringHash } from '../../../Utils'; import { Docs } from '../../documents/Documents'; import { DocumentType } from '../../documents/DocumentTypes'; import { DocUtils } from '../../documents/DocUtils'; -import { DocumentManager } from '../../util/DocumentManager'; import { ScriptingGlobals } from '../../util/ScriptingGlobals'; import { SnappingManager } from '../../util/SnappingManager'; import { undoBatch, UndoManager } from '../../util/UndoManager'; @@ -38,7 +37,7 @@ import { Annotation } from '../pdf/Annotation'; import { GPTPopup } from '../pdf/GPTPopup/GPTPopup'; import { PinDocView, PinProps } from '../PinFuncs'; import { SidebarAnnos } from '../SidebarAnnos'; -import { StyleProp } from '../StyleProvider'; +import { StyleProp } from '../StyleProp'; import { DocumentView } from './DocumentView'; import { FieldView, FieldViewProps } from './FieldView'; import { FocusViewOptions } from './FocusViewOptions'; @@ -324,7 +323,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implem if (this._url && webUrl && webUrl.href !== this._url) this.setData(webUrl.href); if (this._sidebarRef?.current?.makeDocUnfiltered(doc) && !this.SidebarShown) this.toggleSidebar(false); return new Promise<Opt<DocumentView>>(res => { - DocumentManager.Instance.AddViewRenderedCb(doc, dv => res(dv)); + DocumentView.addViewRenderedCb(doc, dv => res(dv)); }); }; diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx index 1c5ea2dd4..5d53a2a5f 100644 --- a/src/client/views/nodes/formattedText/DashFieldView.tsx +++ b/src/client/views/nodes/formattedText/DashFieldView.tsx @@ -293,7 +293,6 @@ export class DashFieldView { unclickable = () => !this.tbox._props.rootSelected?.() && this.node.marks.some((m: any) => m.type === this.tbox.EditorView?.state.schema.marks.linkAnchor && m.attrs.noPreview); constructor(node: any, view: any, getPos: any, tbox: FormattedTextBox) { makeObservable(this); - const self = this; this.node = node; this.tbox = tbox; this.getpos = getPos; @@ -305,14 +304,14 @@ export class DashFieldView { this.dom.onkeypress = function (e: KeyboardEvent) { e.stopPropagation(); }; - this.dom.onkeydown = function (e: KeyboardEvent) { + this.dom.onkeydown = (e: KeyboardEvent) => { e.stopPropagation(); if (e.key === 'Tab') { e.preventDefault(); const editor = tbox.EditorView; if (editor) { const { state } = editor; - for (let i = self.getpos() + 1; i < state.doc.content.size; i++) { + for (let i = this.getpos() + 1; i < state.doc.content.size; i++) { if (state.doc.nodeAt(i)?.type.name === state.schema.nodes.dashField.name) { editor.dispatch(state.tr.setSelection(new NodeSelection(state.doc.resolve(i)))); return; diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index ad6629fc9..8a4a7718a 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -32,16 +32,13 @@ import { Docs } from '../../../documents/Documents'; import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes'; import { DocUtils } from '../../../documents/DocUtils'; import { DictationManager } from '../../../util/DictationManager'; -import { DocumentManager } from '../../../util/DocumentManager'; import { DragManager } from '../../../util/DragManager'; import { dropActionType } from '../../../util/DropActionTypes'; import { MakeTemplate } from '../../../util/DropConverter'; import { LinkManager } from '../../../util/LinkManager'; import { RTFMarkup } from '../../../util/RTFMarkup'; -import { SelectionManager } from '../../../util/SelectionManager'; import { SnappingManager } from '../../../util/SnappingManager'; import { undoable, undoBatch, UndoManager } from '../../../util/UndoManager'; -import { CollectionFreeFormView } from '../../collections/collectionFreeForm/CollectionFreeFormView'; import { CollectionStackingView } from '../../collections/CollectionStackingView'; import { CollectionTreeView } from '../../collections/CollectionTreeView'; import { ContextMenu } from '../../ContextMenu'; @@ -53,9 +50,10 @@ import { AnchorMenu } from '../../pdf/AnchorMenu'; import { GPTPopup } from '../../pdf/GPTPopup/GPTPopup'; import { PinDocView, PinProps } from '../../PinFuncs'; import { SidebarAnnos } from '../../SidebarAnnos'; -import { styleFromLayoutString, StyleProp } from '../../StyleProvider'; +import { StyleProp } from '../../StyleProp'; +import { styleFromLayoutString } from '../../StyleProvider'; import { mediaState } from '../AudioBox'; -import { DocumentView, DocumentViewInternal } from '../DocumentView'; +import { DocumentView } from '../DocumentView'; import { FieldView, FieldViewProps } from '../FieldView'; import { FocusViewOptions } from '../FocusViewOptions'; import { LinkInfo } from '../LinkDocPreview'; @@ -260,7 +258,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB let stopFunc: any; const targetData = target[DocData]; targetData.mediaState = mediaState.Recording; - DocumentViewInternal.recordAudioAnnotation(targetData, Doc.LayoutFieldKey(target), stop => { stopFunc = stop }); // prettier-ignore + DictationManager.recordAudioAnnotation(targetData, Doc.LayoutFieldKey(target), stop => { stopFunc = stop }); // prettier-ignore const reactionDisposer = reaction( () => target.mediaState, @@ -271,7 +269,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB } } ); - target.title = ComputedField.MakeFunction(`self["text_audioAnnotations_text"].lastElement()`); + target.title = ComputedField.MakeFunction(`this.text_audioAnnotations_text.lastElement()`); } }); }; @@ -400,7 +398,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB insertTime = () => { let linkTime; let linkAnchor; - LinkManager.Links(this.dataDoc).forEach(l => { + Doc.Links(this.dataDoc).forEach(l => { const anchor = DocCast(l.link_anchor_1)?.annotationOn ? DocCast(l.link_anchor_1) : DocCast(l.link_anchor_2)?.annotationOn ? DocCast(l.link_anchor_2) : undefined; if (anchor && (anchor.annotationOn as Doc).mediaState === mediaState.Recording) { linkTime = NumCast(anchor._timecodeToShow /* audioStart */); @@ -423,7 +421,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB autoLink = () => { const newAutoLinks = new Set<Doc>(); - const oldAutoLinks = LinkManager.Links(this.Document).filter( + const oldAutoLinks = Doc.Links(this.Document).filter( link => ((!Doc.isTemplateForField(this.Document) && (!Doc.isTemplateForField(DocCast(link.link_anchor_1)) || !Doc.AreProtosEqual(DocCast(link.link_anchor_1), this.Document)) && @@ -442,7 +440,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB tr = tr.setSelection(new TextSelection(tr.doc.resolve(from), tr.doc.resolve(to))); this._editorView?.dispatch(tr); } - oldAutoLinks.filter(oldLink => !newAutoLinks.has(oldLink) && oldLink.link_anchor_2 !== this.Document).forEach(LinkManager.Instance.deleteLink); + oldAutoLinks.filter(oldLink => !newAutoLinks.has(oldLink) && oldLink.link_anchor_2 !== this.Document).forEach(doc => Doc.DeleteLink?.(doc)); }; updateTitle = () => { @@ -496,7 +494,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB if (node.firstChild === null && !node.marks.find((m: Mark) => m.type.name === schema.marks.noAutoLinkAnchor.name) && node.marks.find((m: Mark) => m.type.name === schema.marks.splitter.name)) { alink = alink ?? - (LinkManager.Links(this.Document).find( + (Doc.Links(this.Document).find( link => Doc.AreProtosEqual(Cast(link.link_anchor_1, Doc, null), this.Document) && // Doc.AreProtosEqual(Cast(link.link_anchor_2, Doc, null), target) @@ -787,7 +785,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB deleteAnnotation = (anchor: Doc) => { const batch = UndoManager.StartBatch('delete link'); - LinkManager.Instance.deleteLink(LinkManager.Links(anchor)[0]); + Doc.DeleteLink?.(Doc.Links(anchor)[0]); // const docAnnotations = DocListCast(this._props.dataDoc[this.fieldKey]); // this._props.dataDoc[this.fieldKey] = new List<Doc>(docAnnotations.filter(a => a !== this.annoTextRegion)); // AnchorMenu.Instance.fadeOut(true); @@ -837,7 +835,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB anchorDoc && DocServer.GetRefField(anchorDoc).then( action(anchor => { - anchor && SelectionManager.SelectSchemaViewDoc(anchor as Doc); + anchor && DocumentView.SelectSchemaDoc(anchor as Doc); AnchorMenu.Instance.Status = 'annotation'; AnchorMenu.Instance.Delete = !anchor && editor.state.selection.empty ? returnFalse : !anchor ? deleteMarkups : () => this.deleteAnnotation(anchor as Doc); AnchorMenu.Instance.Pinned = false; @@ -1124,7 +1122,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB setTimeout(() => this._sidebarRef?.current?.makeDocUnfiltered(doc)); } return new Promise<Opt<DocumentView>>(res => { - DocumentManager.Instance.AddViewRenderedCb(doc, dv => res(dv)); + DocumentView.addViewRenderedCb(doc, dv => res(dv)); }); }; focus = (textAnchor: Doc, options: FocusViewOptions) => { @@ -1206,7 +1204,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB } componentDidMount() { !this._props.dontSelectOnLoad && this._props.setContentViewBox?.(this); // this tells the DocumentView that this AudioBox is the "content" of the document. this allows the DocumentView to indirectly call getAnchor() on the AudioBox when making a link. - this._cachedLinks = LinkManager.Links(this.Document); + this._cachedLinks = Doc.Links(this.Document); this._disposers.breakupDictation = reaction(() => Doc.RecordingEvent, this.breakupDictation); this._disposers.layout_autoHeight = reaction( () => ({ autoHeight: this.layout_autoHeight, fontSize: this.fontSize, css: this.Document[DocCss] }), @@ -1244,7 +1242,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB { fireImmediately: !Array.from(FormattedTextBox._globalHighlights).includes('Bold Text') } ); this._disposers.links = reaction( - () => LinkManager.Links(this.dataDoc), // if a link is deleted, then remove all hyperlinks that reference it from the text's marks + () => Doc.Links(this.dataDoc), // if a link is deleted, then remove all hyperlinks that reference it from the text's marks newLinks => { this._cachedLinks.forEach(l => !newLinks.includes(l) && this.RemoveLinkFromDoc(l)); this._cachedLinks = newLinks; @@ -1556,7 +1554,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB // const timecode = NumCast(anchor.timecodeToShow, 0); const audiodoc = anchor.annotationOn as Doc; const func = () => { - const docView = DocumentManager.Instance.getDocumentView(audiodoc); + const docView = DocumentView.getDocumentView(audiodoc); if (!docView) { this._props.addDocTab(audiodoc, OpenWhere.addBottom); setTimeout(func); @@ -1769,7 +1767,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB case 'Escape': this._editorView!.dispatch(state.tr.setSelection(TextSelection.create(state.doc, state.selection.from, state.selection.from))); (document.activeElement as any).blur?.(); - SelectionManager.DeselectAll(); + DocumentView.DeselectAll(); RichTextMenu.Instance?.updateMenu(undefined, undefined, undefined, undefined); return; case 'Enter': @@ -1891,7 +1889,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB } @computed get sidebarCollection() { const renderComponent = (tag: string) => { - const ComponentTag: any = tag === CollectionViewType.Freeform ? CollectionFreeFormView : tag === CollectionViewType.Tree ? CollectionTreeView : tag === 'translation' ? FormattedTextBox : CollectionStackingView; + const ComponentTag: any = tag === CollectionViewType.Tree ? CollectionTreeView : tag === 'translation' ? FormattedTextBox : CollectionStackingView; return ComponentTag === CollectionStackingView ? ( <SidebarAnnos ref={this._sidebarRef} @@ -1913,7 +1911,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB setHeight={this.setSidebarHeight} /> ) : ( - <div onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, emptyFunction, () => SelectionManager.SelectView(this.DocumentView?.()!, false), true)}> + <div onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, emptyFunction, () => DocumentView.SelectView(this.DocumentView?.()!, false), true)}> <ComponentTag // eslint-disable-next-line react/jsx-props-no-spreading {...this._props} @@ -1998,11 +1996,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB @observable _isHovering = false; onPassiveWheel = (e: WheelEvent) => { if (e.clientX > this.ProseRef!.getBoundingClientRect().right) { - if (this.dataDoc[this.SidebarKey + '_type_collection'] === CollectionViewType.Freeform) { - // if the scrolled freeform is a child of the sidebar component, we need to let the event go through - // so react can let the freeform view handle it. We prevent default to stop any containing views from scrolling - e.preventDefault(); - } return; } @@ -2037,7 +2030,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB } render() { TraceMobx(); - const scale = this._props.NativeDimScaling?.() || 1; // * NumCast(this.layoutDoc._freeform_scale, 1); + const scale = this._props.NativeDimScaling?.() || 1; const rounded = StrCast(this.layoutDoc.layout_borderRounding) === '100%' ? '-rounded' : ''; setTimeout(() => !this._props.isContentActive() && FormattedTextBoxComment.textBox === this && FormattedTextBoxComment.Hide); const paddingX = NumCast(this.layoutDoc._xMargin, this._props.xPadding || 0); diff --git a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts index bc6454146..7a8b72be0 100644 --- a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts +++ b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts @@ -11,7 +11,7 @@ import { AclAdmin, AclAugment, AclEdit, DocData } from '../../../../fields/DocSy import { GetEffectiveAcl } from '../../../../fields/util'; import { Docs } from '../../../documents/Documents'; import { RTFMarkup } from '../../../util/RTFMarkup'; -import { SelectionManager } from '../../../util/SelectionManager'; +import { DocumentView } from '../DocumentView'; import { OpenWhere } from '../OpenWhere'; const mac = typeof navigator !== 'undefined' ? /Mac/.test(navigator.platform) : false; @@ -172,7 +172,7 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any): KeyMa bind('Escape', (state: EditorState, dispatch: (tx: Transaction) => void) => { dispatch(state.tr.setSelection(TextSelection.create(state.doc, state.selection.from, state.selection.from))); (document.activeElement as any).blur?.(); - SelectionManager.DeselectAll(); + DocumentView.DeselectAll(); }); bind('Alt-Enter', () => onKey() || true); diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx index 6c12b9991..a612f3c65 100644 --- a/src/client/views/nodes/formattedText/RichTextMenu.tsx +++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx @@ -11,11 +11,10 @@ import * as React from 'react'; import { Doc } from '../../../../fields/Doc'; import { BoolCast, Cast, StrCast } from '../../../../fields/Types'; import { DocServer } from '../../../DocServer'; -import { LinkManager } from '../../../util/LinkManager'; -import { SelectionManager } from '../../../util/SelectionManager'; import { undoBatch, UndoManager } from '../../../util/UndoManager'; import { AntimodeMenu, AntimodeMenuProps } from '../../AntimodeMenu'; import { ObservableReactComponent } from '../../ObservableReactComponent'; +import { DocumentView } from '../DocumentView'; import { EquationBox } from '../EquationBox'; import { FieldViewProps } from '../FieldView'; import { FormattedTextBox } from './FormattedTextBox'; @@ -116,7 +115,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> { _disposer: IReactionDisposer | undefined; componentDidMount() { this._disposer = reaction( - () => SelectionManager.Views.slice(), + () => DocumentView.Selected().slice(), () => this.updateMenu(undefined, undefined, undefined, undefined) ); } @@ -144,8 +143,8 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> { const { activeSizes } = active; const { activeColors } = active; const { activeHighlights } = active; - const refDoc = SelectionManager.Views.lastElement()?.layoutDoc ?? Doc.UserDoc(); - const refField = (pfx => (pfx ? pfx + '_' : ''))(SelectionManager.Views.lastElement()?.LayoutFieldKey); + const refDoc = DocumentView.Selected().lastElement()?.layoutDoc ?? Doc.UserDoc(); + const refField = (pfx => (pfx ? pfx + '_' : ''))(DocumentView.Selected().lastElement()?.LayoutFieldKey); const refVal = (field: string, dflt: string) => StrCast(refDoc[refField + field], StrCast(Doc.UserDoc()[field], dflt)); this._activeListType = this.getActiveListStyle(); @@ -237,8 +236,8 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> { m.type === state.schema.marks.pFontSize && activeSizes.add(m.attrs.fontSize); m.type === state.schema.marks.pFontHighlight && activeHighlights.add(String(m.attrs.fontHighlight)); }); - } else if (SelectionManager.Views.some(dv => dv.ComponentView instanceof EquationBox)) { - SelectionManager.Views.forEach(dv => StrCast(dv.Document._text_fontSize) && activeSizes.add(StrCast(dv.Document._text_fontSize))); + } else if (DocumentView.Selected().some(dv => dv.ComponentView instanceof EquationBox)) { + DocumentView.Selected().forEach(dv => StrCast(dv.Document._text_fontSize) && activeSizes.add(StrCast(dv.Document._text_fontSize))); } return { activeFamilies: Array.from(activeFamilies), activeSizes: Array.from(activeSizes), activeColors: Array.from(activeColors), activeHighlights: Array.from(activeHighlights) }; } @@ -566,7 +565,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> { if (linkDoc instanceof Doc) { const linkAnchor1 = await Cast(linkDoc.link_anchor_1, Doc); const linkAnchor2 = await Cast(linkDoc.link_anchor_2, Doc); - const currentDoc = SelectionManager.Docs.lastElement(); + const currentDoc = DocumentView.Selected().lastElement().Document; if (currentDoc && linkAnchor1 && linkAnchor2) { if (Doc.AreProtosEqual(currentDoc, linkAnchor1)) { return StrCast(linkAnchor2.title); @@ -605,7 +604,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> { .filter((aref: any) => aref?.href.indexOf(Doc.localServerPath()) === 0) .forEach((aref: any) => { const anchorId = aref.href.replace(Doc.localServerPath(), '').split('?')[0]; - anchorId && DocServer.GetRefField(anchorId).then(linkDoc => LinkManager.Instance.deleteLink(linkDoc as Doc)); + anchorId && DocServer.GetRefField(anchorId).then(linkDoc => Doc.DeleteLink?.(linkDoc as Doc)); }); } } diff --git a/src/client/views/nodes/formattedText/SummaryView.tsx b/src/client/views/nodes/formattedText/SummaryView.tsx index 2c366b49b..238267f6e 100644 --- a/src/client/views/nodes/formattedText/SummaryView.tsx +++ b/src/client/views/nodes/formattedText/SummaryView.tsx @@ -21,11 +21,10 @@ export class SummaryView { root: any; constructor(node: any, view: any, getPos: any) { - const self = this; this.dom = document.createElement('span'); this.dom.className = this.className(node.attrs.visibility); - this.dom.onpointerdown = function (e: any) { - self.onPointerDown(e, node, view, getPos); + this.dom.onpointerdown = (e: any) => { + this.onPointerDown(e, node, view, getPos); }; this.dom.onkeypress = function (e: any) { e.stopPropagation(); diff --git a/src/client/views/nodes/generativeFill/GenerativeFill.tsx b/src/client/views/nodes/generativeFill/GenerativeFill.tsx index d5fad296f..91b0ebd5c 100644 --- a/src/client/views/nodes/generativeFill/GenerativeFill.tsx +++ b/src/client/views/nodes/generativeFill/GenerativeFill.tsx @@ -16,7 +16,6 @@ import { NumCast } from '../../../../fields/Types'; import { Networking } from '../../../Network'; import { DocUtils } from '../../../documents/DocUtils'; import { Docs } from '../../../documents/Documents'; -import { DocumentManager } from '../../../util/DocumentManager'; import { CollectionDockingView } from '../../collections/CollectionDockingView'; import { CollectionFreeFormView } from '../../collections/collectionFreeForm'; import { ImageEditorData } from '../ImageBox'; @@ -28,6 +27,7 @@ import { APISuccess, ImageUtility } from './generativeFillUtils/ImageHandler'; import { PointerHandler } from './generativeFillUtils/PointerHandler'; import { activeColor, canvasSize, eraserColor, freeformRenderSize, newCollectionSize, offsetDistanceY, offsetX } from './generativeFillUtils/generativeFillConstants'; import { CursorData, ImageDimensions, Point } from './generativeFillUtils/generativeFillInterfaces'; +import { DocumentView } from '../DocumentView'; // enum BrushStyle { // ADD, @@ -295,7 +295,7 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD _height: newCollectionSize, title: 'Image edit collection', }); - DocUtils.MakeLink(imageRootDoc, newCollectionRef.current, { link_relationship: 'Image Edit Version History', link_displayLine: false }); + DocUtils.MakeLink(imageRootDoc, newCollectionRef.current, { link_relationship: 'Image Edit Version History' }); // opening new tab CollectionDockingView.AddSplit(newCollectionRef.current, OpenWhereMod.right); @@ -400,7 +400,7 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD parentDoc.current.gen_fill_children = new List<Doc>([newImg]); } - DocUtils.MakeLink(parentDoc.current, newImg, { link_relationship: `Image edit; Prompt: ${input}`, link_displayLine: true }); + DocUtils.MakeLink(parentDoc.current, newImg, { link_relationship: `Image edit; Prompt: ${input}` }); adjustImgPositions(); if (isNewCollection && newCollectionRef.current) { @@ -430,7 +430,7 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD ImageEditorData.Open = false; ImageEditorData.Source = ''; if (newCollectionRef.current) { - DocumentManager.Instance.AddViewRenderedCb(newCollectionRef.current, dv => (dv.ComponentView as CollectionFreeFormView)?.fitContentOnce()); + DocumentView.addViewRenderedCb(newCollectionRef.current, dv => (dv.ComponentView as CollectionFreeFormView)?.fitContentOnce()); } setEdits([]); }; diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx index 485ba7367..6b4f5e073 100644 --- a/src/client/views/nodes/trails/PresBox.tsx +++ b/src/client/views/nodes/trails/PresBox.tsx @@ -19,17 +19,14 @@ import { emptyFunction, emptyPath, stringHash } from '../../../../Utils'; import { DocServer } from '../../../DocServer'; import { Docs } from '../../../documents/Documents'; import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes'; -import { DocumentManager } from '../../../util/DocumentManager'; import { dropActionType } from '../../../util/DropActionTypes'; import { ScriptingGlobals } from '../../../util/ScriptingGlobals'; -import { SelectionManager } from '../../../util/SelectionManager'; import { SerializationHelper } from '../../../util/SerializationHelper'; -import { SettingsManager } from '../../../util/SettingsManager'; import { SnappingManager } from '../../../util/SnappingManager'; import { undoBatch, UndoManager } from '../../../util/UndoManager'; import { CollectionDockingView } from '../../collections/CollectionDockingView'; import { CollectionFreeFormView } from '../../collections/collectionFreeForm'; -import { CollectionStackedTimeline } from '../../collections/CollectionStackedTimeline'; +import { CollectionFreeFormPannableContents } from '../../collections/collectionFreeForm/CollectionFreeFormPannableContents'; import { CollectionView } from '../../collections/CollectionView'; import { TreeView } from '../../collections/TreeView'; import { ViewBoxBaseComponent } from '../../DocComponent'; @@ -55,8 +52,9 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { super(props); makeObservable(this); if (!PresBox.navigateToDocScript) { - PresBox.navigateToDocScript = ScriptField.MakeFunction('navigateToDoc(this.presentation_targetDoc, self)')!; + PresBox.navigateToDocScript = ScriptField.MakeFunction('navigateToDoc(this.presentation_targetDoc, this)')!; } + CollectionFreeFormPannableContents.SetOverlayPlugin((fform: Doc) => PresBox.Instance.pathLines(fform)); } private _disposers: { [name: string]: IReactionDisposer } = {}; @@ -122,8 +120,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { return false; } @computed get selectedDocumentView() { - if (SelectionManager.Views.length) return SelectionManager.Views[0]; - if (this.selectedArray.size) return DocumentManager.Instance.getDocumentView(this.Document); + if (DocumentView.Selected().length) return DocumentView.Selected()[0]; + if (this.selectedArray.size) return DocumentView.getDocumentView(this.Document); return undefined; } @computed get isPres() { @@ -175,7 +173,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { this._unmounting = false; this.turnOffEdit(true); this._disposers.selection = reaction( - () => SelectionManager.Views.slice(), + () => DocumentView.Selected().slice(), views => (!PresBox.Instance || views.some(view => view.Document === this.Document)) && this.updateCurrentPresentation(), { fireImmediately: true } ); @@ -200,7 +198,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { startTempMedia = (targetDoc: Doc, activeItem: Doc) => { const duration: number = NumCast(activeItem.config_clipEnd) - NumCast(activeItem.config_clipStart); if ([DocumentType.VID, DocumentType.AUDIO].includes(targetDoc.type as any)) { - const targMedia = DocumentManager.Instance.getDocumentView(targetDoc); + const targMedia = DocumentView.getDocumentView(targetDoc); targMedia?.ComponentView?.playFrom?.(NumCast(activeItem.config_clipStart), NumCast(activeItem.config_clipStart) + duration); } }; @@ -208,7 +206,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { stopTempMedia = (targetDocField: FieldResult) => { const targetDoc = DocCast(DocCast(targetDocField).annotationOn) ?? DocCast(targetDocField); if ([DocumentType.VID, DocumentType.AUDIO].includes(targetDoc.type as any)) { - const targMedia = DocumentManager.Instance.getDocumentView(targetDoc); + const targMedia = DocumentView.getDocumentView(targetDoc); targMedia?.ComponentView?.Pause?.(); } }; @@ -260,7 +258,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { // go to documents chain runSubroutines = (childrenToRun: Opt<Doc[]>, normallyNextSlide: Doc) => { if (childrenToRun && childrenToRun[0] !== normallyNextSlide) { - childrenToRun.forEach(child => DocumentManager.Instance.showDocument(child, {})); + childrenToRun.forEach(child => DocumentView.showDocument(child, {})); } }; @@ -280,7 +278,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { if (listItems && presIndexed < listItems.length) { if (!first) { const listItemDoc = listItems[presIndexed]; - const targetView = listItems && DocumentManager.Instance.getFirstDocumentView(listItemDoc); + const targetView = listItems && DocumentView.getFirstDocumentView(listItemDoc); Doc.linkFollowUnhighlight(); Doc.HighlightDoc(listItemDoc); listItemDoc.presentation_effect = this.activeItem.presBulletEffect; @@ -425,7 +423,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { const acontext = activeItem.config_activeFrame !== undefined ? DocCast(DocCast(activeItem.presentation_targetDoc).embedContainer) : DocCast(activeItem.presentation_targetDoc); const context = DocCast(acontext)?.annotationOn ? DocCast(DocCast(acontext).annotationOn) : acontext; if (context) { - const ffview = CollectionFreeFormView.from(DocumentManager.Instance.getFirstDocumentView(context)); + const ffview = CollectionFreeFormView.from(DocumentView.getFirstDocumentView(context)); if (ffview?.childDocs) { PresBox.Instance._keyTimer = CollectionFreeFormView.gotoKeyframe(PresBox.Instance._keyTimer, ffview.childDocs, frameTime); ffview.layoutDoc._currentFrame = NumCast(activeFrame); @@ -605,7 +603,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { const viewport = { panX: (contentBounds[0] + contentBounds[2]) / 2, panY: (contentBounds[1] + contentBounds[3]) / 2, width: contentBounds[2] - contentBounds[0], height: contentBounds[3] - contentBounds[1] }; bestTarget._freeform_panX = viewport.panX; bestTarget._freeform_panY = viewport.panY; - const dv = DocumentManager.Instance.getDocumentView(bestTarget); + const dv = DocumentView.getDocumentView(bestTarget); if (dv) { changed = true; const computedScale = NumCast(activeItem.config_zoom, 1) * Math.min(dv._props.PanelWidth() / viewport.width, dv._props.PanelHeight() / viewport.height); @@ -644,8 +642,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { const eleViewCache = Array.from(this._eleArray); const resetSelection = action(() => { if (!this._props.isSelected()) { - const presDocView = DocumentManager.Instance.getDocumentView(this.Document); - if (presDocView) SelectionManager.SelectView(presDocView, false); + const presDocView = DocumentView.getDocumentView(this.Document); + if (presDocView) DocumentView.SelectView(presDocView, false); this.clearSelectedArray(); selViewCache.forEach(doc => this.addToSelectedArray(doc)); this._dragArray.splice(0, this._dragArray.length, ...dragViewCache); @@ -660,7 +658,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { static NavigateToTarget(targetDoc: Doc, activeItem: Doc, finished?: () => void) { if (activeItem.presentation_movement === PresMovement.None && targetDoc.type === DocumentType.SCRIPTING) { - (DocumentManager.Instance.getFirstDocumentView(targetDoc)?.ComponentView as ScriptingBox)?.onRun?.(); + (DocumentView.getFirstDocumentView(targetDoc)?.ComponentView as ScriptingBox)?.onRun?.(); return; } const effect = activeItem.presentation_effect && activeItem.presentation_effect !== PresEffect.None ? activeItem.presentation_effect : undefined; @@ -680,19 +678,19 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { }; if (activeItem.presentation_openInLightbox) { const context = DocCast(targetDoc.annotationOn) ?? targetDoc; - if (!DocumentManager.Instance.getLightboxDocumentView(context)) { + if (!DocumentView.getLightboxDocumentView(context)) { LightboxView.Instance.SetLightboxDoc(context); } } if (targetDoc) { if (activeItem.presentation_targetDoc instanceof Doc) activeItem.presentation_targetDoc[Animation] = undefined; - DocumentManager.Instance.AddViewRenderedCb(LightboxView.LightboxDoc, () => { + DocumentView.addViewRenderedCb(LightboxView.LightboxDoc, () => { // if target or the doc it annotates is not in the lightbox, then close the lightbox - if (!DocumentManager.Instance.getLightboxDocumentView(DocCast(targetDoc.annotationOn) ?? targetDoc)) { + if (!DocumentView.getLightboxDocumentView(DocCast(targetDoc.annotationOn) ?? targetDoc)) { LightboxView.Instance.SetLightboxDoc(undefined); } - DocumentManager.Instance.showDocument(targetDoc, options, finished); + DocumentView.showDocument(targetDoc, options, finished); }); } else finished?.(); } @@ -1032,8 +1030,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { @action selectPres = () => { - const presDocView = DocumentManager.Instance.getDocumentView(this.Document); - presDocView && SelectionManager.SelectView(presDocView, false); + const presDocView = DocumentView.getDocumentView(this.Document); + presDocView && DocumentView.SelectView(presDocView, false); }; focusElement = (doc: Doc) => { @@ -1044,7 +1042,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { // Regular click @action selectElement = (doc: Doc, noNav = false) => { - CollectionStackedTimeline.CurrentlyPlaying?.map(clip => clip?.ComponentView?.Pause?.()); + DocumentView.CurrentlyPlaying?.map(clip => clip?.ComponentView?.Pause?.()); if (noNav) { const index = this.childDocs.indexOf(doc); if (index >= 0 && index < this.childDocs.length) { @@ -1200,7 +1198,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { const order: JSX.Element[] = []; const docs = new Set<Doc>(); const presCollection = collection; - const dv = DocumentManager.Instance.getDocumentView(presCollection); + const dv = DocumentView.getDocumentView(presCollection); this.childDocs.forEach((doc, index) => { const tagDoc = PresBox.targetRenderedDoc(doc); const srcContext = Cast(tagDoc.embedContainer, Doc, null); @@ -2206,7 +2204,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { if (doc) { const tabMap = CollectionDockingView.Instance?.tabMap; const docTab = tabMap && Array.from(tabMap).find(tab => tab.DashDoc.type === DocumentType.COL)?.DashDoc; - const presCollection = DocumentManager.GetContextPath(this.activeItem).reverse().lastElement().presentation_targetDoc ?? docTab; + const presCollection = DocumentView.getContextPath(this.activeItem).reverse().lastElement().presentation_targetDoc ?? docTab; const data = Cast(presCollection?.data, listSpec(Doc)); const configData = Cast(this.Document.data, listSpec(Doc)); if (data && configData) { @@ -2282,12 +2280,12 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { @action toggleProperties = () => { - SettingsManager.Instance.propertiesWidth = SettingsManager.Instance.propertiesWidth > 0 ? 0 : 250; + SnappingManager.SetPropertiesWidth(SnappingManager.PropertiesWidth > 0 ? 0 : 250); }; @computed get toolbar() { - const propIcon = SettingsManager.Instance.propertiesWidth > 0 ? 'angle-double-right' : 'angle-double-left'; - const propTitle = SettingsManager.Instance.propertiesWidth > 0 ? 'Close Presentation Panel' : 'Open Presentation Panel'; + const propIcon = SnappingManager.PropertiesWidth > 0 ? 'angle-double-right' : 'angle-double-left'; + const propTitle = SnappingManager.PropertiesWidth > 0 ? 'Close Presentation Panel' : 'Open Presentation Panel'; const mode = StrCast(this.Document._type_collection) as CollectionViewType; const isMini: boolean = this.toolbarWidth <= 100; const activeColor = SnappingManager.userVariantColor; @@ -2316,7 +2314,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { </Tooltip> <Tooltip title={<div className="dash-tooltip">{propTitle}</div>}> <div className="toolbar-button" style={{ position: 'absolute', right: 4, fontSize: 16 }} onClick={this.toggleProperties}> - <FontAwesomeIcon className="toolbar-thumbtack" icon={propIcon} style={{ color: SettingsManager.Instance.propertiesWidth > 0 ? activeColor : inactiveColor }} /> + <FontAwesomeIcon className="toolbar-thumbtack" icon={propIcon} style={{ color: SnappingManager.PropertiesWidth > 0 ? activeColor : inactiveColor }} /> </div> </Tooltip> </> diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx index af0ab3b53..306b98190 100644 --- a/src/client/views/nodes/trails/PresElementBox.tsx +++ b/src/client/views/nodes/trails/PresElementBox.tsx @@ -13,9 +13,8 @@ import { BoolCast, Cast, DocCast, NumCast, StrCast } from '../../../../fields/Ty import { emptyFunction } from '../../../../Utils'; import { Docs } from '../../../documents/Documents'; import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes'; -import { DocumentManager } from '../../../util/DocumentManager'; import { DragManager } from '../../../util/DragManager'; -import { SettingsManager } from '../../../util/SettingsManager'; +import { SnappingManager } from '../../../util/SnappingManager'; import { Transform } from '../../../util/Transform'; import { undoable, undoBatch } from '../../../util/UndoManager'; import { TreeView } from '../../collections/TreeView'; @@ -23,7 +22,7 @@ import { ViewBoxBaseComponent } from '../../DocComponent'; import { EditableView } from '../../EditableView'; import { Colors } from '../../global/globalEnums'; import { PinDocView } from '../../PinFuncs'; -import { StyleProp } from '../../StyleProvider'; +import { StyleProp } from '../../StyleProp'; import { DocumentView } from '../DocumentView'; import { FieldView, FieldViewProps } from '../FieldView'; import { PresBox } from './PresBox'; @@ -282,8 +281,8 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() { @action toggleProperties = () => { - if (SettingsManager.Instance.propertiesWidth < 5) { - SettingsManager.Instance.propertiesWidth = 250; + if (SnappingManager.PropertiesWidth < 5) { + SnappingManager.SetPropertiesWidth(250); } }; @@ -417,7 +416,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() { @computed get toolbarWidth(): number { - const presBoxDocView = DocumentManager.Instance.getDocumentView(this.presBox); + const presBoxDocView = DocumentView.getDocumentView(this.presBox); const width = NumCast(this.presBox?._width); return presBoxDocView ? presBoxDocView._props.PanelWidth() : width || 300; } diff --git a/src/client/views/pdf/AnchorMenu.tsx b/src/client/views/pdf/AnchorMenu.tsx index f02c56471..a837969aa 100644 --- a/src/client/views/pdf/AnchorMenu.tsx +++ b/src/client/views/pdf/AnchorMenu.tsx @@ -8,12 +8,12 @@ import { ClientUtils, returnFalse, setupMoveUpEvents } from '../../../ClientUtil import { emptyFunction, unimplementedFunction } from '../../../Utils'; import { Doc, Opt } from '../../../fields/Doc'; import { DocumentType } from '../../documents/DocumentTypes'; -import { SelectionManager } from '../../util/SelectionManager'; import { SettingsManager } from '../../util/SettingsManager'; import { AntimodeMenu, AntimodeMenuProps } from '../AntimodeMenu'; import { LinkPopup } from '../linking/LinkPopup'; import './AnchorMenu.scss'; import { GPTPopup } from './GPTPopup/GPTPopup'; +import { DocumentView } from '../nodes/DocumentView'; @observer export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> { @@ -66,7 +66,7 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> { componentDidMount() { this._disposer = reaction( - () => SelectionManager.Views.slice(), + () => DocumentView.Selected().slice(), () => AnchorMenu.Instance.fadeOut(true) ); } @@ -145,7 +145,7 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> { * all selected text available to summarize but its only supported for pdf and web ATM. * @returns Whether the GPT icon for summarization should appear */ - canSummarize = () => SelectionManager.Docs.some(doc => [DocumentType.PDF, DocumentType.WEB].includes(doc.type as any)); + canSummarize = () => DocumentView.SelectedDocs().some(doc => [DocumentType.PDF, DocumentType.WEB].includes(doc.type as any)); render() { const buttons = diff --git a/src/client/views/pdf/Annotation.tsx b/src/client/views/pdf/Annotation.tsx index 8b2b179d3..7dd4047c1 100644 --- a/src/client/views/pdf/Annotation.tsx +++ b/src/client/views/pdf/Annotation.tsx @@ -5,10 +5,10 @@ import { Doc, DocListCast, Opt, StrListCast } from '../../../fields/Doc'; import { Highlight } from '../../../fields/DocSymbols'; import { List } from '../../../fields/List'; import { BoolCast, DocCast, NumCast, StrCast } from '../../../fields/Types'; -import { LinkFollower } from '../../util/LinkFollower'; import { LinkManager } from '../../util/LinkManager'; import { undoable } from '../../util/UndoManager'; import { ObservableReactComponent } from '../ObservableReactComponent'; +import { DocumentView } from '../nodes/DocumentView'; import { FieldViewProps } from '../nodes/FieldView'; import { OpenWhere } from '../nodes/OpenWhere'; import { AnchorMenu } from './AnchorMenu'; @@ -56,7 +56,7 @@ export class Annotation extends ObservableReactComponent<IAnnotationProps> { @computed get linkHighlighted() { const found = LinkManager.Instance.getAllDirectLinks(this._props.annoDoc).find(link => { - const a1 = LinkManager.getOppositeAnchor(link, this._props.annoDoc); + const a1 = Doc.getOppositeAnchor(link, this._props.annoDoc); return a1 && Doc.GetBrushStatus(DocCast(a1.annotationOn, a1)); }); return found; @@ -103,7 +103,7 @@ export class Annotation extends ObservableReactComponent<IAnnotationProps> { e.preventDefault(); } else if (e.button === 0) { e.stopPropagation(); - LinkFollower.FollowLink(undefined, this._props.annoDoc, false); + DocumentView.FollowLink(undefined, this._props.annoDoc, false); } }; brushed = () => this._props.annoDoc && Doc.GetBrushHighlightStatus(this._props.annoDoc); diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 15ac73f78..45b1d727e 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -15,17 +15,17 @@ import { Cast, NumCast, StrCast } from '../../../fields/Types'; import { TraceMobx } from '../../../fields/util'; import { emptyFunction } from '../../../Utils'; import { DocUtils } from '../../documents/DocUtils'; -import { SelectionManager } from '../../util/SelectionManager'; import { SnappingManager } from '../../util/SnappingManager'; import { MarqueeOptionsMenu } from '../collections/collectionFreeForm'; import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView'; import { MarqueeAnnotator } from '../MarqueeAnnotator'; +import { DocumentView } from '../nodes/DocumentView'; import { FieldViewProps } from '../nodes/FieldView'; import { FocusViewOptions } from '../nodes/FocusViewOptions'; import { LinkInfo } from '../nodes/LinkDocPreview'; import { PDFBox } from '../nodes/PDFBox'; import { ObservableReactComponent } from '../ObservableReactComponent'; -import { StyleProp } from '../StyleProvider'; +import { StyleProp } from '../StyleProp'; import { AnchorMenu } from './AnchorMenu'; import { Annotation } from './Annotation'; import { GPTPopup } from './GPTPopup/GPTPopup'; @@ -122,7 +122,7 @@ export class PDFViewer extends ObservableReactComponent<IViewerProps> { this._disposers.selected = reaction( () => this._props.isSelected(), - () => SelectionManager.Views.length === 1 && this.setupPdfJsViewer(), + () => DocumentView.Selected().length === 1 && this.setupPdfJsViewer(), { fireImmediately: true } ); this._disposers.curPage = reaction( diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx index 5df934231..56552c952 100644 --- a/src/client/views/search/SearchBox.tsx +++ b/src/client/views/search/SearchBox.tsx @@ -12,16 +12,15 @@ import { DocCast, StrCast } from '../../../fields/Types'; import { DocUtils } from '../../documents/DocUtils'; import { DocumentType } from '../../documents/DocumentTypes'; import { Docs } from '../../documents/Documents'; -import { DocumentManager } from '../../util/DocumentManager'; -import { LinkManager } from '../../util/LinkManager'; import { SearchUtil } from '../../util/SearchUtil'; -import { SettingsManager } from '../../util/SettingsManager'; +import { SnappingManager } from '../../util/SnappingManager'; import { undoBatch } from '../../util/UndoManager'; import { ViewBoxBaseComponent } from '../DocComponent'; import { ObservableReactComponent } from '../ObservableReactComponent'; import { CollectionDockingView } from '../collections/CollectionDockingView'; import { IRecommendation, Recommendation } from '../newlightbox/components'; import { fetchRecommendations } from '../newlightbox/utils'; +import { DocumentView } from '../nodes/DocumentView'; import { FieldView, FieldViewProps } from '../nodes/FieldView'; import './SearchBox.scss'; @@ -54,7 +53,7 @@ export class SearchBoxItem extends ObservableReactComponent<SearchBoxItemProps> * or opening it in a new tab. */ selectElement = async (doc: Doc, finishFunc: () => void) => { - await DocumentManager.Instance.showDocument(doc, { willZoomCentered: true }, finishFunc); + await DocumentView.showDocument(doc, { willZoomCentered: true }, finishFunc); }; /** @@ -65,12 +64,12 @@ export class SearchBoxItem extends ObservableReactComponent<SearchBoxItemProps> */ onResultClick = action(async (doc: Doc) => { this._props.selectItem(doc); - this.selectElement(doc, () => DocumentManager.Instance.getFirstDocumentView(doc)?.ComponentView?.search?.(this._props.searchString, undefined, false)); + this.selectElement(doc, () => DocumentView.getFirstDocumentView(doc)?.ComponentView?.search?.(this._props.searchString, undefined, false)); }); componentWillUnmount(): void { const doc = this._props.Document; - DocumentManager.Instance.getFirstDocumentView(doc)?.ComponentView?.search?.('', undefined, true); + DocumentView.getFirstDocumentView(doc)?.ComponentView?.search?.('', undefined, true); } @undoBatch @@ -99,20 +98,18 @@ export class SearchBoxItem extends ObservableReactComponent<SearchBoxItemProps> } } style={{ - fontWeight: LinkManager.Links(this._props.linkFrom).find( - link => - Doc.AreProtosEqual(LinkManager.getOppositeAnchor(link, this._props.linkFrom!), this._props.Document) || - Doc.AreProtosEqual(DocCast(LinkManager.getOppositeAnchor(link, this._props.linkFrom!)?.annotationOn), this._props.Document) + fontWeight: Doc.Links(this._props.linkFrom).find( + link => Doc.AreProtosEqual(Doc.getOppositeAnchor(link, this._props.linkFrom!), this._props.Document) || Doc.AreProtosEqual(DocCast(Doc.getOppositeAnchor(link, this._props.linkFrom!)?.annotationOn), this._props.Document) ) ? 'bold' : '', }} className={this._props.className}> <div className="searchBox-result-title">{title as string}</div> - <div className="searchBox-result-type" style={{ color: SettingsManager.userVariantColor }}> + <div className="searchBox-result-type" style={{ color: SnappingManager.userVariantColor }}> {formattedType} </div> - <div className="searchBox-result-keys" style={{ color: SettingsManager.userVariantColor }}> + <div className="searchBox-result-keys" style={{ color: SnappingManager.userVariantColor }}> {this._props.matchedKeys.join(', ')} </div> </div> @@ -373,7 +370,7 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() { const query = StrCast(this._searchString); Doc.SetSearchQuery(query); - if (!this._props.linkSearch) Array.from(this._results.keys()).forEach(doc => DocumentManager.Instance.getFirstDocumentView(doc)?.ComponentView?.search?.(this._searchString, undefined, true)); + if (!this._props.linkSearch) Array.from(this._results.keys()).forEach(doc => DocumentView.getFirstDocumentView(doc)?.ComponentView?.search?.(this._searchString, undefined, true)); this._results.clear(); if (query) { @@ -412,7 +409,7 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() { this._timeout && clearTimeout(this._timeout); this._timeout = undefined; this._results.forEach((_, doc) => { - DocumentManager.Instance.getFirstDocumentView(doc)?.ComponentView?.search?.('', undefined, true); + DocumentView.getFirstDocumentView(doc)?.ComponentView?.search?.('', undefined, true); Doc.UnBrushDoc(doc); Doc.UnHighlightDoc(doc); Doc.ClearSearchMatches(); @@ -476,7 +473,7 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() { const recommendationsJSX: JSX.Element[] = this._recommendations.map(props => <Recommendation {...props} />); return ( - <div className="searchBox-container" style={{ pointerEvents: 'all', color: SettingsManager.userColor, background: SettingsManager.userBackgroundColor }}> + <div className="searchBox-container" style={{ pointerEvents: 'all', color: SnappingManager.userColor, background: SnappingManager.userBackgroundColor }}> <div className="searchBox-bar"> {isLinkSearch ? null : ( <select name="type" id="searchBox-type" className="searchBox-type" onChange={this.onSelectChange}> @@ -501,7 +498,7 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() { </div> {resultsJSX.length > 0 && ( <div className="searchBox-results-container"> - <div className="section-header" style={{ background: SettingsManager.userVariantColor }}> + <div className="section-header" style={{ background: SnappingManager.userVariantColor }}> <div className="section-title">Results</div> <div className="section-subtitle">{`${validResults} result` + (validResults === 1 ? '' : 's')}</div> </div> @@ -510,7 +507,7 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() { )} {recommendationsJSX.length > 0 && ( <div className="searchBox-recommendations-container"> - <div className="section-header" style={{ background: SettingsManager.userVariantColor }}> + <div className="section-header" style={{ background: SnappingManager.userVariantColor }}> <div className="section-title">Recommendations</div> <div className="section-subtitle">{`${validResults} result` + (validResults === 1 ? '' : 's')}</div> </div> diff --git a/src/client/views/selectedDoc/SelectedDocView.tsx b/src/client/views/selectedDoc/SelectedDocView.tsx index 6ed3b240d..78a1a92f7 100644 --- a/src/client/views/selectedDoc/SelectedDocView.tsx +++ b/src/client/views/selectedDoc/SelectedDocView.tsx @@ -6,8 +6,8 @@ import * as React from 'react'; import { emptyFunction } from '../../../Utils'; import { Doc } from '../../../fields/Doc'; import { StrCast } from '../../../fields/Types'; -import { DocumentManager } from '../../util/DocumentManager'; -import { SettingsManager } from '../../util/SettingsManager'; +import { SnappingManager } from '../../util/SnappingManager'; +import { DocumentView } from '../nodes/DocumentView'; import { FocusViewOptions } from '../nodes/FocusViewOptions'; export interface SelectedDocViewProps { @@ -33,14 +33,14 @@ export class SelectedDocView extends React.Component<SelectedDocViewProps> { return { text: StrCast(doc.title), val: StrCast(doc._id), - color: SettingsManager.userColor, - background: SettingsManager.userBackgroundColor, + color: SnappingManager.userColor, + background: SnappingManager.userBackgroundColor, icon: <FontAwesomeIcon size="1x" icon={Doc.toIcon(doc)} />, - onClick: () => DocumentManager.Instance.showDocument(doc, options, emptyFunction), + onClick: () => DocumentView.showDocument(doc, options, emptyFunction), }; })} - color={SettingsManager.userColor} - background={SettingsManager.userBackgroundColor} + color={SnappingManager.userColor} + background={SnappingManager.userBackgroundColor} /> </div> ); diff --git a/src/client/views/topbar/TopBar.tsx b/src/client/views/topbar/TopBar.tsx index f82ae34c5..9f97efcce 100644 --- a/src/client/views/topbar/TopBar.tsx +++ b/src/client/views/topbar/TopBar.tsx @@ -11,8 +11,6 @@ import { AclAdmin, DashVersion } from '../../../fields/DocSymbols'; import { StrCast } from '../../../fields/Types'; import { GetEffectiveAcl } from '../../../fields/util'; import { emptyFunction } from '../../../Utils'; -import { CurrentUserUtils } from '../../util/CurrentUserUtils'; -import { DocumentManager } from '../../util/DocumentManager'; import { dropActionType } from '../../util/DropActionTypes'; import { PingManager } from '../../util/PingManager'; import { ReportManager } from '../../util/reportManager/ReportManager'; @@ -25,7 +23,7 @@ import { CollectionDockingView } from '../collections/CollectionDockingView'; import { CollectionLinearView } from '../collections/collectionLinear'; import { DashboardView } from '../DashboardView'; import { Colors } from '../global/globalEnums'; -import { DocumentViewInternal, returnEmptyDocViewList } from '../nodes/DocumentView'; +import { DocumentView, DocumentViewInternal, returnEmptyDocViewList } from '../nodes/DocumentView'; import { ObservableReactComponent } from '../ObservableReactComponent'; import { DefaultStyleProvider } from '../StyleProvider'; import './TopBar.scss'; @@ -170,7 +168,7 @@ export class TopBar extends ObservableReactComponent<{}> { color={this.color} style={{ fontWeight: 700, fontSize: '1rem' }} onClick={(e: React.MouseEvent) => { - const dashView = Doc.ActiveDashboard && DocumentManager.Instance.getDocumentView(Doc.ActiveDashboard); + const dashView = Doc.ActiveDashboard && DocumentView.getDocumentView(Doc.ActiveDashboard); dashView?.showContextMenu(e.clientX + 20, e.clientY + 30); }} /> @@ -184,7 +182,7 @@ export class TopBar extends ObservableReactComponent<{}> { * and allows the user to access their account settings etc. */ @computed get topbarRight() { - const upToDate = DashVersion === CurrentUserUtils.ServerVersion; + const upToDate = DashVersion === SnappingManager.ServerVersion; return ( <div className="topbar-right"> {Doc.ActiveDashboard ? ( diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index 5fd053eef..bb6995398 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -224,8 +224,25 @@ export class Doc extends RefField { @observable public static GuestDashboard: Doc | undefined = undefined; @observable public static GuestTarget: Doc | undefined = undefined; @observable public static GuestMobile: Doc | undefined = undefined; + @observable.shallow public static CurrentlyLoading: Doc[] = observable([]); + // removes from currently loading display + public static removeCurrentlyLoading(doc: Doc) { + if (Doc.CurrentlyLoading) { + const index = Doc.CurrentlyLoading.indexOf(doc); + runInAction(() => index !== -1 && Doc.CurrentlyLoading.splice(index, 1)); + } + } + // adds doc to currently loading display + public static addCurrentlyLoading(doc: Doc) { + if (Doc.CurrentlyLoading.indexOf(doc) === -1) { + runInAction(() => Doc.CurrentlyLoading.push(doc)); + } + } - public static AddLink: undefined | ((link: Doc, checkExists?: boolean) => void); + public static AddLink: (link: Doc, checkExists?: boolean) => void; + public static DeleteLink: (link: Doc) => void; + public static Links: (link: Doc | undefined) => Doc[]; + public static getOppositeAnchor: (linkDoc: Doc, anchor: Doc) => Doc | undefined; public static get MySharedDocs() { return DocCast(Doc.UserDoc().mySharedDocs); } // prettier-ignore public static get MyUserDocView() { return DocCast(Doc.UserDoc().myUserDocView); } // prettier-ignore diff --git a/src/fields/ScriptField.ts b/src/fields/ScriptField.ts index 8a3787768..8fe365ac2 100644 --- a/src/fields/ScriptField.ts +++ b/src/fields/ScriptField.ts @@ -142,7 +142,6 @@ export class ScriptField extends ObjectField { return CompileScript(script, { params: { this: Doc?.name || 'Doc', // this is the doc that executes the script - self: Doc?.name || 'Doc', // self is the root doc of the doc that executes the script documentView: 'any', _last_: 'any', // _last_ is the previous value of a computed field when it is being triggered to re-run. _setCacheResult_: 'any', // set the cached value of the function diff --git a/src/fields/documentSchemas.ts b/src/fields/documentSchemas.ts index c73689e1f..335683270 100644 --- a/src/fields/documentSchemas.ts +++ b/src/fields/documentSchemas.ts @@ -86,7 +86,6 @@ export const documentSchema = createSchema({ followLinkLocation: 'string', // flag for where to place content when following a click interaction (e.g., add:right, lightbox, default, ) hideLinkButton: 'boolean', // whether the blue link counter button should be hidden layout_hideAllLinks: 'boolean', // whether all individual blue anchor dots should be hidden - link_displayLine: 'boolean', // whether a link connection should be shown between link anchor endpoints. isLightbox: 'boolean', // whether the marked object will display addDocTab() calls that target "lightbox" destinations layers: listSpec('string'), // which layers the document is part of _lockedPosition: 'boolean', // whether the document can be moved (dragged) diff --git a/src/mobile/MobileInterface.tsx b/src/mobile/MobileInterface.tsx index 55a85ed1b..7e67690d3 100644 --- a/src/mobile/MobileInterface.tsx +++ b/src/mobile/MobileInterface.tsx @@ -92,7 +92,6 @@ import { faTrash, faTrashAlt, faTree, - faRoute, faTv, faUndoAlt, faVideo, @@ -104,7 +103,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { action, computed, observable, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; -import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue } from '../ClientUtils'; +import { returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue } from '../ClientUtils'; import { CollectionViewType, DocumentType } from '../client/documents/DocumentTypes'; import { Docs, DocumentOptions } from '../client/documents/Documents'; import { CurrentUserUtils } from '../client/util/CurrentUserUtils'; @@ -112,8 +111,8 @@ import { ScriptingGlobals } from '../client/util/ScriptingGlobals'; import { SettingsManager } from '../client/util/SettingsManager'; import { Transform } from '../client/util/Transform'; import { UndoManager } from '../client/util/UndoManager'; +import { DashboardView } from '../client/views/DashboardView'; import { GestureOverlay } from '../client/views/GestureOverlay'; -import { TabDocView } from '../client/views/collections/TabDocView'; import { AudioBox } from '../client/views/nodes/AudioBox'; import { DocumentView } from '../client/views/nodes/DocumentView'; import { RadialMenu } from '../client/views/nodes/RadialMenu'; @@ -127,7 +126,7 @@ import './AudioUpload.scss'; import { Uploader } from './ImageUpload'; import './ImageUpload.scss'; import './MobileInterface.scss'; -import { DashboardView } from '../client/views/DashboardView'; +import { emptyFunction } from '../Utils'; library.add( ...[ @@ -691,7 +690,7 @@ export class MobileInterface extends React.Component { // Only making button available if it is an image if (!(this._activeDoc.type === 'collection' || this._activeDoc.type === 'presentation')) { return ( - <div className="docButton" title={'Pin to presentation'} style={{ backgroundColor: 'white', color: 'black' }} onClick={e => TabDocView.PinDoc(this._activeDoc, {})}> + <div className="docButton" title={'Pin to presentation'} style={{ backgroundColor: 'white', color: 'black' }} onClick={e => DocumentView.PinDoc(this._activeDoc, {})}> <FontAwesomeIcon className="documentdecorations-icon" size="sm" icon="map-pin" /> </div> ); diff --git a/src/server/SharedMediaTypes.ts b/src/server/SharedMediaTypes.ts index 0a961f570..8ae13454e 100644 --- a/src/server/SharedMediaTypes.ts +++ b/src/server/SharedMediaTypes.ts @@ -13,6 +13,11 @@ export namespace AcceptableMedia { export const audioFormats = ['.wav', '.mp3', '.mpeg', '.flac', '.au', '.aiff', '.m4a', '.webm']; } +export enum AudioAnnoState { + stopped = 'stopped', + playing = 'playing', +} + export namespace Upload { export function isImageInformation(uploadResponse: Upload.FileInformation): uploadResponse is Upload.ImageInformation { return 'nativeWidth' in uploadResponse; |