Skip to content

Commit 3e76258

Browse files
committed
Merge branch 'constraintSelect' of https://github.com/DataFlowAnalysis/WebEditor into constraintSelect
2 parents 72c709b + 091e5e4 commit 3e76258

File tree

11 files changed

+260
-225
lines changed

11 files changed

+260
-225
lines changed

package-lock.json

Lines changed: 170 additions & 169 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@
1111
"@eslint/js": "^9.31.0",
1212
"@fortawesome/fontawesome-free": "^6.7.2",
1313
"@vscode/codicons": "^0.0.36",
14-
"eslint": "^9.30.1",
15-
"eslint-config-prettier": "^10.1.5",
14+
"eslint": "^9.31.0",
15+
"eslint-config-prettier": "^10.1.8",
1616
"husky": "^9.1.7",
1717
"inversify": "^6.2.2",
1818
"lint-staged": "^16.1.2",
@@ -23,8 +23,8 @@
2323
"sprotty-elk": "^1.4.0",
2424
"sprotty-protocol": "^1.4.0",
2525
"typescript": "^5.8.3",
26-
"typescript-eslint": "^8.37.0",
27-
"vite": "^7.0.3"
26+
"typescript-eslint": "^8.38.0",
27+
"vite": "^7.0.6"
2828
},
2929
"scripts": {
3030
"dev": "vite",

src/features/constraintMenu/ConstraintMenu.ts

Lines changed: 41 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ export class ConstraintMenu extends AbstractUIExtension implements Switchable {
3030
private tree: AutoCompleteTree;
3131
private forceReadOnly: boolean;
3232
private optionsMenu?: HTMLDivElement;
33+
private ignoreCheckboxChange = false;
3334

3435
constructor(
3536
@inject(ConstraintRegistry) private readonly constraintRegistry: ConstraintRegistry,
@@ -233,7 +234,7 @@ export class ConstraintMenu extends AbstractUIExtension implements Switchable {
233234
const btn = document.createElement("button");
234235
btn.id = "constraint-options-button";
235236
btn.title = "Filter…";
236-
btn.innerHTML = "⋮"; // or insert a font-awesome icon
237+
btn.innerHTML = '<span class="codicon codicon-kebab-vertical"></span>';
237238
btn.onclick = () => this.toggleOptionsMenu();
238239
return btn;
239240
}
@@ -257,28 +258,39 @@ export class ConstraintMenu extends AbstractUIExtension implements Switchable {
257258
const allCb = document.createElement("input");
258259
allCb.type = "checkbox";
259260
allCb.value = "ALL";
260-
// initially checked if no specific constraint is selected
261-
allCb.checked = this.constraintRegistry.getSelectedConstraints().includes("ALL");
261+
allCb.checked = this.constraintRegistry
262+
.getConstraintList()
263+
.map((c) => c.name)
264+
.every((c) => this.constraintRegistry.getSelectedConstraints().includes(c));
262265

263266
allCb.onchange = () => {
264267
if (!this.optionsMenu) return;
265-
if (allCb.checked) {
266-
// uncheck every other constraint-checkbox
267-
this.optionsMenu.querySelectorAll<HTMLInputElement>("input[type=checkbox]").forEach((cb) => {
268-
if (cb !== allCb) cb.checked = false;
269-
});
270-
// dispatch with empty array to mean “all”
271-
this.dispatcher.dispatch(ChooseConstraintAction.create(["ALL"]));
272-
} else {
273-
this.dispatcher.dispatch(ChooseConstraintAction.create([]));
268+
269+
this.ignoreCheckboxChange = true;
270+
try {
271+
if (allCb.checked) {
272+
this.optionsMenu.querySelectorAll<HTMLInputElement>("input[type=checkbox]").forEach((cb) => {
273+
if (cb !== allCb) cb.checked = true;
274+
});
275+
this.dispatcher.dispatch(
276+
ChooseConstraintAction.create(this.constraintRegistry.getConstraintList().map((c) => c.name)),
277+
);
278+
} else {
279+
this.optionsMenu.querySelectorAll<HTMLInputElement>("input[type=checkbox]").forEach((cb) => {
280+
if (cb !== allCb) cb.checked = false;
281+
});
282+
this.dispatcher.dispatch(ChooseConstraintAction.create([]));
283+
}
284+
} finally {
285+
this.ignoreCheckboxChange = false;
274286
}
275287
};
276288

277289
allConstraints.appendChild(allCb);
278290
allConstraints.appendChild(document.createTextNode("All constraints"));
279291
this.optionsMenu.appendChild(allConstraints);
280292

281-
// 2) pull your dynamic items (replace with your real API)
293+
// 2) pull your dynamic items
282294
const items = this.constraintRegistry.getConstraintList();
283295

284296
// 3) for each item build a checkbox
@@ -292,14 +304,14 @@ export class ConstraintMenu extends AbstractUIExtension implements Switchable {
292304
cb.checked = this.constraintRegistry.getSelectedConstraints().includes(cb.value);
293305

294306
cb.onchange = () => {
295-
if (cb.checked) allCb.checked = false;
307+
if (this.ignoreCheckboxChange) return;
296308

297-
const selected = Array.from(
298-
this.optionsMenu!.querySelectorAll<HTMLInputElement>("input[type=checkbox]:checked"),
299-
).map((cb) => cb.value);
309+
const checkboxes = this.optionsMenu!.querySelectorAll<HTMLInputElement>("input[type=checkbox]");
310+
const individualCheckboxes = Array.from(checkboxes).filter((cb) => cb !== allCb);
311+
const selected = individualCheckboxes.filter((cb) => cb.checked).map((cb) => cb.value);
312+
313+
allCb.checked = individualCheckboxes.every((cb) => cb.checked);
300314

301-
// dispatch your action with either an array or
302-
// a comma-joined string—whatever your action expects
303315
this.dispatcher.dispatch(ChooseConstraintAction.create(selected));
304316
};
305317

@@ -312,15 +324,16 @@ export class ConstraintMenu extends AbstractUIExtension implements Switchable {
312324

313325
// optional: click-outside handler
314326
const onClickOutside = (e: MouseEvent) => {
315-
if (
316-
this.optionsMenu &&
317-
!this.optionsMenu.contains(e.target as Node) &&
318-
!(e.target as Element).matches("#constraint-options-button")
319-
) {
320-
this.toggleOptionsMenu();
321-
document.removeEventListener("click", onClickOutside);
322-
}
327+
const target = e.target as Node;
328+
if (!this.optionsMenu || this.optionsMenu.contains(target)) return;
329+
330+
const button = document.getElementById("constraint-options-button");
331+
if (button && button.contains(target)) return;
332+
333+
this.optionsMenu.remove();
334+
this.optionsMenu = undefined;
335+
document.removeEventListener("click", onClickOutside);
323336
};
324-
setTimeout(() => document.addEventListener("click", onClickOutside), 0);
337+
document.addEventListener("click", onClickOutside);
325338
}
326339
}

src/features/constraintMenu/commands.ts

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,16 @@ export class ChooseConstraintCommand extends Command {
2020

2121
execute(context: CommandExecutionContext): CommandReturn {
2222
this.annnotationsManager.clearTfgs();
23-
const names = this.action.names;
23+
let names = this.action.names;
2424
this.constraintRegistry.setSelectedConstraints(names);
25+
26+
if (names.includes("INITIAL_CONSTRAINT_STATE")) {
27+
this.constraintRegistry.setAllConstraintsAsSelected();
28+
names = this.constraintRegistry.getSelectedConstraints();
29+
}
30+
2531
const nodes = context.root.children.filter((node) => getBasicType(node) === "node") as DfdNodeImpl[];
26-
if (names.includes("NO")) {
32+
if (names.length === 0) {
2733
nodes.forEach((node) => {
2834
node.setColor("#1D1C22");
2935
});
@@ -33,7 +39,7 @@ export class ChooseConstraintCommand extends Command {
3339
nodes.forEach((node) => {
3440
const annotations = node.annotations!;
3541
let wasAdjusted = false;
36-
if (names.includes("ALL")) {
42+
if (this.constraintRegistry.selectedContainsAllConstraints()) {
3743
annotations.forEach((annotation) => {
3844
if (annotation.message.startsWith("Constraint")) {
3945
wasAdjusted = true;
@@ -43,7 +49,7 @@ export class ChooseConstraintCommand extends Command {
4349
}
4450
names.forEach((name) => {
4551
annotations.forEach((annotation) => {
46-
if (annotation.message.startsWith("Constraint " + name)) {
52+
if (annotation.message.startsWith("Constraint ") && annotation.message.split(" ")[1] === name) {
4753
node.setColor(annotation.color!);
4854
wasAdjusted = true;
4955
this.annnotationsManager.addTfg(annotation.tfg!);
@@ -53,14 +59,12 @@ export class ChooseConstraintCommand extends Command {
5359
if (!wasAdjusted) node.setColor("#1D1C22");
5460
});
5561

56-
if (!names.includes("ALL") && names.length > 0) {
57-
nodes.forEach((node) => {
58-
const inTFG = node.annotations!.filter((annotation) =>
59-
this.annnotationsManager.getSelectedTfgs().has(annotation.tfg!),
60-
);
61-
if (inTFG.length > 0) node.setColor("#77777A", false);
62-
});
63-
}
62+
nodes.forEach((node) => {
63+
const inTFG = node.annotations!.filter((annotation) =>
64+
this.annnotationsManager.getSelectedTfgs().has(annotation.tfg!),
65+
);
66+
if (inTFG.length > 0) node.setColor("#77777A", false);
67+
});
6468

6569
return context.root;
6670
}

src/features/constraintMenu/constraintRegistry.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ export interface Constraint {
99
export class ConstraintRegistry {
1010
private constraints: Constraint[] = [];
1111
private updateCallbacks: (() => void)[] = [];
12-
private selectedConstraints: string[] = [];
12+
private selectedConstraints: string[] = this.constraints.map((c) => c.name);
1313

1414
public setConstraints(constraints: string[]): void {
1515
this.constraints = this.splitIntoConstraintTexts(constraints).map((c) => this.mapToConstraint(c));
@@ -52,6 +52,16 @@ export class ConstraintRegistry {
5252
return this.constraints;
5353
}
5454

55+
public selectedContainsAllConstraints(): boolean {
56+
return this.getConstraintList()
57+
.map((c) => c.name)
58+
.every((c) => this.getSelectedConstraints().includes(c));
59+
}
60+
61+
public setAllConstraintsAsSelected(): void {
62+
this.selectedConstraints = this.constraints.map((c) => c.name);
63+
}
64+
5565
private splitIntoConstraintTexts(text: string[]): string[] {
5666
const constraints: string[] = [];
5767
let currentConstraint = "";

src/features/dfdElements/nodeAnnotationUi.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import { DfdNodeImpl } from "./nodes";
1414

1515
import "@fortawesome/fontawesome-free/css/all.min.css";
1616
import "./nodeAnnotationUi.css";
17-
import { SettingsUI } from "../settingsMenu/settingsMenu";
17+
import { SettingsManager } from "../settingsMenu/SettingsManager";
1818
import { Mode } from "../settingsMenu/annotationManager";
1919

2020
export class DfdNodeAnnotationUIMouseListener extends MouseListener {
@@ -100,7 +100,7 @@ export class DfdNodeAnnotationUI extends AbstractUIExtension {
100100
constructor(
101101
@inject(DfdNodeAnnotationUIMouseListener)
102102
private readonly mouseListener: DfdNodeAnnotationUIMouseListener,
103-
@inject(SettingsUI) private settings: SettingsUI,
103+
@inject(SettingsManager) private settings: SettingsManager,
104104
) {
105105
super();
106106
}

src/features/dfdElements/nodes.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,8 @@ export abstract class DfdNodeImpl extends DynamicChildrenNode implements WithEdi
152152
opacity: this.opacity.toString(),
153153
};
154154

155+
style["--border"] = "#FFFFFF";
156+
155157
if (this.color) style["--color"] = this.color;
156158

157159
return style;

src/features/serialize/load.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -336,7 +336,7 @@ export async function postLoadActions(
336336
// fit to screen is done after auto layouting because that may change the bounds of the diagram
337337
// requiring another fit to screen.
338338
await actionDispatcher.dispatch(createDefaultFitToScreenAction(newRoot, false));
339-
actionDispatcher.dispatch(ChooseConstraintAction.create(["ALL"]));
339+
actionDispatcher.dispatch(ChooseConstraintAction.create(["INITIAL_CONSTRAINT_STATE"]));
340340
}
341341

342342
let initialPageTitle: string | undefined;

src/features/serialize/webSocketHandler.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { getModelFileName, logger, setModelSource, loadingIndicator } from "../../index";
22
import { SaveDFDandDD } from "./saveDFDandDD";
33

4-
//Debug
54
const webSocketAdress = `wss://websocket.dataflowanalysis.org/events/`;
65

76
let ws: WebSocket;

src/features/settingsMenu/SettingsManager.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { inject, injectable } from "inversify";
22
import { ActionDispatcher, TYPES } from "sprotty";
33
import { ChangeEdgeLabelVisibilityAction, CompleteLayoutProcessAction, SimplifyNodeNamesAction } from "./actions";
44
import { LayoutMethod } from "./LayoutMethod";
5+
import { Mode } from "./annotationManager";
56

67
@injectable()
78
export class SettingsManager {
@@ -11,6 +12,7 @@ export class SettingsManager {
1112
private _hideEdgeLabelsCheckbox?: HTMLInputElement;
1213
private _simplifyNodeNames = false;
1314
private _simplifyNodeNamesCheckbox?: HTMLInputElement;
15+
private _labelModeSelector?: HTMLSelectElement;
1416
private static readonly layoutMethodLocalStorageKey = "dfdwebeditor:settings";
1517

1618
constructor(@inject(TYPES.IActionDispatcher) protected readonly dispatcher: ActionDispatcher) {
@@ -80,4 +82,13 @@ export class SettingsManager {
8082
);
8183
});
8284
}
85+
86+
public bindLabelModeSelector(labelModeSelector: HTMLSelectElement) {
87+
this._labelModeSelector = labelModeSelector;
88+
labelModeSelector.value = Mode.INCOMING;
89+
}
90+
91+
public getCurrentLabelMode(): Mode {
92+
return this._labelModeSelector!.value as Mode;
93+
}
8394
}

0 commit comments

Comments
 (0)