From f978c2010497e3e59ac5784424c3102dc687872e Mon Sep 17 00:00:00 2001 From: Ladd Hoffman Date: Sun, 9 Jul 2023 14:25:34 -0500 Subject: [PATCH 01/10] Fixup, use string for INCINERATOR_ADDRESS --- forum-network/src/classes/dao/forum.js | 6 ++--- .../src/classes/supporting/vertex.js | 25 ++++++------------- forum-network/src/classes/supporting/wdg.js | 2 +- forum-network/src/util/constants.js | 2 +- 4 files changed, 13 insertions(+), 22 deletions(-) diff --git a/forum-network/src/classes/dao/forum.js b/forum-network/src/classes/dao/forum.js index e65dee2..f06c7b3 100644 --- a/forum-network/src/classes/dao/forum.js +++ b/forum-network/src/classes/dao/forum.js @@ -313,9 +313,6 @@ export class Forum extends ReputationHolder { this.actions.propagate.log(post, author, `(${authorIncrement})`); } - // Increment the value of the post - await post.setValue(newValue); - console.log('propagateValue end', { depth, increment, @@ -325,6 +322,9 @@ export class Forum extends ReputationHolder { refundToInbound, }); + // Increment the value of the post + await post.setValue(newValue); + return refundToInbound; } } diff --git a/forum-network/src/classes/supporting/vertex.js b/forum-network/src/classes/supporting/vertex.js index 160126c..b1ae83b 100644 --- a/forum-network/src/classes/supporting/vertex.js +++ b/forum-network/src/classes/supporting/vertex.js @@ -2,7 +2,7 @@ export class Vertex { constructor(graph, type, id, data, options = {}) { this.graph = graph; this.type = type; - this._id = id; + this.id = id; this.data = data; this.options = options; this.edges = { @@ -16,14 +16,6 @@ export class Vertex { this.installedClickCallback = false; } - set id(newId) { - this._id = newId; - } - - get id() { - return this._id; - } - getEdges(type, away) { return this.edges[away ? 'from' : 'to'].filter( (edge) => edge.type === type, @@ -66,18 +58,17 @@ export class Vertex { name: 'Save', cb: ({ form: { value } }, { initializing }) => { if (initializing) return; + let fullRedraw = false; if (value.id && value.id !== vertex.id) { - // TODO: When an ID changes we really need to wipe out and redraw! - // But we don't yet have a systematic approach for doing that. - // Everything is getting rendered as needed. Lacking abstraction. - // HMM we're not actually that far! Just wipe everything out and draw each vertex and edge :) - // for (const vertex of ) - // for (const edge of [...vertex.edges.to, ...vertex.edges.from]) { - // edge.displayEdge(); - // } + fullRedraw = true; } Object.assign(vertex, value); vertex.displayVertex(); + if (fullRedraw) { + graph.redraw(); + } + }, + }) }, }); diff --git a/forum-network/src/classes/supporting/wdg.js b/forum-network/src/classes/supporting/wdg.js index 41f2ad6..45b333e 100644 --- a/forum-network/src/classes/supporting/wdg.js +++ b/forum-network/src/classes/supporting/wdg.js @@ -83,7 +83,7 @@ export class WeightedDirectedGraph { } } - // Rerender + // Ensure rerender this.flowchart?.render(); } diff --git a/forum-network/src/util/constants.js b/forum-network/src/util/constants.js index 65b28c9..fbfdb79 100644 --- a/forum-network/src/util/constants.js +++ b/forum-network/src/util/constants.js @@ -1,6 +1,6 @@ export const EPSILON = 2.23e-16; -export const INCINERATOR_ADDRESS = 0; +export const INCINERATOR_ADDRESS = '0'; export const EdgeTypes = { CITATION: 'citation', From 56132b2fec4032ff1f008e2cd57b388abb79146c Mon Sep 17 00:00:00 2001 From: Ladd Hoffman Date: Sun, 9 Jul 2023 16:33:11 -0500 Subject: [PATCH 02/10] Add cancel buttons --- forum-network/src/classes/supporting/edge.js | 45 +++++++++++-------- .../src/classes/supporting/vertex.js | 10 +++-- 2 files changed, 33 insertions(+), 22 deletions(-) diff --git a/forum-network/src/classes/supporting/edge.js b/forum-network/src/classes/supporting/edge.js index bc15b81..ddbf71d 100644 --- a/forum-network/src/classes/supporting/edge.js +++ b/forum-network/src/classes/supporting/edge.js @@ -99,25 +99,32 @@ export class Edge { if (initializing) return; addEdgeForm(new Edge(graph, null, graph.getVertex(from), graph.getVertex(to))); }, - }); - - form.button({ - id: 'save', - name: 'Save', - cb: ({ form: { value } }, { initializing }) => { - if (initializing) return; - // Handle additions and updates - for (const { type, weight } of value.edges) { - graph.setEdgeWeight(type, from, to, weight); - } - // Handle removals - for (const edge of graph.getEdges(null, from, to)) { - if (!value.edges.find(({ type }) => type === edge.type)) { - graph.deleteEdge(edge.type, from, to); + }) + .button({ + id: 'save', + name: 'Save', + cb: ({ form: { value } }, { initializing }) => { + if (initializing) return; + // Handle additions and updates + for (const { type, weight } of value.edges) { + graph.setEdgeWeight(type, from, to, weight); } - } - graph.redraw(); - }, - }); + // Handle removals + for (const edge of graph.getEdges(null, from, to)) { + if (!value.edges.find(({ type }) => type === edge.type)) { + graph.deleteEdge(edge.type, from, to); + } + } + graph.redraw(); + }, + }) + .button({ + id: 'cancel', + name: 'Cancel', + cb: (_, { initializing }) => { + if (initializing) return; + doc.clear(); + }, + }); } } diff --git a/forum-network/src/classes/supporting/vertex.js b/forum-network/src/classes/supporting/vertex.js index b1ae83b..3df6a53 100644 --- a/forum-network/src/classes/supporting/vertex.js +++ b/forum-network/src/classes/supporting/vertex.js @@ -48,11 +48,10 @@ export class Vertex { const form = doc.form().lastElement; form .textField({ - id: 'id', name: 'id', defaultValue: vertex.id, disabled: true, + id: 'id', name: 'id', defaultValue: vertex.id, }) .textField({ id: 'type', name: 'type', defaultValue: vertex.type }) .textField({ id: 'label', name: 'label', defaultValue: vertex.label }) - .button({ id: 'save', name: 'Save', @@ -69,9 +68,14 @@ export class Vertex { } }, }) + .button({ + id: 'cancel', + name: 'Cancel', + cb: (_, { initializing }) => { + if (initializing) return; + doc.clear(); }, }); - return doc; } } From 07370be4fadfa09ad6ed6414764af7b713e95108 Mon Sep 17 00:00:00 2001 From: Ladd Hoffman Date: Sun, 9 Jul 2023 21:48:00 -0500 Subject: [PATCH 03/10] Misc. fixes/improvements Standardize vertex as label + list of properties, for rendering and editing --- forum-network/src/classes/actors/expert.js | 10 --- forum-network/src/classes/dao/forum.js | 32 ++++----- forum-network/src/classes/display/actor.js | 8 ++- forum-network/src/classes/display/box.js | 3 +- forum-network/src/classes/display/form.js | 28 ++++---- forum-network/src/classes/supporting/edge.js | 41 +++++------- .../src/classes/supporting/vertex.js | 67 ++++++++++++++----- forum-network/src/classes/supporting/wdg.js | 4 +- forum-network/src/index.css | 3 + forum-network/src/tests/scripts/input.test.js | 8 ++- 10 files changed, 116 insertions(+), 88 deletions(-) diff --git a/forum-network/src/classes/actors/expert.js b/forum-network/src/classes/actors/expert.js index 112f867..eb297e2 100644 --- a/forum-network/src/classes/actors/expert.js +++ b/forum-network/src/classes/actors/expert.js @@ -2,7 +2,6 @@ import { Action } from '../display/action.js'; import { CryptoUtil } from '../supporting/crypto.js'; import { ReputationHolder } from '../reputation/reputation-holder.js'; import { EdgeTypes } from '../../util/constants.js'; -import { displayNumber } from '../../util/helpers.js'; export class Expert extends ReputationHolder { constructor(dao, name, scene, options) { @@ -30,15 +29,6 @@ export class Expert extends ReputationHolder { return tokenValues.reduce((value, total) => total += value, 0); } - getLabel() { - return `${this.name} - - - -
reputation${displayNumber(this.getReputation())}
` - .replaceAll(/\n\s*/g, ''); - } - async initialize() { this.reputationKey = await CryptoUtil.generateAsymmetricKey(); // this.reputationPublicKey = await CryptoUtil.exportKey(this.reputationKey.publicKey); diff --git a/forum-network/src/classes/dao/forum.js b/forum-network/src/classes/dao/forum.js index f06c7b3..44b9121 100644 --- a/forum-network/src/classes/dao/forum.js +++ b/forum-network/src/classes/dao/forum.js @@ -22,22 +22,15 @@ class Post extends Actor { this.title = postContent.title; } - getLabel() { - return `${this.name} - - - - - - -
initial${displayNumber(this.initialValue)}
value${displayNumber(this.value)}
` - .replaceAll(/\n\s*/g, ''); - } - async setValue(value) { this.value = value; await this.setDisplayValue('value', value); - this.forum.graph.getVertex(this.id).setDisplayLabel(this.getLabel()); + this.forum.graph.getVertex(this.id).setProperty('value', value).displayVertex(); + } + + setInitialValue(value) { + this.initialValue = value; + this.forum.graph.getVertex(this.id).setProperty('initialValue', value).displayVertex(); } } @@ -62,7 +55,7 @@ export class Forum extends ReputationHolder { async addPost(senderId, postContent) { console.log('addPost', { senderId, postContent }); const post = new Post(this, senderId, postContent); - this.graph.addVertex(VertexTypes.POST, post.id, post, post.getLabel()); + this.graph.addVertex(VertexTypes.POST, post.id, post, post.name); for (const { postId: citedPostId, weight } of post.citations) { // Special case: Incinerator if (citedPostId === INCINERATOR_ADDRESS && !this.graph.getVertex(INCINERATOR_ADDRESS)) { @@ -98,14 +91,13 @@ export class Forum extends ReputationHolder { const post = postVertex.data; post.setStatus('Validated'); post.initialValue = initialValue; - postVertex.setDisplayLabel(post.getLabel()); const addAuthorToGraph = (publicKey, weight, authorTokenId) => { // For graph display purposes, we want to use the existing Expert actors from the current scene. const author = this.scene.findActor(({ reputationPublicKey }) => reputationPublicKey === publicKey); author.setDisplayValue('reputation', () => author.getReputation()); const authorVertex = this.graph.getVertex(publicKey) - ?? this.graph.addVertex(VertexTypes.AUTHOR, publicKey, author, author.getLabel(), { + ?? this.graph.addVertex(VertexTypes.AUTHOR, publicKey, author, author.name, { hide: author.options.hide, }); this.graph.addEdge( @@ -161,15 +153,15 @@ export class Forum extends ReputationHolder { } else { this.dao.reputation.transferValueFrom(tokenId, authorTokenId, amount); } - await author.computeDisplayValues(); - authorVertex.setDisplayLabel(author.getLabel()); + await author.computeDisplayValues((label, value) => authorVertex.setProperty(label, value)); + authorVertex.displayVertex(); } } const senderVertex = this.graph.getVertex(post.senderId); const { data: sender } = senderVertex; - await sender.computeDisplayValues(); - senderVertex.setDisplayLabel(sender.getLabel()); + await sender.computeDisplayValues((label, value) => senderVertex.setProperty(label, value)); + senderVertex.displayVertex(); // Transfer ownership of the minted tokens to the authors for (const authorEdge of postVertex.getEdges(EdgeTypes.AUTHOR, true)) { diff --git a/forum-network/src/classes/display/actor.js b/forum-network/src/classes/display/actor.js index 0f1b011..08ef1c6 100644 --- a/forum-network/src/classes/display/actor.js +++ b/forum-network/src/classes/display/actor.js @@ -83,10 +83,16 @@ export class Actor { return this; } - async computeDisplayValues() { + /** + * @param {(label: string, value) => {}} cb + */ + async computeDisplayValues(cb) { for (const [label, fn] of this.valueFunctions.entries()) { const value = fn(); await this.setDisplayValue(label, value); + if (cb) { + cb(label, value); + } } } diff --git a/forum-network/src/classes/display/box.js b/forum-network/src/classes/display/box.js index 1ed666a..082bff5 100644 --- a/forum-network/src/classes/display/box.js +++ b/forum-network/src/classes/display/box.js @@ -4,7 +4,8 @@ import { randomID } from '../../util/helpers.js'; export class Box { constructor(name, parentEl, options = {}) { this.name = name; - this.el = document.createElement('div'); + const { tagName = 'div' } = options; + this.el = document.createElement(tagName); this.el.box = this; const id = options.id ?? randomID(); this.el.id = `${parentEl.id}_box_${id}`; diff --git a/forum-network/src/classes/display/form.js b/forum-network/src/classes/display/form.js index 9ad297b..df7c811 100644 --- a/forum-network/src/classes/display/form.js +++ b/forum-network/src/classes/display/form.js @@ -1,30 +1,29 @@ import { randomID } from '../../util/helpers.js'; import { Box } from './box.js'; -const updateValuesOnEventTypes = ['keyup', 'mouseup']; - export class FormElement extends Box { constructor(name, form, opts) { const parentEl = opts.parentEl ?? form.el; super(name, parentEl, opts); this.form = form; this.id = opts.id ?? name; - this.includeInOutput = opts.includeInOutput ?? true; - const { cb } = opts; + const { cb, cbEventTypes = ['change'], cbOnInit = false } = opts; if (cb) { - updateValuesOnEventTypes.forEach((eventType) => this.el.addEventListener(eventType, () => { + cbEventTypes.forEach((eventType) => this.el.addEventListener(eventType, () => { cb(this, { initializing: false }); })); - cb(this, { initializing: true }); + if (cbOnInit) { + cb(this, { initializing: true }); + } } } } export class Button extends FormElement { constructor(name, form, opts) { - super(name, form, opts); + super(name, form, { ...opts, cbEventTypes: ['click'] }); this.button = document.createElement('button'); - this.button.setAttribute('type', 'button'); + this.button.setAttribute('type', opts.type ?? 'button'); this.button.innerHTML = name; this.button.disabled = !!opts.disabled; this.el.appendChild(this.button); @@ -77,7 +76,6 @@ export class SubForm extends FormElement { this.subForm = subForm; if (opts.subFormArray) { opts.subFormArray.subForms.push(this); - this.includeInOutput = false; } } @@ -88,10 +86,12 @@ export class SubForm extends FormElement { export class Form extends Box { constructor(document, opts = {}) { - super(opts.name, opts.parentEl || document.el, opts); + super(opts.name, opts.parentEl || document.el, { ...opts, tagName: 'form' }); this.document = document; this.items = []; this.id = opts.id ?? `form_${randomID()}`; + // Action should be handled by a submit button + this.el.onsubmit = () => false; } button(opts) { @@ -124,11 +124,11 @@ export class Form extends Box { } get value() { - return this.items.reduce((result, { id, value, includeInOutput }) => { - if (includeInOutput && value !== undefined) { - result[id] = value; + return this.items.reduce((obj, { id, value }) => { + if (value !== undefined) { + obj[id] = value; } - return result; + return obj; }, {}); } } diff --git a/forum-network/src/classes/supporting/edge.js b/forum-network/src/classes/supporting/edge.js index ddbf71d..48aa0b7 100644 --- a/forum-network/src/classes/supporting/edge.js +++ b/forum-network/src/classes/supporting/edge.js @@ -55,7 +55,8 @@ export class Edge { this.graph.flowchart?.log(`${this.from.id} --- ${this.getFlowchartNode()} --> ${this.to.id}`); this.graph.flowchart?.log(`class ${Edge.getCombinedKey(this)} edge`); if (this.graph.editable && !this.installedClickCallback) { - this.graph.flowchart?.log(`click ${Edge.getCombinedKey(this)} WDGHandler${this.graph.index} "Edit Edge"`); + this.graph.flowchart?.log(`click ${Edge.getCombinedKey(this)} WDGHandler${this.graph.index} \ +"Edit Edge ${this.from.id} -> ${this.to.id}"`); this.installedClickCallback = true; } } @@ -72,20 +73,19 @@ export class Edge { id: 'to', name: 'to', defaultValue: to, disabled: true, }); + doc.remark('

Edge Types

', { parentEl: form.el }); const subFormArray = form.subFormArray({ id: 'edges', name: 'edges' }).lastItem; + const addEdgeForm = (edge) => { const { subForm } = form.subForm({ name: 'subform', subFormArray }).lastItem; - doc.remark('
', { parentEl: subForm.el }); - subForm.textField({ id: 'type', name: 'type', defaultValue: edge.type }); - subForm.textField({ id: 'weight', name: 'weight', defaultValue: edge.weight }); - subForm.button({ - id: 'remove', - name: 'Remove Edge Type', - cb: (_, { initializing }) => { - if (initializing) return; - subFormArray.remove(subForm); - }, - }); + subForm.textField({ id: 'type', name: 'type', defaultValue: edge.type }) + .textField({ id: 'weight', name: 'weight', defaultValue: edge.weight }) + .button({ + id: 'remove', + name: 'Remove Edge Type', + cb: () => subFormArray.remove(subForm), + }); + doc.remark('
', { parentEl: subForm.el }); }; for (const edge of graph.getEdges(null, from, to)) { @@ -95,23 +95,19 @@ export class Edge { form.button({ id: 'add', name: 'Add Edge Type', - cb: (_, { initializing }) => { - if (initializing) return; - addEdgeForm(new Edge(graph, null, graph.getVertex(from), graph.getVertex(to))); - }, + cb: () => addEdgeForm(new Edge(graph, null, graph.getVertex(from), graph.getVertex(to))), }) .button({ id: 'save', name: 'Save', - cb: ({ form: { value } }, { initializing }) => { - if (initializing) return; + cb: ({ form: { value: { edges } } }) => { // Handle additions and updates - for (const { type, weight } of value.edges) { + for (const { type, weight } of edges) { graph.setEdgeWeight(type, from, to, weight); } // Handle removals for (const edge of graph.getEdges(null, from, to)) { - if (!value.edges.find(({ type }) => type === edge.type)) { + if (!edges.find(({ type }) => type === edge.type)) { graph.deleteEdge(edge.type, from, to); } } @@ -121,10 +117,7 @@ export class Edge { .button({ id: 'cancel', name: 'Cancel', - cb: (_, { initializing }) => { - if (initializing) return; - doc.clear(); - }, + cb: () => doc.clear(), }); } } diff --git a/forum-network/src/classes/supporting/vertex.js b/forum-network/src/classes/supporting/vertex.js index 3df6a53..ae1819b 100644 --- a/forum-network/src/classes/supporting/vertex.js +++ b/forum-network/src/classes/supporting/vertex.js @@ -1,15 +1,19 @@ +import { displayNumber } from '../../util/helpers.js'; + export class Vertex { constructor(graph, type, id, data, options = {}) { this.graph = graph; this.type = type; this.id = id; this.data = data; + this.label = options.label ?? this.id; this.options = options; this.edges = { from: [], to: [], }; this.installedClickCallback = false; + this.properties = new Map(); } reset() { @@ -22,18 +26,28 @@ export class Vertex { ); } - setDisplayLabel(label) { - this.label = label; - this.displayVertex(); + setProperty(key, value) { + this.properties.set(key, value); + return this; } displayVertex() { if (this.options.hide) { return; } - this.graph.flowchart?.log(`${this.id}[${this.label}]`); + + let html = `${this.label}`; + html += ''; + for (const [key, value] of this.properties.entries()) { + const displayValue = typeof value === 'number' ? displayNumber(value) : value; + html += ``; + } + html += '
${key}${displayValue}
'; + html = html.replaceAll(/\n\s*/g, ''); + this.graph.flowchart?.log(`${this.id}[${html}]`); + if (this.graph.editable && !this.installedClickCallback) { - this.graph.flowchart?.log(`click ${this.id} WDGHandler${this.graph.index} "Edit Vertex"`); + this.graph.flowchart?.log(`click ${this.id} WDGHandler${this.graph.index} "Edit Vertex ${this.id}"`); this.installedClickCallback = true; } } @@ -44,24 +58,50 @@ export class Vertex { if (!vertex) { throw new Error(`Could not find WDG Vertex ${vertexId}`); } - doc.remark('

Edit Vertex

'); const form = doc.form().lastElement; + doc.remark('

Edit Vertex

', { parentEl: form.el }); form .textField({ id: 'id', name: 'id', defaultValue: vertex.id, }) .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('

Properties

', { parentEl: form.el }); + const subFormArray = form.subFormArray({ id: 'properties', name: 'properties' }).lastItem; + const addPropertyForm = (key, value) => { + const { subForm } = form.subForm({ name: 'subform', subFormArray }).lastItem; + subForm.textField({ id: 'key', name: 'key', defaultValue: key }) + .textField({ id: 'value', name: 'value', defaultValue: value }) + .button({ + id: 'remove', + name: 'Remove Property', + cb: () => subFormArray.remove(subForm), + }); + doc.remark('
', { parentEl: subForm.el }); + }; + + for (const [key, value] of vertex.properties.entries()) { + addPropertyForm(key, value); + } + + form.button({ + id: 'add', + name: 'Add Property', + cb: () => addPropertyForm('', ''), + }) .button({ id: 'save', name: 'Save', - cb: ({ form: { value } }, { initializing }) => { - if (initializing) return; + type: 'submit', + cb: ({ form: { value: formValue } }) => { let fullRedraw = false; - if (value.id && value.id !== vertex.id) { + if (formValue.id !== vertex.id) { fullRedraw = true; } - Object.assign(vertex, value); + // TODO: preserve data types of properties + formValue.properties = new Map(formValue.properties.map(({ key, value }) => [key, value])); + Object.assign(vertex, formValue); vertex.displayVertex(); if (fullRedraw) { graph.redraw(); @@ -71,10 +111,7 @@ export class Vertex { .button({ id: 'cancel', name: 'Cancel', - cb: (_, { initializing }) => { - if (initializing) return; - doc.clear(); - }, + cb: () => doc.clear(), }); return doc; } diff --git a/forum-network/src/classes/supporting/wdg.js b/forum-network/src/classes/supporting/wdg.js index 45b333e..56312a3 100644 --- a/forum-network/src/classes/supporting/wdg.js +++ b/forum-network/src/classes/supporting/wdg.js @@ -103,9 +103,9 @@ export class WeightedDirectedGraph { if (this.vertices.has(id)) { throw new Error(`Vertex already exists with id: ${id}`); } - const vertex = new Vertex(this, type, id, data, options); + const vertex = new Vertex(this, type, id, data, { ...options, label }); this.vertices.set(id, vertex); - vertex.setDisplayLabel(label ?? id); + vertex.displayVertex(); return vertex; } diff --git a/forum-network/src/index.css b/forum-network/src/index.css index 2db2c1b..6354248 100644 --- a/forum-network/src/index.css +++ b/forum-network/src/index.css @@ -83,3 +83,6 @@ label > div { display: inline-block; min-width: 50px; } +table { + width: 100%; +} diff --git a/forum-network/src/tests/scripts/input.test.js b/forum-network/src/tests/scripts/input.test.js index b3bf2ee..a06d5ab 100644 --- a/forum-network/src/tests/scripts/input.test.js +++ b/forum-network/src/tests/scripts/input.test.js @@ -27,7 +27,13 @@ describe('Document > Form > TextField', () => { dv.set(value); }; - form.textField({ id: 'input1', name: 'Input 1', cb: updateFieldValueDisplay }); + form.textField({ + id: 'input1', + name: 'Input 1', + cb: updateFieldValueDisplay, + cbEventTypes: ['keydown', 'keyup'], + cbOnInit: true, + }); doc.remark('Hmm...!'); }); // it('can exist within a graph', () => { From a743a812184ad0b6993e40fc9b420460f806fee4 Mon Sep 17 00:00:00 2001 From: Ladd Hoffman Date: Sun, 9 Jul 2023 23:00:57 -0500 Subject: [PATCH 04/10] Able to add new vertices --- .../src/classes/display/flowchart.js | 3 +- forum-network/src/classes/display/mermaid.js | 3 -- forum-network/src/classes/display/scene.js | 17 +++++---- forum-network/src/classes/supporting/edge.js | 2 +- .../src/classes/supporting/vertex.js | 37 +++++++++++-------- forum-network/src/classes/supporting/wdg.js | 29 ++++++++++----- forum-network/src/tests/scripts/input.test.js | 2 +- 7 files changed, 55 insertions(+), 38 deletions(-) diff --git a/forum-network/src/classes/display/flowchart.js b/forum-network/src/classes/display/flowchart.js index 058e928..77a4958 100644 --- a/forum-network/src/classes/display/flowchart.js +++ b/forum-network/src/classes/display/flowchart.js @@ -1,10 +1,11 @@ import { MermaidDiagram } from './mermaid.js'; export class Flowchart extends MermaidDiagram { - constructor(box, logBox, direction = 'BT') { + constructor(box, logBox, { direction = 'BT' } = {}) { super(box, logBox); this.direction = direction; this.init(); + this.controlBox = this.box.addBox('Controls'); } init() { diff --git a/forum-network/src/classes/display/mermaid.js b/forum-network/src/classes/display/mermaid.js index a11a588..911b141 100644 --- a/forum-network/src/classes/display/mermaid.js +++ b/forum-network/src/classes/display/mermaid.js @@ -9,7 +9,6 @@ export class MermaidDiagram { this.renderBox = this.box.addBox('Render'); this.box.addBox('Spacer').setInnerHTML(' '); this.logBoxPre = logBox.el.appendChild(document.createElement('pre')); - this.inSection = 0; } static initializeAPI() { @@ -20,10 +19,8 @@ export class MermaidDiagram { darkMode: true, primaryColor: '#2a5b6c', primaryTextColor: '#b6b6b6', - // lineColor: '#349cbd', lineColor: '#57747d', signalColor: '#57747d', - // signalColor: '#349cbd', noteBkgColor: '#516f77', noteTextColor: '#cecece', activationBkgColor: '#1d3f49', diff --git a/forum-network/src/classes/display/scene.js b/forum-network/src/classes/display/scene.js index cc40e96..4606c46 100644 --- a/forum-network/src/classes/display/scene.js +++ b/forum-network/src/classes/display/scene.js @@ -47,26 +47,27 @@ export class Scene { } withFlowchart({ direction = 'BT' } = {}) { - const box = this.topSection.addBox('Flowchart').addClass('padded'); - this.box.addBox('Spacer').setInnerHTML(' '); - const logBox = this.box.addBox('Flowchart text').addClass('dim'); - this.flowchart = new Flowchart(box, logBox, direction); + this.withSectionFlowchart({ direction, section: this.topSection }); + this.flowchart = this.lastFlowchart; return this; } - withAdditionalFlowchart({ id, name, direction = 'BT' } = {}) { + withSectionFlowchart({ + id, name, direction = 'BT', section, + } = {}) { + section = section ?? this.middleSection; const index = this.flowcharts.size; name = name ?? `Flowchart ${index}`; 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 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); return this; } - lastFlowchart() { + get lastFlowchart() { if (!this.flowcharts.size) { if (this.flowchart) { return this.flowchart; diff --git a/forum-network/src/classes/supporting/edge.js b/forum-network/src/classes/supporting/edge.js index 48aa0b7..83550a5 100644 --- a/forum-network/src/classes/supporting/edge.js +++ b/forum-network/src/classes/supporting/edge.js @@ -117,7 +117,7 @@ export class Edge { .button({ id: 'cancel', name: 'Cancel', - cb: () => doc.clear(), + cb: () => graph.resetEditorDocument(), }); } } diff --git a/forum-network/src/classes/supporting/vertex.js b/forum-network/src/classes/supporting/vertex.js index ae1819b..3ca1779 100644 --- a/forum-network/src/classes/supporting/vertex.js +++ b/forum-network/src/classes/supporting/vertex.js @@ -54,18 +54,15 @@ export class Vertex { static prepareEditorDocument(graph, doc, vertexId) { doc.clear(); - const vertex = graph.getVertex(vertexId); - if (!vertex) { - throw new Error(`Could not find WDG Vertex ${vertexId}`); - } + const vertex = vertexId ? graph.getVertex(vertexId) : undefined; const form = doc.form().lastElement; - doc.remark('

Edit Vertex

', { parentEl: form.el }); + doc.remark(`

