diff options
Diffstat (limited to 'src/client/util/SelectionManager.ts')
-rw-r--r-- | src/client/util/SelectionManager.ts | 155 |
1 files changed, 54 insertions, 101 deletions
diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts index d0f66d124..f2a327445 100644 --- a/src/client/util/SelectionManager.ts +++ b/src/client/util/SelectionManager.ts @@ -1,6 +1,6 @@ -import { action, observable, ObservableMap } from 'mobx'; -import { computedFn } from 'mobx-utils'; +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'; @@ -10,117 +10,70 @@ import { LinkManager } from './LinkManager'; import { ScriptingGlobals } from './ScriptingGlobals'; import { UndoManager } from './UndoManager'; -export namespace SelectionManager { - class Manager { - @observable IsDragging: boolean = false; - SelectedViewsMap: ObservableMap<DocumentView, Doc> = new ObservableMap(); - @observable SelectedViews: DocumentView[] = []; - @observable SelectedSchemaDocument: Doc | undefined; - - @action - SelectSchemaViewDoc(doc: Opt<Doc>) { - manager.SelectedSchemaDocument = doc; - } - @action - SelectView(docView: DocumentView, ctrlPressed: boolean): void { - // if doc is not in SelectedDocuments, add it - if (!manager.SelectedViewsMap.get(docView)) { - if (!ctrlPressed) { - this.DeselectAll(); - } - - manager.SelectedViews.push(docView); - manager.SelectedViewsMap.set(docView, docView.rootDoc); - docView.props.whenChildContentsActiveChanged(true); - } else if (!ctrlPressed && (Array.from(manager.SelectedViewsMap.entries()).length > 1 || manager.SelectedSchemaDocument)) { - Array.from(manager.SelectedViewsMap.keys()).map(dv => dv !== docView && dv.props.whenChildContentsActiveChanged(false)); - manager.SelectedSchemaDocument = undefined; - manager.SelectedViews.length = 0; - manager.SelectedViewsMap.clear(); - manager.SelectedViews.push(docView); - manager.SelectedViewsMap.set(docView, docView.rootDoc); - } - } - @action - DeselectView(docView?: DocumentView): void { - if (docView && manager.SelectedViewsMap.get(docView)) { - manager.SelectedViewsMap.delete(docView); - manager.SelectedViews.splice(manager.SelectedViews.indexOf(docView), 1); - docView.props.whenChildContentsActiveChanged(false); - } - } - @action - DeselectAll(): void { - LinkManager.currentLink = undefined; - LinkManager.currentLinkAnchor = undefined; - manager.SelectedSchemaDocument = undefined; - Array.from(manager.SelectedViewsMap.keys()).forEach(dv => dv.props.whenChildContentsActiveChanged(false)); - manager.SelectedViewsMap.clear(); - manager.SelectedViews.length = 0; - } +export class SelectionManager { + private static _manager: SelectionManager; + private static get Instance() { + return SelectionManager._manager ?? new SelectionManager(); } - const manager = new Manager(); + @observable.shallow SelectedViews: DocumentView[] = []; + @observable IsDragging: boolean = false; + @observable SelectedSchemaDocument: Doc | undefined = undefined; - export function DeselectView(docView?: DocumentView): void { - manager.DeselectView(docView); - } - export function SelectView(docView: DocumentView | undefined, ctrlPressed: boolean): void { - if (!docView) DeselectAll(); - else manager.SelectView(docView, ctrlPressed); - } - export function SelectSchemaViewDoc(document: Opt<Doc>, deselectAllFirst?: boolean): void { - if (deselectAllFirst) manager.DeselectAll(); - manager.SelectSchemaViewDoc(document); + private constructor() { + SelectionManager._manager = this; + makeObservable(this); } - const IsSelectedCache = computedFn(function isSelected(doc: DocumentView) { - // wrapping get() in a computedFn only generates mobx() invalidations when the return value of the function for the specific get parameters has changed - return manager.SelectedViewsMap.get(doc) ? true : false; + @action + public static SelectSchemaViewDoc = (doc: Opt<Doc>, deselectAllFirst?: boolean) => { + if (deselectAllFirst) this.DeselectAll(); + this.Instance.SelectedSchemaDocument = doc; + }; + + public static SelectView = action((docView: DocumentView | undefined, extendSelection: boolean): void => { + if (!docView) this.DeselectAll(); + else if (!docView.IsSelected) { + if (!extendSelection) this.DeselectAll(); + this.Instance.SelectedViews.push(docView); + docView.IsSelected = true; + docView._props.whenChildContentsActiveChanged(true); + } }); - // computed functions, such as used in IsSelected generate errors if they're called outside of a - // reaction context. Specifying the context with 'outsideReaction' allows an efficiency feature - // to avoid unnecessary mobx invalidations when running inside a reaction. - export function IsSelected(doc: DocumentView | undefined, outsideReaction?: boolean): boolean { - return !doc - ? false - : outsideReaction - ? manager.SelectedViewsMap.get(doc) - ? true - : false // get() accesses a hashtable -- setting anything in the hashtable generates a mobx invalidation for every get() - : IsSelectedCache(doc); - } - export function DeselectAll(except?: Doc): void { - let found: DocumentView | undefined = undefined; - if (except) { - for (const view of Array.from(manager.SelectedViewsMap.keys())) { - if (view.props.Document === except) found = view; - } + public static DeselectView = action((docView?: DocumentView): void => { + if (docView && this.Instance.SelectedViews.includes(docView)) { + docView.IsSelected = false; + this.Instance.SelectedViews.splice(this.Instance.SelectedViews.indexOf(docView), 1); + docView._props.whenChildContentsActiveChanged(false); } + }); - manager.DeselectAll(); - if (found) manager.SelectView(found, false); - } + public static DeselectAll = (except?: Doc): void => { + const found = this.Instance.SelectedViews.find(dv => dv.Document === except); + LinkManager.currentLink = undefined; + LinkManager.currentLinkAnchor = undefined; + runInAction(() => (this.Instance.SelectedSchemaDocument = undefined)); + this.Instance.SelectedViews.forEach(dv => { + dv.IsSelected = false; + dv._props.whenChildContentsActiveChanged(false); + }); + runInAction(() => (this.Instance.SelectedViews.length = 0)); + if (found) this.SelectView(found, false); + }; - export function Views(): Array<DocumentView> { - return manager.SelectedViews; - // Array.from(manager.SelectedViewsMap.keys()); //.filter(dv => manager.SelectedViews.get(dv)?._type_collection !== CollectionViewType.Docking); - } - export function SelectedSchemaDoc(): Doc | undefined { - return manager.SelectedSchemaDocument; - } - export function Docs(): Doc[] { - return manager.SelectedViews.map(dv => dv.rootDoc).filter(doc => doc?._type_collection !== CollectionViewType.Docking); - // Array.from(manager.SelectedViewsMap.values()).filter(doc => doc?._type_collection !== CollectionViewType.Docking); - } + 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 } + ScriptingGlobals.add(function SelectionManager_selectedDocType(type: string, expertMode: boolean, checkContext?: boolean) { if (Doc.noviceMode && expertMode) return false; if (type === 'tab') { - return SelectionManager.Views().lastElement()?.props.renderDepth === 0; + return SelectionManager.Views.lastElement()?._props.renderDepth === 0; } - let selected = (sel => (checkContext ? DocCast(sel?.embedContainer) : sel))(SelectionManager.SelectedSchemaDoc() ?? SelectionManager.Docs().lastElement()); + let selected = (sel => (checkContext ? DocCast(sel?.embedContainer) : sel))(SelectionManager.SelectedSchemaDoc ?? SelectionManager.Docs.lastElement()); return selected?.type === type || selected?.type_collection === type || !type; }); ScriptingGlobals.add(function deselectAll() { @@ -145,8 +98,8 @@ ScriptingGlobals.add(function redo() { return UndoManager.Redo(); }); ScriptingGlobals.add(function selectedDocs(container: Doc, excludeCollections: boolean, prevValue: any) { - const docs = SelectionManager.Views() - .map(dv => dv.props.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.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)) + ); return docs.length ? new List(docs) : prevValue; }); |