Skip to content

Commit 4e13533

Browse files
committed
Fix: Menu Icon, Menu unresponsive bug, checkbox behavior, constraint text, bind labelModeSelector, remove debug comment
1 parent 6432b8f commit 4e13533

File tree

8 files changed

+87
-51
lines changed

8 files changed

+87
-51
lines changed

src/features/constraintMenu/ConstraintMenu.ts

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

3436
constructor(
3537
@inject(ConstraintRegistry) private readonly constraintRegistry: ConstraintRegistry,
@@ -233,7 +235,7 @@ export class ConstraintMenu extends AbstractUIExtension implements Switchable {
233235
const btn = document.createElement("button");
234236
btn.id = "constraint-options-button";
235237
btn.title = "Filter…";
236-
btn.innerHTML = "⋮"; // or insert a font-awesome icon
238+
btn.innerHTML = '<span class="codicon codicon-kebab-vertical"></span>';
237239
btn.onclick = () => this.toggleOptionsMenu();
238240
return btn;
239241
}
@@ -257,28 +259,39 @@ export class ConstraintMenu extends AbstractUIExtension implements Switchable {
257259
const allCb = document.createElement("input");
258260
allCb.type = "checkbox";
259261
allCb.value = "ALL";
260-
// initially checked if no specific constraint is selected
261-
allCb.checked = this.constraintRegistry.getSelectedConstraints().includes("ALL");
262+
allCb.checked = this.constraintRegistry
263+
.getConstraintList()
264+
.map((c) => c.name)
265+
.every((c) => this.constraintRegistry.getSelectedConstraints().includes(c));
262266

263267
allCb.onchange = () => {
264268
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([]));
269+
270+
this.ignoreCheckboxChange = true;
271+
try {
272+
if (allCb.checked) {
273+
this.optionsMenu.querySelectorAll<HTMLInputElement>("input[type=checkbox]").forEach((cb) => {
274+
if (cb !== allCb) cb.checked = true;
275+
});
276+
this.dispatcher.dispatch(
277+
ChooseConstraintAction.create(this.constraintRegistry.getConstraintList().map((c) => c.name)),
278+
);
279+
} else {
280+
this.optionsMenu.querySelectorAll<HTMLInputElement>("input[type=checkbox]").forEach((cb) => {
281+
if (cb !== allCb) cb.checked = false;
282+
});
283+
this.dispatcher.dispatch(ChooseConstraintAction.create([]));
284+
}
285+
} finally {
286+
this.ignoreCheckboxChange = false;
274287
}
275288
};
276289

277290
allConstraints.appendChild(allCb);
278291
allConstraints.appendChild(document.createTextNode("All constraints"));
279292
this.optionsMenu.appendChild(allConstraints);
280293

281-
// 2) pull your dynamic items (replace with your real API)
294+
// 2) pull your dynamic items
282295
const items = this.constraintRegistry.getConstraintList();
283296

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

294307
cb.onchange = () => {
295-
if (cb.checked) allCb.checked = false;
308+
if (this.ignoreCheckboxChange) return;
296309

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

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

@@ -312,15 +325,16 @@ export class ConstraintMenu extends AbstractUIExtension implements Switchable {
312325

313326
// optional: click-outside handler
314327
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-
}
328+
const target = e.target as Node;
329+
if (!this.optionsMenu || this.optionsMenu.contains(target)) return;
330+
331+
const button = document.getElementById("constraint-options-button");
332+
if (button && button.contains(target)) return;
333+
334+
this.optionsMenu.remove();
335+
this.optionsMenu = undefined;
336+
document.removeEventListener("click", onClickOutside);
323337
};
324-
setTimeout(() => document.addEventListener("click", onClickOutside), 0);
338+
document.addEventListener("click", onClickOutside);
325339
}
326340
}

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
/*

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

src/features/settingsMenu/settingsMenu.ts

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -117,12 +117,7 @@ export class SettingsUI extends AbstractUIExtension {
117117
this.dispatcher.dispatch(ChangeEditorModeAction.create(readOnlyCheckbox.checked ? "view" : "edit"));
118118
});
119119

120-
const modeSelect = containerElement.querySelector("#setting-mode-option") as HTMLSelectElement;
121-
modeSelect.value = Mode.INCOMING;
122-
}
123-
124-
public getCurrentLabelMode(): Mode {
125-
const modeSelect = document.getElementById("setting-mode-option") as HTMLSelectElement;
126-
return modeSelect.value as Mode;
120+
const labelModeSelector = containerElement.querySelector("#setting-mode-option") as HTMLSelectElement;
121+
this.settings.bindLabelModeSelector(labelModeSelector);
127122
}
128123
}

0 commit comments

Comments
 (0)