Able to add new vertices

This commit is contained in:
Ladd Hoffman 2023-07-09 23:00:57 -05:00
parent 07370be4fa
commit a743a81218
7 changed files with 55 additions and 38 deletions

View File

@ -1,10 +1,11 @@
import { MermaidDiagram } from './mermaid.js'; import { MermaidDiagram } from './mermaid.js';
export class Flowchart extends MermaidDiagram { export class Flowchart extends MermaidDiagram {
constructor(box, logBox, direction = 'BT') { constructor(box, logBox, { direction = 'BT' } = {}) {
super(box, logBox); super(box, logBox);
this.direction = direction; this.direction = direction;
this.init(); this.init();
this.controlBox = this.box.addBox('Controls');
} }
init() { init() {

View File

@ -9,7 +9,6 @@ export class MermaidDiagram {
this.renderBox = this.box.addBox('Render'); this.renderBox = this.box.addBox('Render');
this.box.addBox('Spacer').setInnerHTML(' '); this.box.addBox('Spacer').setInnerHTML(' ');
this.logBoxPre = logBox.el.appendChild(document.createElement('pre')); this.logBoxPre = logBox.el.appendChild(document.createElement('pre'));
this.inSection = 0;
} }
static initializeAPI() { static initializeAPI() {
@ -20,10 +19,8 @@ export class MermaidDiagram {
darkMode: true, darkMode: true,
primaryColor: '#2a5b6c', primaryColor: '#2a5b6c',
primaryTextColor: '#b6b6b6', primaryTextColor: '#b6b6b6',
// lineColor: '#349cbd',
lineColor: '#57747d', lineColor: '#57747d',
signalColor: '#57747d', signalColor: '#57747d',
// signalColor: '#349cbd',
noteBkgColor: '#516f77', noteBkgColor: '#516f77',
noteTextColor: '#cecece', noteTextColor: '#cecece',
activationBkgColor: '#1d3f49', activationBkgColor: '#1d3f49',

View File

@ -47,26 +47,27 @@ export class Scene {
} }
withFlowchart({ direction = 'BT' } = {}) { withFlowchart({ direction = 'BT' } = {}) {
const box = this.topSection.addBox('Flowchart').addClass('padded'); this.withSectionFlowchart({ direction, section: this.topSection });
this.box.addBox('Spacer').setInnerHTML(' '); this.flowchart = this.lastFlowchart;
const logBox = this.box.addBox('Flowchart text').addClass('dim');
this.flowchart = new Flowchart(box, logBox, direction);
return this; return this;
} }
withAdditionalFlowchart({ id, name, direction = 'BT' } = {}) { withSectionFlowchart({
id, name, direction = 'BT', section,
} = {}) {
section = section ?? this.middleSection;
const index = this.flowcharts.size; const index = this.flowcharts.size;
name = name ?? `Flowchart ${index}`; name = name ?? `Flowchart ${index}`;
id = id ?? `flowchart_${CryptoUtil.randomUUID().slice(0, 4)}`; id = id ?? `flowchart_${CryptoUtil.randomUUID().slice(0, 4)}`;
const container = this.middleSection.addBox(name).flex(); const container = section.addBox(name).flex();
const box = container.addBox('Flowchart').addClass('padded'); const box = container.addBox('Flowchart').addClass('padded');
const logBox = container.addBox('Flowchart text').addClass('dim'); const logBox = container.addBox('Flowchart text').addClass('dim');
const flowchart = new Flowchart(box, logBox, direction); const flowchart = new Flowchart(box, logBox, { direction });
this.flowcharts.set(id, flowchart); this.flowcharts.set(id, flowchart);
return this; return this;
} }
lastFlowchart() { get lastFlowchart() {
if (!this.flowcharts.size) { if (!this.flowcharts.size) {
if (this.flowchart) { if (this.flowchart) {
return this.flowchart; return this.flowchart;

View File

@ -117,7 +117,7 @@ export class Edge {
.button({ .button({
id: 'cancel', id: 'cancel',
name: 'Cancel', name: 'Cancel',
cb: () => doc.clear(), cb: () => graph.resetEditorDocument(),
}); });
} }
} }

View File

@ -54,18 +54,15 @@ export class Vertex {
static prepareEditorDocument(graph, doc, vertexId) { static prepareEditorDocument(graph, doc, vertexId) {
doc.clear(); doc.clear();
const vertex = graph.getVertex(vertexId); const vertex = vertexId ? graph.getVertex(vertexId) : undefined;
if (!vertex) {
throw new Error(`Could not find WDG Vertex ${vertexId}`);
}
const form = doc.form().lastElement; const form = doc.form().lastElement;
doc.remark('<h3>Edit Vertex</h3>', { parentEl: form.el }); doc.remark(`<h3>${vertex ? 'Edit' : 'Add'} Vertex</h3>`, { parentEl: form.el });
form form
.textField({ .textField({
id: 'id', name: 'id', defaultValue: vertex.id, id: 'id', name: 'id', defaultValue: vertex?.id,
}) })
.textField({ id: 'type', name: 'type', defaultValue: vertex.type }) .textField({ id: 'type', name: 'type', defaultValue: vertex?.type })
.textField({ id: 'label', name: 'label', defaultValue: vertex.label }); .textField({ id: 'label', name: 'label', defaultValue: vertex?.label });
doc.remark('<h4>Properties</h4>', { parentEl: form.el }); doc.remark('<h4>Properties</h4>', { parentEl: form.el });
const subFormArray = form.subFormArray({ id: 'properties', name: 'properties' }).lastItem; const subFormArray = form.subFormArray({ id: 'properties', name: 'properties' }).lastItem;
@ -81,9 +78,11 @@ export class Vertex {
doc.remark('<br>', { parentEl: subForm.el }); doc.remark('<br>', { parentEl: subForm.el });
}; };
if (vertex) {
for (const [key, value] of vertex.properties.entries()) { for (const [key, value] of vertex.properties.entries()) {
addPropertyForm(key, value); addPropertyForm(key, value);
} }
}
form.button({ form.button({
id: 'add', id: 'add',
@ -92,26 +91,34 @@ export class Vertex {
}) })
.button({ .button({
id: 'save', id: 'save',
name: 'Save', name: this.id ? 'Save' : 'Add',
type: 'submit', type: 'submit',
cb: ({ form: { value: formValue } }) => { cb: ({ form: { value: formValue } }) => {
let fullRedraw = false; let fullRedraw = false;
if (formValue.id !== vertex.id) { if (vertex && formValue.id !== vertex.id) {
fullRedraw = true; fullRedraw = true;
} }
// TODO: preserve data types of properties // TODO: preserve data types of properties
formValue.properties = new Map(formValue.properties.map(({ key, value }) => [key, value])); formValue.properties = new Map(formValue.properties.map(({ key, value }) => [key, value]));
if (vertex) {
Object.assign(vertex, formValue); Object.assign(vertex, formValue);
vertex.displayVertex(); vertex.displayVertex();
} else {
graph.addVertex(formValue.type, formValue.id, null, formValue.label);
}
if (fullRedraw) { if (fullRedraw) {
graph.redraw(); graph.redraw();
} }
if (!vertex) {
// This was a new vertex. Now let's edit.
Vertex.prepareEditorDocument(graph, doc, formValue.id);
}
}, },
}) })
.button({ .button({
id: 'cancel', id: 'cancel',
name: 'Cancel', name: 'Cancel',
cb: () => doc.clear(), cb: () => graph.resetEditorDocument(),
}); });
return doc; return doc;
} }

View File

@ -1,13 +1,13 @@
import { Vertex } from './vertex.js'; import { Vertex } from './vertex.js';
import { Edge } from './edge.js'; import { Edge } from './edge.js';
import { Document } from '../display/document.js';
const graphs = []; const allGraphs = [];
const makeWDGHandler = (graphIndex) => (vertexId) => { const makeWDGHandler = (graphIndex) => (vertexId) => {
const graph = graphs[graphIndex]; const graph = allGraphs[graphIndex];
// We want a document for editing this node, which may be a vertex or an edge // We want a document for editing this node, which may be a vertex or an edge
const editorDoc = graph.scene.getDocument('editorDocument') const { editorDoc } = graph;
?? graph.scene.withDocument('editorDocument').lastDocument;
if (vertexId.startsWith('edge:')) { if (vertexId.startsWith('edge:')) {
const [, from, to] = vertexId.split(':'); const [, from, to] = vertexId.split(':');
Edge.prepareEditorDocument(graph, editorDoc, from, to); Edge.prepareEditorDocument(graph, editorDoc, from, to);
@ -22,14 +22,17 @@ export class WeightedDirectedGraph {
this.vertices = new Map(); this.vertices = new Map();
this.edgeTypes = new Map(); this.edgeTypes = new Map();
this.nextVertexId = 0; this.nextVertexId = 0;
this.flowchart = scene?.flowchart; this.flowchart = undefined;
this.editable = options.editable; this.editable = options.editable;
this.index = graphs.length;
graphs.push(this);
// Mermaid supports a click callback, but we can't customize arguments; we just get the vertex ID. // Mermaid supports a click callback, but we can't customize arguments; we just get the vertex ID.
// In order to provide the appropriate graph context for each callback, we create a separate callback // In order to provide the appropriate graph context for each callback, we create a separate callback
// function for each graph. // function for each graph.
this.index = allGraphs.length;
allGraphs.push(this);
window[`WDGHandler${this.index}`] = makeWDGHandler(this.index); window[`WDGHandler${this.index}`] = makeWDGHandler(this.index);
// TODO: Populate history
this.history = {}; this.history = {};
} }
@ -88,11 +91,19 @@ export class WeightedDirectedGraph {
} }
withFlowchart() { withFlowchart() {
this.scene?.withAdditionalFlowchart(); this.scene?.withSectionFlowchart();
this.flowchart = this.scene?.lastFlowchart(); this.flowchart = this.scene?.lastFlowchart;
if (this.editable) {
this.editorDoc = new Document('Controls', this.flowchart.controlBox.el);
this.resetEditorDocument();
}
return this; return this;
} }
resetEditorDocument() {
Vertex.prepareEditorDocument(this, this.editorDoc);
}
addVertex(type, id, data, label, options) { addVertex(type, id, data, label, options) {
// Supports simple case of auto-incremented numeric ids // Supports simple case of auto-incremented numeric ids
if (typeof id === 'object') { if (typeof id === 'object') {

View File

@ -37,7 +37,7 @@ describe('Document > Form > TextField', () => {
doc.remark('Hmm...!'); doc.remark('Hmm...!');
}); });
// it('can exist within a graph', () => { // it('can exist within a graph', () => {
// scene.withAdditionalFlowchart({ id: 'flowchart', name: 'Graph' }); // scene.withSectionFlowchart({ id: 'flowchart', name: 'Graph' });
// const graph = scene.lastFlowchart(); // const graph = scene.lastFlowchart();
// }); // });
}); });