Skip to content
This repository was archived by the owner on Oct 21, 2025. It is now read-only.

Commit 3311697

Browse files
author
Tom Hüller
committed
Full refactor
1 parent 2ae7b54 commit 3311697

24 files changed

Lines changed: 842 additions & 327 deletions

File tree

bundles/org.dataflowanalysis.standalone/resources/WebEditor/src/common/commonStyling.css

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,14 @@ kbd {
7070
the text would be selected otherwise due to a double click. */
7171
-webkit-user-select: none;
7272
user-select: none;
73+
74+
/* Default orientation of the arrow: pointing down */
75+
--arrow-scale: 1;
76+
}
77+
78+
.accordion-button.flip-arrow {
79+
/* Default orientation of the arrow: pointing up */
80+
--arrow-scale: -1;
7381
}
7482

7583
.accordion-button::after {
@@ -88,10 +96,10 @@ kbd {
8896

8997
vertical-align: text-top;
9098
transition: transform 500ms ease;
91-
transform: scaleY(1);
99+
transform: scaleY(var(--arrow-scale));
92100
}
93101

94102
.accordion-state:checked ~ label .accordion-button::after {
95103
/* flip arrow in y direction */
96-
transform: scaleY(-1);
104+
transform: scaleY(calc(var(--arrow-scale) * -1));
97105
}

bundles/org.dataflowanalysis.standalone/resources/WebEditor/src/common/helpUi.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ export class HelpUI extends AbstractUIExtension {
2020
containerElement.innerHTML = `
2121
<input type="checkbox" id="accordion-state-help" class="accordion-state" hidden>
2222
<label id="help-ui-accordion-label" for="accordion-state-help">
23-
<div class="accordion-button">
23+
<div class="accordion-button flip-arrow">
2424
Keyboard Shortcuts | Help
2525
</div>
2626
</label>
Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
import { inject, injectable } from "inversify";
2+
import "./constraintMenu.css";
3+
import { AbstractUIExtension } from "sprotty";
4+
import { calculateTextSize, generateRandomSprottyId } from "../../utils";
5+
import { Constraint, ConstraintRegistry } from "./constraintRegistry";
6+
import { executeAction } from "../..";
7+
import { AnalyzeDiagramAction } from "../serialize/analyze";
8+
9+
@injectable()
10+
export class ConstraintMenu extends AbstractUIExtension {
11+
static readonly ID = "constraint-menu";
12+
private selectedConstraint: Constraint | undefined;
13+
14+
constructor(@inject(ConstraintRegistry) private readonly constraintRegistry: ConstraintRegistry) {
15+
super();
16+
this.constraintRegistry = constraintRegistry;
17+
}
18+
19+
id(): string {
20+
return ConstraintMenu.ID;
21+
}
22+
containerClass(): string {
23+
return ConstraintMenu.ID;
24+
}
25+
protected initializeContents(containerElement: HTMLElement): void {
26+
containerElement.classList.add("ui-float");
27+
containerElement.innerHTML = `
28+
<input type="checkbox" id="expand-state-constraint" hidden>
29+
<label id="constraint-menu-expand-label" for="expand-state-constraint">
30+
<div class="expand-button">
31+
Constraints
32+
</div>
33+
</label>
34+
`;
35+
containerElement.appendChild(this.buildConstraintInputWrapper());
36+
containerElement.appendChild(this.buildConstraintListWrapper());
37+
containerElement.appendChild(this.buildRunButton());
38+
}
39+
40+
private buildConstraintInputWrapper(): HTMLElement {
41+
const wrapper = document.createElement("div");
42+
wrapper.id = "constraint-menu-input";
43+
wrapper.innerHTML = `
44+
<input type="text" id="constraint-input" placeholder="Enter constraint here">
45+
`;
46+
return wrapper;
47+
}
48+
49+
private buildConstraintListWrapper(): HTMLElement {
50+
const wrapper = document.createElement("div");
51+
wrapper.id = "constraint-menu-list";
52+
53+
this.rerenderConstraintList(wrapper);
54+
55+
return wrapper;
56+
}
57+
58+
private buildConstraintListItem(constraint: Constraint): HTMLElement {
59+
const valueElement = document.createElement("div");
60+
valueElement.classList.add("constrain-label");
61+
62+
valueElement.onclick = () => {
63+
const elements = document.getElementsByClassName("constraint-label");
64+
for (let i = 0; i < elements.length; i++) {
65+
elements[i].classList.remove("selected");
66+
}
67+
valueElement.classList.add("selected");
68+
this.selectConstraintListItem(constraint);
69+
};
70+
71+
const valueInput = document.createElement("input");
72+
valueInput.value = constraint.name;
73+
valueInput.placeholder = "Name";
74+
this.dynamicallySetInputSize(valueInput);
75+
valueInput.onchange = () => {
76+
constraint.name = valueInput.value;
77+
this.constraintRegistry.constraintChanged();
78+
};
79+
80+
valueElement.appendChild(valueInput);
81+
82+
const deleteButton = document.createElement("button");
83+
deleteButton.innerHTML = '<span class="codicon codicon-trash"></span>';
84+
deleteButton.onclick = () => {
85+
this.constraintRegistry.unregisterConstraint(constraint);
86+
this.rerenderConstraintList();
87+
if (this.selectedConstraint === constraint) {
88+
this.selectConstraintListItem(undefined);
89+
}
90+
};
91+
valueElement.appendChild(deleteButton);
92+
return valueElement;
93+
}
94+
95+
private selectConstraintListItem(constraint?: Constraint): void {
96+
this.selectedConstraint = constraint;
97+
const input = document.getElementById("constraint-input") as HTMLInputElement;
98+
input.value = constraint?.constraint ?? "";
99+
}
100+
101+
private rerenderConstraintList(list?: HTMLElement): void {
102+
if (!list) {
103+
list = document.getElementById("constraint-menu-list") ?? undefined;
104+
}
105+
console.info(list);
106+
if (!list) return;
107+
list.innerHTML = "";
108+
this.constraintRegistry.getConstraints().forEach((constraint) => {
109+
list.appendChild(this.buildConstraintListItem(constraint));
110+
});
111+
112+
const addButton = document.createElement("button");
113+
addButton.classList.add("constraint-add");
114+
addButton.innerHTML = '<span class="codicon codicon-add"></span> Constraint';
115+
addButton.onclick = () => {
116+
/*if (this.editorModeController?.isReadOnly()) {
117+
return;
118+
}*/
119+
var inputValue = "Test";
120+
const inputField = document.getElementById("constraint-input") as HTMLInputElement;
121+
if (inputField) {
122+
// Access the value property
123+
inputValue = inputField.value;
124+
}
125+
126+
const constraint: Constraint = {
127+
id: generateRandomSprottyId(),
128+
name: "",
129+
constraint: inputValue,
130+
};
131+
this.constraintRegistry.registerConstraint(constraint);
132+
133+
// Insert label type last but before the button
134+
const newValueElement = this.buildConstraintListItem(constraint);
135+
list.insertBefore(newValueElement, list.lastChild);
136+
this.selectConstraintListItem(constraint);
137+
138+
// Select the text input element of the new value to allow entering the value
139+
newValueElement.querySelector("input")?.focus();
140+
};
141+
list.appendChild(addButton);
142+
}
143+
144+
/**
145+
* Sets and dynamically updates the size property of the passed input element.
146+
* When the text is zero the width is set to the placeholder length to make place for it.
147+
* When the text is changed the size gets updated with the keyup event.
148+
* @param inputElement the html dom input element to set the size property for
149+
*/
150+
private dynamicallySetInputSize(inputElement: HTMLInputElement): void {
151+
const handleResize = () => {
152+
const displayText = inputElement.value || inputElement.placeholder;
153+
const { width } = calculateTextSize(displayText, window.getComputedStyle(inputElement).font);
154+
155+
// Values have higher padding for the rounded border
156+
const widthPadding = 8;
157+
const finalWidth = width + widthPadding;
158+
159+
inputElement.style.width = finalWidth + "px";
160+
};
161+
162+
inputElement.onkeyup = handleResize;
163+
164+
// The inputElement is not added to the DOM yet, so we cannot set the size now.
165+
// Wait for next JS tick, after which the element has been added to the DOM and we can set the initial size
166+
setTimeout(handleResize, 0);
167+
}
168+
169+
private buildRunButton(): HTMLElement {
170+
const wrapper = document.createElement("div");
171+
wrapper.id = "run-button-container";
172+
173+
const button = document.createElement("button");
174+
button.id = "run-button";
175+
button.innerHTML = "Run";
176+
button.onclick = () => {
177+
executeAction(AnalyzeDiagramAction.create());
178+
};
179+
180+
wrapper.appendChild(button);
181+
return wrapper;
182+
}
183+
}
Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
div.constraint-menu {
2+
right: 20px;
3+
bottom: 20px;
4+
padding: 10px 10px;
5+
display: grid;
6+
grid-template-columns: auto 1fr;
7+
grid-template-rows: 1fr;
8+
grid-auto-rows: 0;
9+
overflow: hidden;
10+
gap: 8px;
11+
}
12+
13+
div.constraint-menu:has(> input:checked) {
14+
grid-template-rows: 1fr 200px 1fr;
15+
}
16+
17+
div.constraint-menu > * {
18+
grid-column-start: 1;
19+
grid-column-end: 2;
20+
grid-row-start: 1;
21+
grid-row-end: 2;
22+
}
23+
24+
#run-button {
25+
background-color: green;
26+
color: white;
27+
border: none;
28+
border-radius: 8px;
29+
padding: 5px 10px;
30+
text-align: center;
31+
text-decoration: none;
32+
display: inline-block;
33+
width: fit-content;
34+
}
35+
36+
#run-button-container {
37+
grid-column-start: 2;
38+
grid-column-end: 3;
39+
grid-row-start: 1;
40+
grid-row-end: 2;
41+
}
42+
43+
#expand-state-constraint:checked ~ #run-button-container {
44+
grid-column-start: 2;
45+
grid-column-end: 3;
46+
grid-row-start: 3;
47+
grid-row-end: 4;
48+
}
49+
50+
#expand-state-constraint:checked ~ #run-button-container > #run-button {
51+
width: 100%;
52+
}
53+
54+
#run-button::before {
55+
content: "";
56+
background-image: url("@fortawesome/fontawesome-free/svgs/solid/play.svg");
57+
display: inline-block;
58+
filter: invert(var(--dark-mode));
59+
height: 16px;
60+
width: 16px;
61+
background-size: 16px 16px;
62+
vertical-align: text-top;
63+
}
64+
65+
#constraint-menu-input {
66+
grid-row-start: 2;
67+
grid-row-end: 4;
68+
grid-column-start: 1;
69+
grid-column-end: 2;
70+
display: none;
71+
}
72+
73+
#expand-state-constraint:checked ~ #constraint-menu-input {
74+
display: block;
75+
}
76+
77+
#constraint-menu-list {
78+
grid-row-start: 2;
79+
grid-row-end: 3;
80+
grid-column-start: 2;
81+
grid-column-end: 3;
82+
display: none;
83+
}
84+
85+
#expand-state-constraint:checked ~ #constraint-menu-list {
86+
display: block;
87+
}
88+
89+
#constraint-menu-expand-label {
90+
padding-right: 2em;
91+
position: relative;
92+
display: flex;
93+
grid-column-start: 1;
94+
grid-column-end: 2;
95+
align-items: center;
96+
}
97+
98+
#expand-state-constraint:checked ~ #constraint-menu-expand-label {
99+
grid-column-end: 3;
100+
}
101+
102+
#constraint-menu-expand-label::after {
103+
content: "";
104+
background-image: url("@fortawesome/fontawesome-free/svgs/solid/chevron-up.svg");
105+
right: 0.5em;
106+
position: absolute;
107+
display: inline-block;
108+
109+
/* only filter=invert(1) if dark mode is enabled aka --dark-mode is set to 1 */
110+
filter: invert(var(--dark-mode));
111+
112+
width: 16px;
113+
height: 16px;
114+
background-size: 16px 16px;
115+
116+
transition: transform 500ms ease;
117+
transform: scaleY(1);
118+
}
119+
120+
#expand-state-constraint:checked ~ #constraint-menu-expand-label::after {
121+
transform: scaleY(-1);
122+
}
123+
124+
.constrain-label input {
125+
background-color: var(--color-background);
126+
text-align: center;
127+
border: 1px solid var(--color-foreground);
128+
border-radius: 15px;
129+
padding: 3px;
130+
margin: 4px;
131+
}
132+
133+
.constrain-label.selected input {
134+
border: 1px solid var(--color-foreground);
135+
}
136+
137+
.constrain-label button {
138+
background-color: transparent;
139+
border: none;
140+
cursor: pointer;
141+
padding: 0;
142+
}
143+
144+
.constraint-add {
145+
padding: 0;
146+
border: none;
147+
background-color: transparent;
148+
cursor: pointer;
149+
display: flex;
150+
align-items: center;
151+
gap: 5px;
152+
}

0 commit comments

Comments
 (0)