${vertex ? 'Edit' : 'Add'} Vertex

`, { parentEl: form.el }); form .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: 'label', name: 'label', defaultValue: vertex.label }); + .textField({ id: 'type', name: 'type', defaultValue: vertex?.type }) + .textField({ id: 'label', name: 'label', defaultValue: vertex?.label }); doc.remark('

Properties

', { parentEl: form.el }); const subFormArray = form.subFormArray({ id: 'properties', name: 'properties' }).lastItem; @@ -81,8 +78,10 @@ export class Vertex { doc.remark('
', { parentEl: subForm.el }); }; - for (const [key, value] of vertex.properties.entries()) { - addPropertyForm(key, value); + if (vertex) { + for (const [key, value] of vertex.properties.entries()) { + addPropertyForm(key, value); + } } form.button({ @@ -92,26 +91,34 @@ export class Vertex { }) .button({ id: 'save', - name: 'Save', + name: this.id ? 'Save' : 'Add', type: 'submit', cb: ({ form: { value: formValue } }) => { let fullRedraw = false; - if (formValue.id !== vertex.id) { + if (vertex && formValue.id !== vertex.id) { fullRedraw = true; } // TODO: preserve data types of properties formValue.properties = new Map(formValue.properties.map(({ key, value }) => [key, value])); - Object.assign(vertex, formValue); - vertex.displayVertex(); + if (vertex) { + Object.assign(vertex, formValue); + vertex.displayVertex(); + } else { + graph.addVertex(formValue.type, formValue.id, null, formValue.label); + } if (fullRedraw) { graph.redraw(); } + if (!vertex) { + // This was a new vertex. Now let's edit. + Vertex.prepareEditorDocument(graph, doc, formValue.id); + } }, }) .button({ id: 'cancel', name: 'Cancel', - cb: () => doc.clear(), + cb: () => graph.resetEditorDocument(), }); return doc; } diff --git a/forum-network/src/classes/supporting/wdg.js b/forum-network/src/classes/supporting/wdg.js index 56312a3..28e9c8d 100644 --- a/forum-network/src/classes/supporting/wdg.js +++ b/forum-network/src/classes/supporting/wdg.js @@ -1,13 +1,13 @@ import { Vertex } from './vertex.js'; import { Edge } from './edge.js'; +import { Document } from '../display/document.js'; -const graphs = []; +const allGraphs = []; 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 - const editorDoc = graph.scene.getDocument('editorDocument') - ?? graph.scene.withDocument('editorDocument').lastDocument; + const { editorDoc } = graph; if (vertexId.startsWith('edge:')) { const [, from, to] = vertexId.split(':'); Edge.prepareEditorDocument(graph, editorDoc, from, to); @@ -22,14 +22,17 @@ export class WeightedDirectedGraph { this.vertices = new Map(); this.edgeTypes = new Map(); this.nextVertexId = 0; - this.flowchart = scene?.flowchart; + this.flowchart = undefined; 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. // In order to provide the appropriate graph context for each callback, we create a separate callback // function for each graph. + this.index = allGraphs.length; + allGraphs.push(this); window[`WDGHandler${this.index}`] = makeWDGHandler(this.index); + + // TODO: Populate history this.history = {}; } @@ -88,11 +91,19 @@ export class WeightedDirectedGraph { } withFlowchart() { - this.scene?.withAdditionalFlowchart(); - this.flowchart = this.scene?.lastFlowchart(); + this.scene?.withSectionFlowchart(); + this.flowchart = this.scene?.lastFlowchart; + if (this.editable) { + this.editorDoc = new Document('Controls', this.flowchart.controlBox.el); + this.resetEditorDocument(); + } return this; } + resetEditorDocument() { + Vertex.prepareEditorDocument(this, this.editorDoc); + } + addVertex(type, id, data, label, options) { // Supports simple case of auto-incremented numeric ids if (typeof id === 'object') { diff --git a/forum-network/src/tests/scripts/input.test.js b/forum-network/src/tests/scripts/input.test.js index a06d5ab..8908dc4 100644 --- a/forum-network/src/tests/scripts/input.test.js +++ b/forum-network/src/tests/scripts/input.test.js @@ -37,7 +37,7 @@ describe('Document > Form > TextField', () => { doc.remark('Hmm...!'); }); // it('can exist within a graph', () => { - // scene.withAdditionalFlowchart({ id: 'flowchart', name: 'Graph' }); + // scene.withSectionFlowchart({ id: 'flowchart', name: 'Graph' }); // const graph = scene.lastFlowchart(); // }); }); From ea6e2d44941959dad1af254d351727e577392c12 Mon Sep 17 00:00:00 2001 From: Ladd Hoffman Date: Sun, 9 Jul 2023 23:38:59 -0500 Subject: [PATCH 05/10] Able to add new vertices and edges --- forum-network/src/classes/supporting/edge.js | 1 - .../src/classes/supporting/vertex.js | 82 ++++++++++++------- forum-network/src/classes/supporting/wdg.js | 2 + 3 files changed, 54 insertions(+), 31 deletions(-) diff --git a/forum-network/src/classes/supporting/edge.js b/forum-network/src/classes/supporting/edge.js index 83550a5..79391a5 100644 --- a/forum-network/src/classes/supporting/edge.js +++ b/forum-network/src/classes/supporting/edge.js @@ -62,7 +62,6 @@ export class Edge { } static prepareEditorDocument(graph, doc, from, to) { - doc.clear(); const form = doc.form({ name: 'editorForm' }).lastElement; doc.remark('

Edit Edge

', { parentEl: form.el }); form diff --git a/forum-network/src/classes/supporting/vertex.js b/forum-network/src/classes/supporting/vertex.js index 3ca1779..d02d4b8 100644 --- a/forum-network/src/classes/supporting/vertex.js +++ b/forum-network/src/classes/supporting/vertex.js @@ -1,5 +1,7 @@ import { displayNumber } from '../../util/helpers.js'; +import { Edge } from './edge.js'; + export class Vertex { constructor(graph, type, id, data, options = {}) { this.graph = graph; @@ -88,38 +90,58 @@ export class Vertex { id: 'add', name: 'Add Property', cb: () => addPropertyForm('', ''), - }) - .button({ - id: 'save', - name: this.id ? 'Save' : 'Add', - type: 'submit', - cb: ({ form: { value: formValue } }) => { - let fullRedraw = false; - if (vertex && formValue.id !== vertex.id) { - fullRedraw = true; - } - // TODO: preserve data types of properties - formValue.properties = new Map(formValue.properties.map(({ key, value }) => [key, value])); - if (vertex) { - Object.assign(vertex, formValue); - vertex.displayVertex(); - } else { - graph.addVertex(formValue.type, formValue.id, null, formValue.label); - } - if (fullRedraw) { - graph.redraw(); - } - if (!vertex) { - // This was a new vertex. Now let's edit. - Vertex.prepareEditorDocument(graph, doc, formValue.id); - } + }); + + form.button({ + id: 'save', + name: 'Save', + type: 'submit', + cb: ({ form: { value: formValue } }) => { + let fullRedraw = false; + if (vertex && formValue.id !== vertex.id) { + fullRedraw = true; + } + // TODO: preserve data types of properties + formValue.properties = new Map(formValue.properties.map(({ key, value }) => [key, value])); + if (vertex) { + Object.assign(vertex, formValue); + vertex.displayVertex(); + } else { + graph.addVertex(formValue.type, formValue.id, null, formValue.label); + } + if (fullRedraw) { + graph.redraw(); + } + if (!vertex) { + // This was a new vertex. Now let's edit. + doc.clear(); + Vertex.prepareEditorDocument(graph, doc, formValue.id); + } + }, + }); + + if (vertex) { + doc.remark('

New Edge

', { parentEl: form.el }); + const { subForm } = form.subForm({ name: 'newEdge' }).lastItem; + subForm.textField({ name: 'to' }); + subForm.textField({ name: 'type' }); + subForm.textField({ name: 'weight' }); + subForm.button({ + name: 'Save', + cb: ({ form: { value: { to, type, weight } } }) => { + graph.addEdge(type, vertex, to, weight, null); + doc.clear(); + Edge.prepareEditorDocument(graph, doc, vertex.id, to); }, - }) - .button({ - id: 'cancel', - name: 'Cancel', - cb: () => graph.resetEditorDocument(), }); + } + + form.button({ + id: 'cancel', + name: 'Cancel', + cb: () => graph.resetEditorDocument(), + }); + return doc; } } diff --git a/forum-network/src/classes/supporting/wdg.js b/forum-network/src/classes/supporting/wdg.js index 28e9c8d..4f72777 100644 --- a/forum-network/src/classes/supporting/wdg.js +++ b/forum-network/src/classes/supporting/wdg.js @@ -8,6 +8,7 @@ const makeWDGHandler = (graphIndex) => (vertexId) => { const graph = allGraphs[graphIndex]; // We want a document for editing this node, which may be a vertex or an edge const { editorDoc } = graph; + editorDoc.clear(); if (vertexId.startsWith('edge:')) { const [, from, to] = vertexId.split(':'); Edge.prepareEditorDocument(graph, editorDoc, from, to); @@ -101,6 +102,7 @@ export class WeightedDirectedGraph { } resetEditorDocument() { + this.editorDoc.clear(); Vertex.prepareEditorDocument(this, this.editorDoc); } From 72c3bd16633914225dee78c05bc47dab5656d13a Mon Sep 17 00:00:00 2001 From: Ladd Hoffman Date: Sun, 9 Jul 2023 23:48:45 -0500 Subject: [PATCH 06/10] Able to delete vertices --- .../src/classes/supporting/vertex.js | 12 ++++++ forum-network/src/classes/supporting/wdg.js | 40 +++++++++++-------- 2 files changed, 36 insertions(+), 16 deletions(-) diff --git a/forum-network/src/classes/supporting/vertex.js b/forum-network/src/classes/supporting/vertex.js index d02d4b8..4e2e79a 100644 --- a/forum-network/src/classes/supporting/vertex.js +++ b/forum-network/src/classes/supporting/vertex.js @@ -142,6 +142,18 @@ export class Vertex { cb: () => graph.resetEditorDocument(), }); + if (vertex) { + form.button({ + id: 'delete', + name: 'Delete Vertex', + cb: () => { + graph.deleteVertex(vertex.id); + graph.redraw(); + graph.resetEditorDocument(); + }, + }); + } + return doc; } } diff --git a/forum-network/src/classes/supporting/wdg.js b/forum-network/src/classes/supporting/wdg.js index 4f72777..c0e71cb 100644 --- a/forum-network/src/classes/supporting/wdg.js +++ b/forum-network/src/classes/supporting/wdg.js @@ -146,22 +146,6 @@ export class WeightedDirectedGraph { return edges?.get(edgeKey); } - deleteEdge(type, from, to) { - from = from instanceof Vertex ? from : this.getVertex(from); - to = to instanceof Vertex ? to : this.getVertex(to); - const edges = this.edgeTypes.get(type); - const edgeKey = Edge.getKey({ type, from, to }); - if (!edges) return; - const edge = edges.get(edgeKey); - if (!edge) return; - to.edges.from.forEach((x, i) => (x === edge) && to.edges.from.splice(i, 1)); - from.edges.to.forEach((x, i) => (x === edge) && from.edges.to.splice(i, 1)); - edges.delete(edgeKey); - if (edges.size === 0) { - this.edgeTypes.delete(type); - } - } - getEdgeWeight(type, from, to) { return this.getEdge(type, from, to)?.weight; } @@ -219,4 +203,28 @@ export class WeightedDirectedGraph { } return Array.from(this.vertices.values()).filter((vertex) => vertex.type === type).length; } + + deleteEdge(type, from, to) { + from = from instanceof Vertex ? from : this.getVertex(from); + to = to instanceof Vertex ? to : this.getVertex(to); + const edges = this.edgeTypes.get(type); + const edgeKey = Edge.getKey({ type, from, to }); + if (!edges) return; + const edge = edges.get(edgeKey); + if (!edge) return; + to.edges.from.forEach((x, i) => (x === edge) && to.edges.from.splice(i, 1)); + from.edges.to.forEach((x, i) => (x === edge) && from.edges.to.splice(i, 1)); + edges.delete(edgeKey); + if (edges.size === 0) { + this.edgeTypes.delete(type); + } + } + + deleteVertex(id) { + const vertex = this.getVertex(id); + for (const { type, from, to } of [...vertex.edges.to, ...vertex.edges.from]) { + this.deleteEdge(type, from, to); + } + this.vertices.delete(id); + } } From 8d0daf20622516e9524ed6e470d0207ebf712618 Mon Sep 17 00:00:00 2001 From: Ladd Hoffman Date: Mon, 10 Jul 2023 01:20:51 -0500 Subject: [PATCH 07/10] Minor cleanup --- .../src/classes/display/flowchart.js | 1 - forum-network/src/classes/display/form.js | 6 ++-- forum-network/src/classes/display/mermaid.js | 1 + .../{controls.js => scene-controls.js} | 2 +- forum-network/src/classes/display/scene.js | 6 ++-- forum-network/src/classes/supporting/edge.js | 17 ++++++++-- .../src/classes/supporting/vertex.js | 32 ++++++++----------- forum-network/src/classes/supporting/wdg.js | 3 +- forum-network/src/index.css | 5 ++- .../src/tests/scripts/availability.test.js | 2 +- .../src/tests/scripts/forum.test-util.js | 2 +- .../src/tests/scripts/validation-pool.test.js | 2 +- 12 files changed, 46 insertions(+), 33 deletions(-) rename forum-network/src/classes/display/{controls.js => scene-controls.js} (98%) diff --git a/forum-network/src/classes/display/flowchart.js b/forum-network/src/classes/display/flowchart.js index 77a4958..33d4d97 100644 --- a/forum-network/src/classes/display/flowchart.js +++ b/forum-network/src/classes/display/flowchart.js @@ -5,7 +5,6 @@ export class Flowchart extends MermaidDiagram { super(box, logBox); this.direction = direction; this.init(); - this.controlBox = this.box.addBox('Controls'); } init() { diff --git a/forum-network/src/classes/display/form.js b/forum-network/src/classes/display/form.js index df7c811..3dec6bc 100644 --- a/forum-network/src/classes/display/form.js +++ b/forum-network/src/classes/display/form.js @@ -2,7 +2,7 @@ import { randomID } from '../../util/helpers.js'; import { Box } from './box.js'; export class FormElement extends Box { - constructor(name, form, opts) { + constructor(name, form, opts = {}) { const parentEl = opts.parentEl ?? form.el; super(name, parentEl, opts); this.form = form; @@ -71,7 +71,7 @@ export class SubFormArray extends FormElement { export class SubForm extends FormElement { constructor(name, form, opts) { const parentEl = opts.subFormArray ? opts.subFormArray.el : form.el; - const subForm = form.document.form({ name, parentEl }).lastElement; + const subForm = form.document.form({ name, parentEl, tagName: 'div' }).lastElement; super(name, form, { ...opts, parentEl }); this.subForm = subForm; if (opts.subFormArray) { @@ -86,7 +86,7 @@ export class SubForm extends FormElement { export class Form extends Box { constructor(document, opts = {}) { - super(opts.name, opts.parentEl || document.el, { ...opts, tagName: 'form' }); + super(opts.name, opts.parentEl || document.el, { tagName: 'form', ...opts }); this.document = document; this.items = []; this.id = opts.id ?? `form_${randomID()}`; diff --git a/forum-network/src/classes/display/mermaid.js b/forum-network/src/classes/display/mermaid.js index 911b141..9e1b99a 100644 --- a/forum-network/src/classes/display/mermaid.js +++ b/forum-network/src/classes/display/mermaid.js @@ -28,6 +28,7 @@ export class MermaidDiagram { }, securityLevel: 'loose', // 'loose' so that we can use click events // logLevel: 'debug', + useMaxWidth: true, }); } diff --git a/forum-network/src/classes/display/controls.js b/forum-network/src/classes/display/scene-controls.js similarity index 98% rename from forum-network/src/classes/display/controls.js rename to forum-network/src/classes/display/scene-controls.js index 4911456..02340e3 100644 --- a/forum-network/src/classes/display/controls.js +++ b/forum-network/src/classes/display/scene-controls.js @@ -6,7 +6,7 @@ class Button { } } -export class Controls { +export class SceneControls { constructor(parentBox) { this.disableAutoplayButton = new Button('Disable Auto-play', () => { const url = new URL(window.location.href); diff --git a/forum-network/src/classes/display/scene.js b/forum-network/src/classes/display/scene.js index 4606c46..43103e5 100644 --- a/forum-network/src/classes/display/scene.js +++ b/forum-network/src/classes/display/scene.js @@ -4,7 +4,7 @@ import { MermaidDiagram } from './mermaid.js'; import { SequenceDiagram } from './sequence.js'; import { Table } from './table.js'; import { Flowchart } from './flowchart.js'; -import { Controls } from './controls.js'; +import { SceneControls } from './scene-controls.js'; import { Box } from './box.js'; import { Document } from './document.js'; @@ -33,8 +33,8 @@ export class Scene { if (!window.disableSceneControls) { this.topRail = new Box('Top rail', document.body, { prepend: true }).addClass('top-rail'); - this.controlsBox = this.topRail.addBox('Controls').addClass('controls'); - this.controls = new Controls(this.controlsBox); + const controlsBox = this.topRail.addBox('SceneControls').addClass('scene-controls'); + this.controls = new SceneControls(controlsBox); } } diff --git a/forum-network/src/classes/supporting/edge.js b/forum-network/src/classes/supporting/edge.js index 79391a5..14af725 100644 --- a/forum-network/src/classes/supporting/edge.js +++ b/forum-network/src/classes/supporting/edge.js @@ -77,8 +77,12 @@ export class Edge { const addEdgeForm = (edge) => { const { subForm } = form.subForm({ name: 'subform', subFormArray }).lastItem; - subForm.textField({ id: 'type', name: 'type', defaultValue: edge.type }) - .textField({ id: 'weight', name: 'weight', defaultValue: edge.weight }) + subForm.textField({ + id: 'type', name: 'type', defaultValue: edge.type, required: true, + }) + .textField({ + id: 'weight', name: 'weight', defaultValue: edge.weight, required: true, + }) .button({ id: 'remove', name: 'Remove Edge Type', @@ -99,7 +103,15 @@ export class Edge { .button({ id: 'save', name: 'Save', + type: 'submit', cb: ({ form: { value: { edges } } }) => { + // Do validation + for (const { type, weight } of edges) { + if (type === null || weight === null) { + graph.errorDoc.remark('
type and weight are required
'); + return; + } + } // Handle additions and updates for (const { type, weight } of edges) { graph.setEdgeWeight(type, from, to, weight); @@ -111,6 +123,7 @@ export class Edge { } } graph.redraw(); + graph.errorDoc.clear(); }, }) .button({ diff --git a/forum-network/src/classes/supporting/vertex.js b/forum-network/src/classes/supporting/vertex.js index 4e2e79a..9c874d6 100644 --- a/forum-network/src/classes/supporting/vertex.js +++ b/forum-network/src/classes/supporting/vertex.js @@ -107,20 +107,28 @@ export class Vertex { Object.assign(vertex, formValue); vertex.displayVertex(); } else { - graph.addVertex(formValue.type, formValue.id, null, formValue.label); + const newVertex = graph.addVertex(formValue.type, formValue.id, null, formValue.label); + Object.assign(newVertex, formValue); + doc.clear(); + Vertex.prepareEditorDocument(graph, doc, newVertex.id); } if (fullRedraw) { graph.redraw(); } - if (!vertex) { - // This was a new vertex. Now let's edit. - doc.clear(); - Vertex.prepareEditorDocument(graph, doc, formValue.id); - } }, }); if (vertex) { + form.button({ + id: 'delete', + name: 'Delete Vertex', + cb: () => { + graph.deleteVertex(vertex.id); + graph.redraw(); + graph.resetEditorDocument(); + }, + }); + doc.remark('

New Edge

', { parentEl: form.el }); const { subForm } = form.subForm({ name: 'newEdge' }).lastItem; subForm.textField({ name: 'to' }); @@ -142,18 +150,6 @@ export class Vertex { cb: () => graph.resetEditorDocument(), }); - if (vertex) { - form.button({ - id: 'delete', - name: 'Delete Vertex', - cb: () => { - graph.deleteVertex(vertex.id); - graph.redraw(); - graph.resetEditorDocument(); - }, - }); - } - return doc; } } diff --git a/forum-network/src/classes/supporting/wdg.js b/forum-network/src/classes/supporting/wdg.js index c0e71cb..c1768a0 100644 --- a/forum-network/src/classes/supporting/wdg.js +++ b/forum-network/src/classes/supporting/wdg.js @@ -95,9 +95,10 @@ export class WeightedDirectedGraph { this.scene?.withSectionFlowchart(); this.flowchart = this.scene?.lastFlowchart; if (this.editable) { - this.editorDoc = new Document('Controls', this.flowchart.controlBox.el); + this.editorDoc = new Document('WDGControls', this.flowchart.box.el); this.resetEditorDocument(); } + this.errorDoc = new Document('WDGErrors', this.flowchart.box.el); return this; } diff --git a/forum-network/src/index.css b/forum-network/src/index.css index 6354248..baf34d5 100644 --- a/forum-network/src/index.css +++ b/forum-network/src/index.css @@ -42,7 +42,7 @@ a:visited { width: 100%; height: 0; } -.controls { +.scene-controls { position: relative; left: 150px; } @@ -86,3 +86,6 @@ label > div { table { width: 100%; } +form { + min-width: 20em; +} \ No newline at end of file diff --git a/forum-network/src/tests/scripts/availability.test.js b/forum-network/src/tests/scripts/availability.test.js index 64c45a7..7cac03d 100644 --- a/forum-network/src/tests/scripts/availability.test.js +++ b/forum-network/src/tests/scripts/availability.test.js @@ -4,7 +4,7 @@ import { Expert } from '../../classes/actors/expert.js'; import { DAO } from '../../classes/dao/dao.js'; import { Public } from '../../classes/actors/public.js'; import { PostContent } from '../../classes/supporting/post-content.js'; -import { delayOrWait } from '../../classes/display/controls.js'; +import { delayOrWait } from '../../classes/display/scene-controls.js'; import { mochaRun } from '../../util/helpers.js'; const DELAY_INTERVAL = 100; diff --git a/forum-network/src/tests/scripts/forum.test-util.js b/forum-network/src/tests/scripts/forum.test-util.js index 02e85d5..36cb47a 100644 --- a/forum-network/src/tests/scripts/forum.test-util.js +++ b/forum-network/src/tests/scripts/forum.test-util.js @@ -3,7 +3,7 @@ import { Scene } from '../../classes/display/scene.js'; import { Expert } from '../../classes/actors/expert.js'; import { PostContent } from '../../classes/supporting/post-content.js'; import { DAO } from '../../classes/dao/dao.js'; -import { delayOrWait } from '../../classes/display/controls.js'; +import { delayOrWait } from '../../classes/display/scene-controls.js'; export class ForumTest { constructor(options) { diff --git a/forum-network/src/tests/scripts/validation-pool.test.js b/forum-network/src/tests/scripts/validation-pool.test.js index b7a5b05..77cfd65 100644 --- a/forum-network/src/tests/scripts/validation-pool.test.js +++ b/forum-network/src/tests/scripts/validation-pool.test.js @@ -3,7 +3,7 @@ import { Scene } from '../../classes/display/scene.js'; import { Expert } from '../../classes/actors/expert.js'; import { PostContent } from '../../classes/supporting/post-content.js'; import { DAO } from '../../classes/dao/dao.js'; -import { delayOrWait } from '../../classes/display/controls.js'; +import { delayOrWait } from '../../classes/display/scene-controls.js'; import { mochaRun } from '../../util/helpers.js'; const POOL_DURATION_MS = 100; From 9eff8846369c83234d7d936cfb2b4a13b2cd7357 Mon Sep 17 00:00:00 2001 From: Ladd Hoffman Date: Mon, 10 Jul 2023 01:46:52 -0500 Subject: [PATCH 08/10] Display vertex type in graph --- forum-network/src/classes/supporting/vertex.js | 4 +++- forum-network/src/index.css | 5 +++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/forum-network/src/classes/supporting/vertex.js b/forum-network/src/classes/supporting/vertex.js index 9c874d6..d37967f 100644 --- a/forum-network/src/classes/supporting/vertex.js +++ b/forum-network/src/classes/supporting/vertex.js @@ -38,7 +38,9 @@ export class Vertex { return; } - let html = `${this.label}`; + let html = ''; + html += `${this.type}
`; + html += `${this.label}`; html += ''; for (const [key, value] of this.properties.entries()) { const displayValue = typeof value === 'number' ? displayNumber(value) : value; diff --git a/forum-network/src/index.css b/forum-network/src/index.css index baf34d5..b9108fc 100644 --- a/forum-network/src/index.css +++ b/forum-network/src/index.css @@ -88,4 +88,9 @@ table { } form { min-width: 20em; +} +.small { + font-size: 8pt; + line-height: 0; + color: #999999; } \ No newline at end of file From 5230f8664b53493fadaa87313e1e5870a10818f0 Mon Sep 17 00:00:00 2001 From: Ladd Hoffman Date: Mon, 10 Jul 2023 02:33:29 -0500 Subject: [PATCH 09/10] Show vertex ids in graph --- forum-network/src/classes/supporting/edge.js | 11 ++++++++--- forum-network/src/classes/supporting/vertex.js | 7 +++++-- forum-network/src/classes/supporting/wdg.js | 2 +- forum-network/src/index.css | 6 ++---- 4 files changed, 16 insertions(+), 10 deletions(-) diff --git a/forum-network/src/classes/supporting/edge.js b/forum-network/src/classes/supporting/edge.js index 14af725..d8e4017 100644 --- a/forum-network/src/classes/supporting/edge.js +++ b/forum-network/src/classes/supporting/edge.js @@ -29,16 +29,21 @@ export class Edge { } getHtml() { - let html = '
'; - for (const { type, weight } of this.getComorphicEdges()) { + const edges = this.getComorphicEdges(); + let html = ''; + html += `edge${edges.length > 1 ? 's' : ''}
`; + html += '
'; + for (const { type, weight } of edges) { html += ``; } html += '
${type}${weight}
'; + html += `${this.from.id} → ${this.to.id}
`; + return html; } getFlowchartNode() { - return `${Edge.getCombinedKey(this)}(${this.getHtml()})`; + return `${Edge.getCombinedKey(this)}("${this.getHtml()}")`; } displayEdgeNode() { diff --git a/forum-network/src/classes/supporting/vertex.js b/forum-network/src/classes/supporting/vertex.js index d37967f..e7ee468 100644 --- a/forum-network/src/classes/supporting/vertex.js +++ b/forum-network/src/classes/supporting/vertex.js @@ -39,7 +39,7 @@ export class Vertex { } let html = ''; - html += `${this.type}
`; + html += 'vertex
'; html += `${this.label}`; html += ''; for (const [key, value] of this.properties.entries()) { @@ -47,8 +47,11 @@ export class Vertex { html += ``; } html += '
${key}${displayValue}
'; + if (this.id !== this.label) { + html += `${this.id}
`; + } html = html.replaceAll(/\n\s*/g, ''); - this.graph.flowchart?.log(`${this.id}[${html}]`); + this.graph.flowchart?.log(`${this.id}["${html}"]`); if (this.graph.editable && !this.installedClickCallback) { this.graph.flowchart?.log(`click ${this.id} WDGHandler${this.graph.index} "Edit Vertex ${this.id}"`); diff --git a/forum-network/src/classes/supporting/wdg.js b/forum-network/src/classes/supporting/wdg.js index c1768a0..ab083e7 100644 --- a/forum-network/src/classes/supporting/wdg.js +++ b/forum-network/src/classes/supporting/wdg.js @@ -23,7 +23,7 @@ export class WeightedDirectedGraph { this.vertices = new Map(); this.edgeTypes = new Map(); this.nextVertexId = 0; - this.flowchart = undefined; + this.flowchart = scene?.flowchart; this.editable = options.editable; // Mermaid supports a click callback, but we can't customize arguments; we just get the vertex ID. diff --git a/forum-network/src/index.css b/forum-network/src/index.css index b9108fc..a42bab2 100644 --- a/forum-network/src/index.css +++ b/forum-network/src/index.css @@ -89,8 +89,6 @@ table { form { min-width: 20em; } -.small { - font-size: 8pt; - line-height: 0; - color: #999999; +span.small { + font-size: smaller; } \ No newline at end of file From 907b99bb65db0e4d5da5c93f74377e38c5cc8e1f Mon Sep 17 00:00:00 2001 From: Ladd Hoffman Date: Mon, 10 Jul 2023 02:41:44 -0500 Subject: [PATCH 10/10] Remove extraneous info --- forum-network/src/classes/supporting/edge.js | 2 -- forum-network/src/classes/supporting/vertex.js | 1 - forum-network/src/tests/scripts/availability.test.js | 4 ---- forum-network/src/tests/scripts/validation-pool.test.js | 1 - 4 files changed, 8 deletions(-) diff --git a/forum-network/src/classes/supporting/edge.js b/forum-network/src/classes/supporting/edge.js index d8e4017..dd22fe1 100644 --- a/forum-network/src/classes/supporting/edge.js +++ b/forum-network/src/classes/supporting/edge.js @@ -31,13 +31,11 @@ export class Edge { getHtml() { const edges = this.getComorphicEdges(); let html = ''; - html += `edge${edges.length > 1 ? 's' : ''}
`; html += ''; for (const { type, weight } of edges) { html += ``; } html += '
${type}${weight}
'; - html += `${this.from.id} → ${this.to.id}
`; return html; } diff --git a/forum-network/src/classes/supporting/vertex.js b/forum-network/src/classes/supporting/vertex.js index e7ee468..fb27be6 100644 --- a/forum-network/src/classes/supporting/vertex.js +++ b/forum-network/src/classes/supporting/vertex.js @@ -39,7 +39,6 @@ export class Vertex { } let html = ''; - html += 'vertex
'; html += `${this.label}`; html += ''; for (const [key, value] of this.properties.entries()) { diff --git a/forum-network/src/tests/scripts/availability.test.js b/forum-network/src/tests/scripts/availability.test.js index 7cac03d..5ac1619 100644 --- a/forum-network/src/tests/scripts/availability.test.js +++ b/forum-network/src/tests/scripts/availability.test.js @@ -18,10 +18,6 @@ const newExpert = async () => { const index = experts.length; const name = `Expert${index + 1}`; const expert = await new Expert(dao, name, scene).initialize(); - expert.setDisplayValue( - 'rep', - () => dao.reputation.valueOwnedBy(expert.reputationPublicKey), - ); experts.push(expert); return expert; }; diff --git a/forum-network/src/tests/scripts/validation-pool.test.js b/forum-network/src/tests/scripts/validation-pool.test.js index 77cfd65..15ceef4 100644 --- a/forum-network/src/tests/scripts/validation-pool.test.js +++ b/forum-network/src/tests/scripts/validation-pool.test.js @@ -17,7 +17,6 @@ async function newExpert() { const index = experts.length; const name = `Expert${index + 1}`; const expert = await new Expert(dao, name, scene).initialize(); - await expert.addComputedValue('rep', () => dao.reputation.valueOwnedBy(expert.reputationPublicKey)); experts.push(expert); return expert; }