Skip to content

Commit 9b21cd4

Browse files
committed
feat: update modals and handle errors
1 parent 1774b6c commit 9b21cd4

File tree

6 files changed

+206
-59
lines changed

6 files changed

+206
-59
lines changed

index.html

Lines changed: 57 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -77,13 +77,24 @@ <h1 class="select-none">Automata Simulation</h1>
7777
>
7878
qf
7979
</div>
80+
81+
<div
82+
class="bg-white cursor-pointer select-none rounded-full border-2 w-8 h-8 text-center leading-6 text-xs"
83+
title="Clear all"
84+
id="btn-clear-all"
85+
>
86+
<i class="fa-solid fa-trash-can text-[10px]"></i>
87+
</div>
8088
</div>
8189

8290
<div class="flex gap-4">
8391
<a
8492
target="_blank"
8593
href="https://github.com/byandrev/automata-simulation"
86-
><i class="fa-brands fa-github"></i
94+
class="bg-gray-600 shadow text-white flex items-center gap-2 rounded px-2 py-1"
95+
>
96+
<span class="text-xs block">Open Source</span>
97+
<i class="fa-brands fa-github"></i
8798
></a>
8899
</div>
89100
</div>
@@ -102,8 +113,9 @@ <h1 class="select-none">Automata Simulation</h1>
102113

103114
<button
104115
id="run"
105-
class="rounded px-4 h-8 text-xs bg-blue-500 text-white hover:bg-blue-600 active:bg-blue-500"
116+
class="rounded flex items-center gap-2 shadow px-4 h-8 text-xs bg-blue-500 text-white hover:bg-blue-600 active:bg-blue-500"
106117
>
118+
<i class="fa-solid fa-play"></i>
107119
Run
108120
</button>
109121
</div>
@@ -112,6 +124,8 @@ <h1 class="select-none">Automata Simulation</h1>
112124
<p><span id="out"></span></p>
113125
<p class="mt-2 text-xl" id="string-out"></p>
114126
</div>
127+
128+
<div class="mt-8" id="error"></div>
115129
</div>
116130
</div>
117131

@@ -126,38 +140,51 @@ <h2 class="text-xl">How work?</h2>
126140
>
127141
<div class="modal__overlay" tabindex="-1" data-micromodal-close>
128142
<div
129-
class="modal__container"
130143
role="dialog"
131144
aria-modal="true"
132145
aria-labelledby="modal-1-title"
146+
class="modal__container relative pt-4"
133147
>
134-
<header class="modal__header">
135-
<h2 class="modal__title" id="modal-1-title">Transition</h2>
136-
<button
137-
class="modal__close"
138-
aria-label="Close modal"
139-
data-micromodal-close
140-
></button>
141-
</header>
142-
<main class="modal__content" id="modal-1-content">
143-
<input
144-
type="text"
145-
id="input-label-name"
146-
class="p-2 rounded border-2"
147-
/>
148-
</main>
149-
<footer class="modal__footer">
150-
<button id="btn-label-name" class="modal__btn modal__btn-primary">
151-
Save
152-
</button>
153-
<button
154-
class="modal__btn"
155-
data-micromodal-close
156-
aria-label="Close this dialog window"
157-
>
158-
Close
159-
</button>
160-
</footer>
148+
<span
149+
class="absolute top-[2px] left-0 text-xs bg-yellow-300 px-2 rounded-t"
150+
>Label name</span
151+
>
152+
<input
153+
type="text"
154+
id="input-label-name"
155+
class="p-2 rounded border-2"
156+
/>
157+
<!-- <button id="btn-label-name" class="modal__btn modal__btn-primary">
158+
Save
159+
</button> -->
160+
</div>
161+
</div>
162+
</div>
163+
164+
<div
165+
class="modal micromodal-slide"
166+
id="modal-state-name"
167+
aria-hidden="true"
168+
>
169+
<div class="modal__overlay" tabindex="-1" data-micromodal-close>
170+
<div
171+
role="dialog"
172+
aria-modal="true"
173+
aria-labelledby="modal-1-title"
174+
class="modal__container relative pt-4"
175+
>
176+
<span
177+
class="absolute top-[2px] left-0 text-xs bg-yellow-300 px-2 rounded-t"
178+
>State name</span
179+
>
180+
<input
181+
type="text"
182+
id="input-state-name"
183+
class="p-2 rounded border-2"
184+
/>
185+
<!-- <button id="btn-state-name" class="modal__btn modal__btn-primary">
186+
Save
187+
</button> -->
161188
</div>
162189
</div>
163190
</div>

