Able to add new vertices
This commit is contained in:
parent
07370be4fa
commit
a743a81218
|
@ -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() {
|
||||||
|
|
|
@ -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',
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -117,7 +117,7 @@ export class Edge {
|
||||||
.button({
|
.button({
|
||||||
id: 'cancel',
|
id: 'cancel',
|
||||||
name: 'Cancel',
|
name: 'Cancel',
|
||||||
cb: () => doc.clear(),
|
cb: () => graph.resetEditorDocument(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,8 +78,10 @@ export class Vertex {
|
||||||
doc.remark('<br>', { parentEl: subForm.el });
|
doc.remark('<br>', { parentEl: subForm.el });
|
||||||
};
|
};
|
||||||
|
|
||||||
for (const [key, value] of vertex.properties.entries()) {
|
if (vertex) {
|
||||||
addPropertyForm(key, value);
|
for (const [key, value] of vertex.properties.entries()) {
|
||||||
|
addPropertyForm(key, value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
form.button({
|
form.button({
|
||||||
|
@ -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]));
|
||||||
Object.assign(vertex, formValue);
|
if (vertex) {
|
||||||
vertex.displayVertex();
|
Object.assign(vertex, formValue);
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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') {
|
||||||
|
|
|
@ -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();
|
||||||
// });
|
// });
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue