From 496c132b82bad8b85ddf0056b1c9e699a48bd375 Mon Sep 17 00:00:00 2001 From: Ladd Hoffman Date: Tue, 11 Jul 2023 12:53:40 -0500 Subject: [PATCH] Enable wdg import --- src/classes/display/form.js | 33 ++++++++++++++++++- src/classes/supporting/edge.js | 3 +- src/classes/supporting/vertex.js | 3 +- src/classes/supporting/wdg.js | 39 +++++++++++++++++++++-- src/tests/scripts/wdg.test.js | 54 ++++++++++++++++++++++++++++++-- 5 files changed, 122 insertions(+), 10 deletions(-) diff --git a/src/classes/display/form.js b/src/classes/display/form.js index 3dec6bc..f2fc618 100644 --- a/src/classes/display/form.js +++ b/src/classes/display/form.js @@ -26,7 +26,14 @@ export class Button extends FormElement { this.button.setAttribute('type', opts.type ?? 'button'); this.button.innerHTML = name; this.button.disabled = !!opts.disabled; - this.el.appendChild(this.button); + if (opts.label) { + this.label = document.createElement('label'); + this.label.innerHTML = opts.label; + this.label.appendChild(this.button); + this.el.appendChild(this.label); + } else { + this.el.appendChild(this.button); + } } } @@ -84,6 +91,20 @@ export class SubForm extends FormElement { } } +export class FileInput extends FormElement { + constructor(name, form, opts) { + super(name, form, opts); + this.input = document.createElement('input'); + this.input.type = 'file'; + this.input.accept = 'application/json'; + // this.input.classList.add('visually-hidden') + this.label = document.createElement('label'); + this.label.innerHTML = name; + this.label.appendChild(this.input); + this.el.appendChild(this.label); + } +} + export class Form extends Box { constructor(document, opts = {}) { super(opts.name, opts.parentEl || document.el, { tagName: 'form', ...opts }); @@ -99,6 +120,11 @@ export class Form extends Box { return this; } + submit(opts) { + this.items.push(new Button(opts.name, this, { ...opts, type: 'submit' })); + return this; + } + textField(opts) { this.items.push(new TextField(opts.name, this, opts)); return this; @@ -119,6 +145,11 @@ export class Form extends Box { return this; } + fileInput(opts) { + this.items.push(new FileInput(opts.label || opts.name, this, opts)); + return this; + } + get lastItem() { return this.items[this.items.length - 1]; } diff --git a/src/classes/supporting/edge.js b/src/classes/supporting/edge.js index 08c56b0..a05e48f 100644 --- a/src/classes/supporting/edge.js +++ b/src/classes/supporting/edge.js @@ -112,10 +112,9 @@ export class Edge { name: 'Add Edge Type', cb: () => addEdgeForm(new Edge(graph, null, graph.getVertex(from), graph.getVertex(to))), }) - .button({ + .submit({ id: 'save', name: 'Save', - type: 'submit', cb: ({ form: { value: { edges } } }) => { // Do validation for (const { type, weight } of edges) { diff --git a/src/classes/supporting/vertex.js b/src/classes/supporting/vertex.js index 8219c32..7677c8e 100644 --- a/src/classes/supporting/vertex.js +++ b/src/classes/supporting/vertex.js @@ -108,10 +108,9 @@ export class Vertex { cb: () => addPropertyForm('', ''), }); - form.button({ + form.submit({ id: 'save', name: 'Save', - type: 'submit', cb: ({ form: { value: formValue } }) => { let fullRedraw = false; if (vertex && formValue.id !== vertex.id) { diff --git a/src/classes/supporting/wdg.js b/src/classes/supporting/wdg.js index f017c3f..762c050 100644 --- a/src/classes/supporting/wdg.js +++ b/src/classes/supporting/wdg.js @@ -45,15 +45,36 @@ export class WeightedDirectedGraph { toJSON() { return { + name: this.name, vertices: Array.from(this.vertices.values()) .map((vertex) => vertex.toJSON()), edges: Array.from(this.edgeTypes.values()) .flatMap((edges) => Array.from(edges.values())) .map((edge) => edge.toJSON()), - history: this.getHistory(), + // history: this.getHistory(), }; } + fromJSON({ name, vertices, edges }) { + this.name = name; + this.vertices.clear(); + this.edgeTypes.clear(); + for (const { + id, type, label, properties, + } of vertices) { + const vertex = this.addVertex(type, id, null, label); + for (const [key, value] of Object.entries(properties)) { + vertex.setProperty(key, value); + } + } + for (const { + from, to, type, weight, + } of edges) { + this.addEdge(type, from, to, weight); + } + this.redraw(); + } + redraw() { // Call .reset() on all vertices and edges for (const vertex of this.vertices.values()) { @@ -108,11 +129,12 @@ export class WeightedDirectedGraph { resetEditor() { this.editorDoc.clear(); + this.controlDoc.clear(); Vertex.prepareEditorDocument(this, this.editorDoc); const form = this.controlDoc.form({ name: 'controlForm' }).lastElement; form.button({ - id: 'export', - name: 'Export', + name: 'Download', + label: 'Export', cb: () => { const a = window.document.createElement('a'); const json = JSON.stringify(this.toJSON(), null, 2); @@ -123,6 +145,17 @@ export class WeightedDirectedGraph { a.click(); }, }); + form.fileInput({ + label: 'Import', + cb: ({ input: { files: [file] } }) => { + const reader = new FileReader(); + reader.onload = ({ target: { result: text } }) => { + const data = JSON.parse(text); + this.fromJSON(data); + }; + reader.readAsText(file); + }, + }); } addVertex(type, id, data, label, options) { diff --git a/src/tests/scripts/wdg.test.js b/src/tests/scripts/wdg.test.js index 1eed931..64520eb 100644 --- a/src/tests/scripts/wdg.test.js +++ b/src/tests/scripts/wdg.test.js @@ -50,8 +50,8 @@ describe('Weighted Directed Graph', function tests() { it('can export to JSON', () => { const result = graph.toJSON(); - console.log('export to JSON, result', result); result.should.eql({ + name: 'test1', vertices: [ { id: '0', type: 'v1', label: '0', properties: {}, @@ -95,9 +95,59 @@ describe('Weighted Directed Graph', function tests() { weight: 0.125, }, ], - history: [], + // history: [], }); }); + + it('can import from JSON', () => { + const graph2 = new WeightedDirectedGraph('hidden1'); + graph2.fromJSON({ + name: 'hidden1import', + vertices: [{ + id: '1', + label: '1', + type: 'v1', + properties: { + color: 'green', + }, + }, { + id: '2', + label: '2', + type: 'v1', + properties: { + color: 'blue', + }, + }], + edges: [{ + from: '1', + to: '2', + type: 'e1', + weight: 0.5, + }], + }); + + graph2.vertices.size.should.equal(2); + graph2.edgeTypes.size.should.equal(1); + graph2.edgeTypes.get('e1').size.should.equal(1); + + const v1 = graph2.getVertex('1'); + v1.id.should.equal('1'); + v1.label.should.equal('1'); + v1.type.should.equal('v1'); + v1.properties.size.should.equal(1); + v1.properties.get('color').should.equal('green'); + + const v2 = graph2.getVertex('2'); + v2.id.should.equal('2'); + v2.label.should.equal('2'); + v2.type.should.equal('v1'); + v2.properties.size.should.equal(1); + v2.properties.get('color').should.equal('blue'); + + const e1 = graph2.getEdge('e1', '1', '2'); + v1.edges.from[0].should.equal(e1); + v2.edges.to[0].should.equal(e1); + }); }); describe('Editable', () => {