src/afd.js

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { animateNode, renderOut } from "./animateNode.js";
1+
import { animateNode, renderError, renderOut } from "./animateNode.js";
22

33
function verifyAFD(paper, graph, automata, string) {
44
let i = 0;
@@ -15,6 +15,7 @@ function verifyAFD(paper, graph, automata, string) {
1515
}
1616

1717
renderOut("INVALID");
18+
renderError("I do not end in a final state")
1819
return;
1920
}
2021

@@ -34,7 +35,13 @@ function verifyAFD(paper, graph, automata, string) {
3435
return;
3536
}
3637

37-
animateNode(paper, graph, state, symbol);
38+
animateNode(
39+
paper,
40+
graph,
41+
state,
42+
symbol,
43+
automata.finalStates.includes(state)
44+
);
3845

3946
i++;
4047
state = isState.nextState;

src/animateNode.js

Lines changed: 50 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,34 @@
1-
function animateNode(paper, graph, state = "q0", symbol, color = "orange") {
1+
import { FILL_NODE_FINAL } from "./shapes";
2+
3+
function animateNode(
4+
paper,
5+
graph,
6+
state = "q0",
7+
symbol,
8+
isFinal,
9+
color = "#008080"
10+
) {
211
const data = paper.model
312
.getElements()
413
.find((el) => el.attributes.attrs.label.text === state);
14+
515
data.attr("body/stroke", color);
16+
data.attr("body/fill", "#21bcbc");
617

718
const links = graph.getConnectedLinks(data, { outbound: true });
819

920
const currentLink = links.find((el) =>
1021
el.attributes.labels[0].attrs.text.text.split(",").includes(symbol)
1122
);
1223

13-
if (currentLink) currentLink.attr("line/stroke", "#ff0000");
24+
if (currentLink) currentLink.attr("line/stroke", "orange");
1425

1526
setTimeout(() => {
1627
data.attr("body/stroke", "black");
28+
29+
if (!isFinal) data.attr("body/fill", "#ffffff");
30+
else data.attr("body/fill", FILL_NODE_FINAL);
31+
1732
if (currentLink) currentLink.attr("line/stroke", "black");
1833

1934
document
@@ -24,7 +39,24 @@ function animateNode(paper, graph, state = "q0", symbol, color = "orange") {
2439
}
2540

2641
function renderOut(value) {
27-
document.querySelector("#out").textContent = `${value}`;
42+
document.querySelector("#out").innerHTML = `
43+
<div class="animate__fadeIn">
44+
${
45+
value === "VALID"
46+
? `<i class="fa-solid fa-circle-check text-sm text-green-500"></i>`
47+
: value === "INVALID"
48+
? `<i class="fa-solid fa-circle-exclamation text-sm text-red-500"></i>`
49+
: ""
50+
}
51+
<span class="${
52+
value === "VALID"
53+
? "text-green-400"
54+
: value === "INVALID"
55+
? "text-red-400"
56+
: ""
57+
}">${value}</span>
58+
</div>
59+
`;
2860
}
2961

3062
function renderOutString(value) {
@@ -34,4 +66,18 @@ function renderOutString(value) {
3466
.join("")}`;
3567
}
3668

37-
export { animateNode, renderOut, renderOutString };
69+
function renderError(error) {
70+
const errorEl = document.querySelector("#error");
71+
72+
if (!error) {
73+
errorEl.innerHTML = "";
74+
return;
75+
}
76+
77+
errorEl.innerHTML = `<p class="animate__fadeIn">
78+
<span class="font-bold block text-red-500">Error:</span>
79+
${error}
80+
</p>`;
81+
}
82+
83+
export { animateNode, renderOut, renderOutString, renderError };

src/graph.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,6 @@ function initGraph() {
9494
const inputEl = document.querySelector("#input-label-name");
9595
MicroModal.show("modal-label-name");
9696
inputEl.setAttribute("link-id", this.model.id);
97-
// inputEl.value = this.model.attributes.labels[0].attrs.text.text;
9897
},
9998
}),
10099
});
@@ -118,8 +117,12 @@ function initGraph() {
118117

119118
paper.on("element:pointerdblclick", function (elementView, evt) {
120119
const currentElement = elementView.model;
121-
const label = prompt("Label name?");
122-
currentElement.attr("label/text", label);
120+
// const label = prompt("Label name?");
121+
// currentElement.attr("label/text", label);
122+
console.log(currentElement);
123+
const inputEl = document.querySelector("#input-state-name");
124+
MicroModal.show("modal-state-name");
125+
inputEl.setAttribute("state-id", currentElement.id);
123126
});
124127

125128
return { graph, paper };

src/main.js

Lines changed: 64 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import Split from "split.js";
22
import { verifyAFD } from "./afd.js";
3-
import { renderOut, renderOutString } from "./animateNode.js";
4-
import { createAutomata } from "./automata.js";
3+
import { renderError, renderOut, renderOutString } from "./animateNode.js";
4+
import { createAutomata, clearAutomata } from "./automata.js";
55
import { startDragTools } from "./dragTools.js";
66
import { CANVAS_HEIGHT, initGraph } from "./graph.js";
77
import { CircleShape, FILL_NODE_FINAL, NODE_WIDTH } from "./shapes.js";
@@ -10,6 +10,11 @@ import MicroModal from "micromodal"; // es6 module
1010
const { graph, paper } = initGraph();
1111
const inputString = document.querySelector("#input-string");
1212
const inputEl = document.querySelector("#input-label-name");
13+
const inputLabel = document.querySelector("#input-label-name");
14+
const inputState = document.querySelector("#input-state-name");
15+
const btnClearAll = document.querySelector("#btn-clear-all");
16+
17+
const automata = createAutomata();
1318

1419
function run() {
1520
const data = graph.toJSON();
@@ -21,7 +26,8 @@ function run() {
2126
const statesArr = [];
2227
const transitions = [];
2328

24-
const automata = createAutomata();
29+
// clear errors
30+
renderError(null);
2531

2632
elements.forEach((el) => {
2733
if (el.attributes.type === "Circle") {
@@ -51,11 +57,14 @@ function run() {
5157
Object.values(states).forEach((state) => statesArr.push(state.text));
5258

5359
if (statesArr.length <= 0) {
54-
alert("No states");
60+
renderError("No states");
5561
return;
5662
}
5763

58-
console.log(transitions);
64+
if (!statesArr.includes("q0")) {
65+
renderError("Initial state not found: q0");
66+
return;
67+
}
5968

6069
automata.alphabet = alphabet;
6170
automata.initialState = "q0";
@@ -70,6 +79,34 @@ function run() {
7079
verifyAFD(paper, graph, automata, string);
7180
}
7281

82+
function changeLabelName() {
83+
const id = inputLabel.getAttribute("link-id");
84+
const currentLink = graph.getLinks().find((link) => link.id === id);
85+
86+
currentLink.label(0, {
87+
attrs: {
88+
text: {
89+
text: inputLabel.value || "λ",
90+
},
91+
},
92+
});
93+
94+
inputLabel.value = "";
95+
MicroModal.close("modal-label-name");
96+
}
97+
98+
function changeStateName() {
99+
const id = inputState.getAttribute("state-id");
100+
const data = paper.model.getElements().find((el) => {
101+
return el.id === id;
102+
});
103+
104+
if (data) data.attr("label/text", inputState.value);
105+
106+
inputState.value = "";
107+
MicroModal.close("modal-state-name");
108+
}
109+
73110
window.addEventListener("DOMContentLoaded", () => {
74111
MicroModal.init();
75112
Split(["#paper", "#split-1"], { sizes: [80, 20], minSize: 300 });
@@ -91,19 +128,28 @@ window.addEventListener("resize", () => {
91128
paper.setDimensions(document.body.clientWidth);
92129
});
93130

94-
document.querySelector("#btn-label-name").addEventListener("click", () => {
95-
const inputLabel = document.querySelector("#input-label-name");
96-
const id = inputLabel.getAttribute("link-id");
97-
const currentLink = graph.getLinks().find((link) => link.id === id);
131+
// Button change label name
132+
// document.querySelector("#btn-label-name").addEventListener("click", () => {});
98133

99-
currentLink.label(0, {
100-
attrs: {
101-
text: {
102-
text: inputLabel.value || "λ",
103-
},
104-
},
105-
});
134+
inputLabel.addEventListener("keydown", (e) => {
135+
if (e.code === "Enter") {
136+
changeLabelName();
137+
}
138+
});
106139

107-
inputLabel.value = "";
108-
MicroModal.close("modal-label-name");
140+
// Button change state name
141+
// document
142+
// .querySelector("#btn-state-name")
143+
// .addEventListener("click", changeStateName);
144+
145+
inputState.addEventListener("keydown", (e) => {
146+
if (e.code === "Enter") {
147+
changeStateName();
148+
}
149+
});
150+
151+
// Clear all
152+
btnClearAll.addEventListener("click", () => {
153+
clearAutomata(automata);
154+
graph.clear();
109155
});

0 commit comments

Comments
 (0)