Migrate to a D3-based graph renderer

This commit is contained in:
2024-02-03 20:47:22 +01:00
parent b68f84bc98
commit be1cd8b39f
14 changed files with 1035 additions and 51 deletions

View File

@@ -1,4 +1,5 @@
import mermaid from 'mermaid';
import { Graph } from './editor/d3/graph.ts';
import { GraphEditor } from './editor/GraphEditor.ts';
const IS_VALID = 'is-valid';
const IS_INVALID = 'is-invalid';
@@ -8,11 +9,6 @@ const buttons = /** @type {HTMLDivElement} */ (document.getElementById('input-bu
const clearButton = /** @type {HTMLButtonElement} */ (document.getElementById('clear-button'));
const light = /** @type {HTMLDivElement} */ (document.getElementById('light'));
mermaid.initialize({
theme: window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'default',
startOnLoad: false,
});
/**
* @param {import('./examples.js').State[]} states
*/
@@ -20,25 +16,28 @@ export async function selectAutomaton(states) {
let state = 0;
let builder = '';
// Build the mermaid graph definition
let graphDefinition = 'stateDiagram-v2\n classDef acceptingnode font-weight:bold,stroke-width:2px,stroke:yellow';
for (let i = 0; i < states.length; ++i) {
graphDefinition += `\n s${i} : ${i}`;
if (i === 0) {
graphDefinition += '\n [*] --> s0';
}
const viewer = new GraphEditor();
viewer.readonly = true;
const graph = new Graph();
for (let i = 0; i < states.length; i++) {
const node = graph.createNode(i);
if (states[i].accepting) {
graphDefinition += `\n s${i} --> [*]`;
graphDefinition += `\n class s${i} acceptingnode`;
node.accepting = true;
}
for (const [transition, destination] of Object.entries(states[i].transitions)) {
graphDefinition += `\n s${i} --> s${destination}: ${transition}`;
if (i === 0) {
node.start = true;
}
}
const graph = /** @type {HTMLDivElement} */ (document.getElementById('pen'));
const { svg } = await mermaid.render('state-graph', graphDefinition);
graph.innerHTML = svg;
const nodes = graph.querySelectorAll('.label-container');
for (let i = 0; i < states.length; i++) {
const state = states[i];
for (const [letter, target] of Object.entries(state.transitions)) {
graph.createLink(i, target, letter);
}
}
const container = /** @type {HTMLDivElement} */ (document.getElementById('state-graph'));
container.appendChild(viewer);
viewer.graph = graph;
/**
* Updates the UI to reflect the current state.
@@ -52,10 +51,11 @@ export async function selectAutomaton(states) {
light.classList.remove(IS_INVALID);
light.classList.add(IS_VALID);
}
nodes.forEach((node) => node.classList.remove('current-node'));
if (state in nodes) {
nodes[state].classList.add('current-node');
graph.forEach((node) => node.active = false);
if (state in graph.nodes) {
graph.nodes[state].active = true;
}
viewer.restart();
}
/**