aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects/frontend/src/graph/GraphStore.ts
diff options
context:
space:
mode:
Diffstat (limited to 'subprojects/frontend/src/graph/GraphStore.ts')
-rw-r--r--subprojects/frontend/src/graph/GraphStore.ts187
1 files changed, 187 insertions, 0 deletions
diff --git a/subprojects/frontend/src/graph/GraphStore.ts b/subprojects/frontend/src/graph/GraphStore.ts
new file mode 100644
index 00000000..ecb016b5
--- /dev/null
+++ b/subprojects/frontend/src/graph/GraphStore.ts
@@ -0,0 +1,187 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6
7import { makeAutoObservable, observable } from 'mobx';
8
9import type {
10 RelationMetadata,
11 SemanticsSuccessResult,
12} from '../xtext/xtextServiceResults';
13
14export type Visibility = 'all' | 'must' | 'none';
15
16export function getDefaultVisibility(
17 metadata: RelationMetadata | undefined,
18): Visibility {
19 if (metadata === undefined || metadata.arity <= 0 || metadata.arity > 2) {
20 return 'none';
21 }
22 const { detail } = metadata;
23 switch (detail.type) {
24 case 'class':
25 case 'reference':
26 case 'opposite':
27 return 'all';
28 case 'predicate':
29 return detail.error ? 'must' : 'none';
30 default:
31 return 'none';
32 }
33}
34
35export function isVisibilityAllowed(
36 metadata: RelationMetadata | undefined,
37 visibility: Visibility,
38): boolean {
39 if (metadata === undefined || metadata.arity <= 0 || metadata.arity > 2) {
40 return visibility === 'none';
41 }
42 const { detail } = metadata;
43 if (detail.type === 'predicate' && detail.error) {
44 // We can't display may matches of error predicates,
45 // because they have none by definition.
46 return visibility !== 'all';
47 }
48 return true;
49}
50
51export default class GraphStore {
52 semantics: SemanticsSuccessResult = {
53 nodes: [],
54 relations: [],
55 partialInterpretation: {},
56 };
57
58 relationMetadata = new Map<string, RelationMetadata>();
59
60 visibility = new Map<string, Visibility>();
61
62 abbreviate = true;
63
64 scopes = false;
65
66 selectedSymbol: RelationMetadata | undefined;
67
68 constructor() {
69 makeAutoObservable(this, {
70 semantics: observable.ref,
71 });
72 }
73
74 getVisibility(relation: string): Visibility {
75 const visibilityOverride = this.visibility.get(relation);
76 if (visibilityOverride !== undefined) {
77 return visibilityOverride;
78 }
79 return this.getDefaultVisibility(relation);
80 }
81
82 getDefaultVisibility(relation: string): Visibility {
83 const metadata = this.relationMetadata.get(relation);
84 return getDefaultVisibility(metadata);
85 }
86
87 isVisibilityAllowed(relation: string, visibility: Visibility): boolean {
88 const metadata = this.relationMetadata.get(relation);
89 return isVisibilityAllowed(metadata, visibility);
90 }
91
92 setVisibility(relation: string, visibility: Visibility): void {
93 const metadata = this.relationMetadata.get(relation);
94 if (metadata === undefined || !isVisibilityAllowed(metadata, visibility)) {
95 return;
96 }
97 const defaultVisiblity = getDefaultVisibility(metadata);
98 if (defaultVisiblity === visibility) {
99 this.visibility.delete(relation);
100 } else {
101 this.visibility.set(relation, visibility);
102 }
103 }
104
105 cycleVisibility(relation: string): void {
106 const metadata = this.relationMetadata.get(relation);
107 if (metadata === undefined) {
108 return;
109 }
110 switch (this.getVisibility(relation)) {
111 case 'none':
112 if (isVisibilityAllowed(metadata, 'must')) {
113 this.setVisibility(relation, 'must');
114 }
115 break;
116 case 'must':
117 {
118 const next = isVisibilityAllowed(metadata, 'all') ? 'all' : 'none';
119 this.setVisibility(relation, next);
120 }
121 break;
122 default:
123 this.setVisibility(relation, 'none');
124 break;
125 }
126 }
127
128 hideAll(): void {
129 this.relationMetadata.forEach((metadata, name) => {
130 if (getDefaultVisibility(metadata) === 'none') {
131 this.visibility.delete(name);
132 } else {
133 this.visibility.set(name, 'none');
134 }
135 });
136 }
137
138 resetFilter(): void {
139 this.visibility.clear();
140 }
141
142 getName({ name, simpleName }: { name: string; simpleName: string }): string {
143 return this.abbreviate ? simpleName : name;
144 }
145
146 toggleAbbrevaite(): void {
147 this.abbreviate = !this.abbreviate;
148 }
149
150 toggleScopes(): void {
151 this.scopes = !this.scopes;
152 }
153
154 setSelectedSymbol(option: RelationMetadata | undefined): void {
155 if (option === undefined) {
156 this.selectedSymbol = undefined;
157 return;
158 }
159 const metadata = this.relationMetadata.get(option.name);
160 if (metadata !== undefined) {
161 this.selectedSymbol = metadata;
162 } else {
163 this.selectedSymbol = undefined;
164 }
165 }
166
167 setSemantics(semantics: SemanticsSuccessResult) {
168 this.semantics = semantics;
169 this.relationMetadata.clear();
170 this.semantics.relations.forEach((metadata) => {
171 this.relationMetadata.set(metadata.name, metadata);
172 });
173 const toRemove = new Set<string>();
174 this.visibility.forEach((value, key) => {
175 if (
176 !this.isVisibilityAllowed(key, value) ||
177 this.getDefaultVisibility(key) === value
178 ) {
179 toRemove.add(key);
180 }
181 });
182 toRemove.forEach((key) => {
183 this.visibility.delete(key);
184 });
185 this.setSelectedSymbol(this.selectedSymbol);
186 }
187}