aboutsummaryrefslogtreecommitdiff
path: root/src/client/util/SelectionManager.ts
diff options
context:
space:
mode:
authorSophie Zhang <sophie_zhang@brown.edu>2024-01-25 11:35:26 -0500
committerSophie Zhang <sophie_zhang@brown.edu>2024-01-25 11:35:26 -0500
commitf3dab2a56db5e4a6a3dca58185d94e1ff7d1dc32 (patch)
treea7bc895266b53bb620dbd2dd71bad2e83b555446 /src/client/util/SelectionManager.ts
parentb5c5410b4af5d2c68d2107d3f064f6e3ec4ac3f2 (diff)
parent136f3d9f349d54e8bdd73b6380ea47c19e5edebf (diff)
Merge branch 'master' into sophie-ai-images
Diffstat (limited to 'src/client/util/SelectionManager.ts')
-rw-r--r--src/client/util/SelectionManager.ts155
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;
});