aboutsummaryrefslogtreecommitdiff
path: root/src/client/util/SelectionManager.ts
blob: 4be9448b378edf921ff0dfe7c67102cf2eedf837 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
import { action, observable, ObservableMap } from 'mobx';
import { computedFn } from 'mobx-utils';
import { Doc, Opt } from '../../fields/Doc';
import { DocCast } from '../../fields/Types';
import { CollectionViewType } from '../documents/DocumentTypes';
import { DocumentView } from '../views/nodes/DocumentView';
import { LinkManager } from './LinkManager';
import { ScriptingGlobals } from './ScriptingGlobals';

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) {
                    if (LinkManager.currentLink && !LinkManager.Links(docView.rootDoc).includes(LinkManager.currentLink) && docView.rootDoc !== LinkManager.currentLink) {
                        LinkManager.currentLink = undefined;
                    }
                    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 {
            manager.SelectedSchemaDocument = undefined;
            Array.from(manager.SelectedViewsMap.keys()).forEach(dv => dv.props.whenChildContentsActiveChanged(false));
            manager.SelectedViewsMap.clear();
            manager.SelectedViews.length = 0;
        }
    }

    const manager = new Manager();

    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);
    }

    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;
    });
    // 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;
            }
        }

        manager.DeselectAll();
        if (found) manager.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);
    }
}
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;
    }
    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() {
    SelectionManager.DeselectAll();
});