diff --git a/Architype.js b/Architype.js index f822d40..1f1580f 100644 --- a/Architype.js +++ b/Architype.js @@ -47,19 +47,18 @@ class Architype { this.addDefaultEntries(); } - this.observer_ = new MutationObserver(e => { this.onChange(e); }); - this.observer2_ = new MutationObserver(e => { this.snapshot(e); }); + this.observer_ = new MutationObserver(e => { this.onChange(); }); + this.observer2_ = new MutationObserver(e => { this.snapshot(false); }); this.observe(); - this.saveAndRender(); - - history.replaceState('first', null, '#' + btoa(this.serializedStr_)); + this.render(); + this.snapshot(true); } observe() { this.observer_.observe(this.editorElem_, { attributes: true, - attributeFilter: ['data-arch-refresh'], + attributeFilter: ['data-arch-render'], childList: true, subtree: true, }); @@ -130,7 +129,7 @@ class Architype { this.editor_.clear(); this.unserialize(ser); this.observe(); - this.saveAndRender(); + this.render(); } onHashChange() { @@ -145,19 +144,25 @@ class Architype { onChange() { ++this.generation_; - this.saveAndRender(); + this.render(); } - snapshot() { - history.pushState(null, null, '#' + btoa(this.serializedStr_)); - this.first_ = false; - } - - saveAndRender() { + snapshot(first) { this.serialized_ = this.serialize(); - this.startRender(); this.serializedStr_ = JSON.stringify(this.serialized_); localStorage.setItem('currentState', this.serializedStr_); + this.first_ = first || false; + let hash = '#' + btoa(this.serializedStr_); + if (first) { + history.replaceState('first', null, hash); + } else { + history.pushState(null, null, hash); + } + } + + render() { + this.serialized_ = this.serialize(); + this.startRender(); } addDefaultEntries() { diff --git a/Editor.js b/Editor.js index bf82d70..948f4c4 100644 --- a/Editor.js +++ b/Editor.js @@ -196,10 +196,8 @@ class Editor extends List { case '`': if (!this.container_.parentElement.xArchObj) { for (let entry of this.queryEntries('.highlight')) { - entry.getElement().classList.toggle('highlight', false); + entry.setHighlight(false); } - this.container_.setAttribute('data-arch-refresh', ''); - this.container_.setAttribute('data-arch-snapshot', ''); e.stopPropagation(); e.preventDefault(); return; diff --git a/EditorEntryBase.js b/EditorEntryBase.js index 50c8ca0..b8198e8 100644 --- a/EditorEntryBase.js +++ b/EditorEntryBase.js @@ -13,6 +13,12 @@ class EditorEntryBase extends ListenUtils { this.elem_.xArchObj = this; } + serialize(base) { + base.id = this.getId(); + base.highlight = this.elem_.classList.contains('highlight'); + return base; + } + remove() { if (document.activeElement == this.elem_ || document.activeElement == document.body) { @@ -42,11 +48,31 @@ class EditorEntryBase extends ListenUtils { return this.elem_.id; } + setHighlight(highlight) { + this.elem_.classList.toggle('highlight', highlight); + for (let elem of document.getElementsByClassName('grid-' + this.getId())) { + elem.classList.toggle('highlight', highlight); + } + // Do NOT refresh: this bypasses the rendering pipeline + this.elem_.setAttribute('data-arch-snapshot', ''); + } + + toggleHighlight() { + this.setHighlight(!this.elem_.classList.contains('highlight')); + } + onElemFocus() { this.elem_.scrollIntoView({block: 'nearest'}); } - onKeyDown() { + onKeyDown(e) { + switch (e.key) { + case ' ': + this.toggleHighlight(); + e.stopPropagation(); + e.preventDefault(); + break; + } } afterDomAdd() { diff --git a/EditorGroup.js b/EditorGroup.js index 7c01e08..02adda0 100644 --- a/EditorGroup.js +++ b/EditorGroup.js @@ -29,13 +29,11 @@ class EditorGroup extends EditorEntryBase { } serialize() { - return { + return super.serialize({ type: 'group', - id: this.getId(), label: this.getLabel(), members: this.nodes_.serialize(EditorNode), - highlight: this.elem_.classList.contains('highlight'), - }; + }); } getNodes() { @@ -57,10 +55,6 @@ class EditorGroup extends EditorEntryBase { } } - setHighlight(highlight) { - this.elem_.classList.toggle('highlight', highlight); - } - onKeyDown(e) { super.onKeyDown(e); @@ -72,14 +66,6 @@ class EditorGroup extends EditorEntryBase { e.stopPropagation(); e.preventDefault(); break; - - case ' ': - this.elem_.classList.toggle('highlight'); - this.elem_.setAttribute('data-arch-refresh', ''); - this.elem_.setAttribute('data-arch-snapshot', ''); - e.stopPropagation(); - e.preventDefault(); - break; } } diff --git a/EditorHelp.js b/EditorHelp.js index 4044b67..a357da5 100644 --- a/EditorHelp.js +++ b/EditorHelp.js @@ -117,10 +117,9 @@ class EditorHelp extends EditorEntryBase { } serialize() { - return { + return super.serialize({ type: 'help', - id: this.getId(), - }; + }); } static unserialize(ser) { diff --git a/EditorInputBase.js b/EditorInputBase.js index 51d4ef7..28a7a3e 100644 --- a/EditorInputBase.js +++ b/EditorInputBase.js @@ -21,7 +21,7 @@ class EditorInputBase extends EditorEntryBase { } serialize(base) { - base.id = this.getId(); + super.serialize(base); base.label = this.getLabel(); return base; } @@ -41,7 +41,7 @@ class EditorInputBase extends EditorEntryBase { } onInput() { - this.elem_.setAttribute('data-arch-refresh', ''); + this.elem_.setAttribute('data-arch-render', ''); } onBlur() { diff --git a/EditorLabel.js b/EditorLabel.js index cce007f..b808693 100644 --- a/EditorLabel.js +++ b/EditorLabel.js @@ -9,25 +9,13 @@ class EditorLabel extends EditorInputBase { serialize() { return super.serialize({ type: 'label', - id: this.getId(), }); } - onKeyDown(e) { - super.onKeyDown(e); - - switch (e.key) { - case ' ': - // We don't support highlighting, but stop propagation - e.stopPropagation(); - e.preventDefault(); - break; - } - } - static unserialize(ser) { let label = new EditorLabel(ser.id); label.setLabel(ser.label); + label.setHighlight(ser.highlight); return label.getElement(); } } diff --git a/EditorLink.js b/EditorLink.js index b57c058..8299e4d 100644 --- a/EditorLink.js +++ b/EditorLink.js @@ -26,14 +26,12 @@ class EditorLink extends EditorEntryBase { } serialize() { - return { + return super.serialize({ type: 'link', - id: this.getId(), label: this.getLabel(), from: this.getFrom().serialize(), to: this.getTo().serialize(), - highlight: this.elem_.classList.contains('highlight'), - }; + }); } getFrom() { @@ -59,10 +57,6 @@ class EditorLink extends EditorEntryBase { } } - setHighlight(highlight) { - this.elem_.classList.toggle('highlight', highlight); - } - flip() { let entries = this.nodes_.getEntries(EditorNode); let fromElem = entries[0].getElement(); @@ -91,14 +85,6 @@ class EditorLink extends EditorEntryBase { e.preventDefault(); break; - case ' ': - this.elem_.classList.toggle('highlight'); - this.elem_.setAttribute('data-arch-refresh', ''); - this.elem_.setAttribute('data-arch-snapshot', ''); - e.stopPropagation(); - e.preventDefault(); - break; - case 'f': this.flip(); e.stopPropagation(); diff --git a/EditorNode.js b/EditorNode.js index af8b7b3..ecd1f46 100644 --- a/EditorNode.js +++ b/EditorNode.js @@ -9,14 +9,9 @@ class EditorNode extends EditorInputBase { serialize() { return super.serialize({ type: 'node', - highlight: this.elem_.classList.contains('highlight'), }); } - setHighlight(highlight) { - this.elem_.classList.toggle('highlight', highlight); - } - isSoft() { // Nested nodes are presumed to be references to other nodes if they exist for (let iter = this.elem_.parentElement; iter; iter = iter.parentElement) { @@ -27,20 +22,6 @@ class EditorNode extends EditorInputBase { return false; } - onKeyDown(e) { - super.onKeyDown(e); - - switch (e.key) { - case ' ': - this.elem_.classList.toggle('highlight'); - this.elem_.setAttribute('data-arch-snapshot', ''); - this.onInput(); - e.stopPropagation(); - e.preventDefault(); - break; - } - } - static unserialize(ser) { let node = new EditorNode(ser.id); node.setLabel(ser.label); diff --git a/GraphGroup.js b/GraphGroup.js index 712b6f2..049a2f0 100644 --- a/GraphGroup.js +++ b/GraphGroup.js @@ -28,7 +28,6 @@ class GraphGroup { let group = new GraphGroup(); group.id = item.id; group.label = item.label; - group.highlight = item.highlight; group.nodeLabels = new Set(); for (let member of item.members) { if (member.label == '') { diff --git a/GraphLink.js b/GraphLink.js index c30861b..2ae3812 100644 --- a/GraphLink.js +++ b/GraphLink.js @@ -11,13 +11,11 @@ class GraphLink { to: to, id: this.id, label: this.label, - highlight: this.highlight, }); to.linksIn.push({ from: from, id: this.id, label: this.label, - highlight: this.highlight, }); } } @@ -35,7 +33,6 @@ class GraphLink { link.label = item.label; link.fromLabel = item.from.label; link.toLabel = item.to.label; - link.highlight = item.highlight; if (link.fromLabel == '' || link.toLabel == '') { return null; } diff --git a/GraphNode.js b/GraphNode.js index fa3da15..1b96a94 100644 --- a/GraphNode.js +++ b/GraphNode.js @@ -48,7 +48,6 @@ class GraphNode { node.id = item.id; node.label = item.label; node.soft = soft; - node.highlight = item.highlight; return node; } } diff --git a/Grid.js b/Grid.js index 75c550d..4147887 100644 --- a/Grid.js +++ b/Grid.js @@ -22,7 +22,7 @@ class Grid { break; case 'arrow': - this.drawArrow(step.id, step.pos, step.cls, step.highlight); + this.drawArrow(step.id, step.pos, step.cls); break; case 'graphLabel': @@ -30,12 +30,11 @@ class Grid { break; case 'group': - this.drawGroup(step.id, step.min, step.max, step.label, - step.highlight); + this.drawGroup(step.id, step.min, step.max, step.label); break; case 'line': - this.drawLine(step.id, step.pos, step.cls, step.highlight); + this.drawLine(step.id, step.pos, step.cls); break; case 'linkLabel': @@ -43,7 +42,7 @@ class Grid { break; case 'node': - this.drawNode(step.id, step.label, step.pos, step.highlight); + this.drawNode(step.id, step.label, step.pos); break; } } @@ -60,14 +59,14 @@ class Grid { size[0] + ')))'; } - drawArrow(id, pos, cls, highlight) { + drawArrow(id, pos, cls) { let svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); this.container_.appendChild(svg); svg.classList.add('gridArrow'); svg.classList.add('grid-' + id); + this.maybeHighlight(svg, id); svg.style.gridColumn = pos[0] + 1; svg.style.gridRow = pos[1] + 1; - svg.classList.toggle('highlight', highlight); let use = document.createElementNS('http://www.w3.org/2000/svg', 'use'); svg.appendChild(use); @@ -79,26 +78,29 @@ class Grid { this.container_.appendChild(elem); elem.classList.add('gridGraphLabel'); elem.classList.add('grid-' + id); + this.maybeHighlight(elem, id); elem.style.gridColumn = (min[0] + 1) + ' / ' + (max[0] + 2); elem.style.gridRow = (min[1] + 1) + ' / ' + (max[1] + 2); elem.innerText = label; this.toSize_.push(elem); } - drawGroup(id, min, max, label, highlight) { + drawGroup(id, min, max, label) { let group = document.createElement('div'); this.container_.appendChild(group); group.classList.add('gridGroup'); group.classList.add('grid-' + id); + this.maybeHighlight(group, id); group.style.gridColumn = (min[0] + 1) + ' / ' + (max[0] + 2); group.style.gridRow = (min[1] + 1) + ' / ' + (max[1] + 2); - group.classList.toggle('highlight', highlight); if (label != '') { + // TODO: split this into its own draw step type let labelNode = document.createElement('div'); this.container_.appendChild(labelNode); labelNode.classList.add('gridGroupLabel'); labelNode.classList.add('grid-' + id); + this.maybeHighlight(labelNode, id); labelNode.innerText = label; labelNode.style.gridColumn = (min[0] + 1) + ' / ' + (max[0] + 2); labelNode.style.gridRow = min[1] + 1; @@ -106,14 +108,14 @@ class Grid { } } - drawLine(id, pos, cls, highlight) { + drawLine(id, pos, cls) { let svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); this.container_.appendChild(svg); svg.classList.add('gridLines'); svg.classList.add('grid-' + id); + this.maybeHighlight(svg, id); svg.style.gridColumn = pos[0] + 1; svg.style.gridRow = pos[1] + 1; - svg.classList.toggle('highlight', highlight); let use = document.createElementNS('http://www.w3.org/2000/svg', 'use'); svg.appendChild(use); @@ -125,27 +127,35 @@ class Grid { this.container_.appendChild(elem); elem.classList.add('gridLinkLabel'); elem.classList.add('grid-' + id); + this.maybeHighlight(elem, id); elem.innerText = label; elem.style.gridColumn = pos[0] + 1; elem.style.gridRow = pos[1] + 1; this.toSize_.push(elem); } - drawNode(id, label, pos, highlight) { + drawNode(id, label, pos) { let node = document.createElement('div'); this.container_.appendChild(node); node.classList.add('gridNode'); node.classList.add('grid-' + id); + this.maybeHighlight(node, id); node.innerText = label; - node.classList.toggle('highlight', highlight); node.style.gridColumn = pos[0] + 1; node.style.gridRow = pos[1] + 1; this.toSize_.push(node); + } - node.addEventListener('click', () => { + maybeHighlight(elem, id) { + let source = document.getElementById(id); + if (!source) { + return; + } + elem.classList.toggle('highlight', source.classList.contains('highlight')); + + elem.addEventListener('click', () => { let editorElem = document.getElementById(id); - editorElem.classList.toggle('highlight'); - editorElem.setAttribute('data-arch-refresh', ''); + editorElem.xArchObj.toggleHighlight(); }); } diff --git a/Layout.js b/Layout.js index 3ec4b4e..3319c58 100644 --- a/Layout.js +++ b/Layout.js @@ -227,7 +227,6 @@ class Layout { to: link.to, id: link.id, label: link.label, - highlight: link.highlight, }); } } @@ -240,7 +239,6 @@ class Layout { for (let link of links) { this.links_.push( new LayoutLink(link.from, link.to, link.id, link.label, - link.highlight, this.nodesByPos_, this.linksByPos_, this.labelsByPos_)); } diff --git a/LayoutGroup.js b/LayoutGroup.js index 621921a..a734bbc 100644 --- a/LayoutGroup.js +++ b/LayoutGroup.js @@ -92,7 +92,6 @@ class LayoutGroup { max: max, id: this.graphGroup_.id, label: this.graphGroup_.label, - highlight: this.graphGroup_.highlight, }; } diff --git a/LayoutLink.js b/LayoutLink.js index 49189ec..91d43ee 100644 --- a/LayoutLink.js +++ b/LayoutLink.js @@ -1,10 +1,9 @@ class LayoutLink { - constructor(from, to, id, label, highlight, nodesByPos, linksByPos, labelsByPos) { + constructor(from, to, id, label, nodesByPos, linksByPos, labelsByPos) { this.from_ = from; this.to_ = to; this.id_ = id; this.label_ = label; - this.highlight_ = highlight; this.nodesByPos_ = nodesByPos; this.linksByPos_ = linksByPos; this.labelsByPos_ = labelsByPos; @@ -229,7 +228,6 @@ class LayoutLink { id: this.id_, pos: Array.from(this.path[0]), cls: 's' + this.getOutPoint(this.path[0], this.path[1]), - highlight: this.highlight_, }); for (let i = 1; i < this.path.length - 1; ++i) { @@ -240,7 +238,6 @@ class LayoutLink { id: this.id_, pos: Array.from(this.path[i]), cls: `i${inPoint}o${outPoint}`, - highlight: this.highlight_, }); } @@ -252,7 +249,6 @@ class LayoutLink { id: this.id_, pos: Array.from(this.path[this.path.length - 1]), cls: 's' + endInPoint, - highlight: this.highlight_, }); steps.push({ @@ -260,7 +256,6 @@ class LayoutLink { id: this.id_, pos: Array.from(this.path[this.path.length - 1]), cls: 'a' + endInPoint, - highlight: this.highlight_, }); if (this.labelPos_) { diff --git a/LayoutNode.js b/LayoutNode.js index 371009a..600e5e8 100644 --- a/LayoutNode.js +++ b/LayoutNode.js @@ -21,7 +21,6 @@ class LayoutNode { to: nodesByGraphNode.get(link.to), id: link.id, label: link.label, - highlight: link.highlight, }); } } @@ -161,7 +160,6 @@ class LayoutNode { pos: this.pos, id: this.graphNode_.id, label: this.graphNode_.label, - highlight: this.graphNode_.highlight, }; } } diff --git a/architype.css b/architype.css index 4044915..bba81f0 100644 --- a/architype.css +++ b/architype.css @@ -141,6 +141,14 @@ body { background-color: var(--label); } +.editor li.label.highlight { + background: repeating-linear-gradient( + -45deg, + transparent 0 10px, + rgba(255,0,0,0.3) 10px 20px + ), var(--label); +} + .editor li.help { padding: 10px; font-size: 16px; @@ -260,6 +268,10 @@ body { text-align: center; } +.gridGraphLabel.highlight { + color: var(--focus); +} + .gridGroup { width: 100%; height: 100%; @@ -283,14 +295,12 @@ body { overflow: hidden; overflow-wrap: anywhere; z-index: 1; - pointer-events: none; } .gridLines { width: 100%; height: 100%; z-index: 2; - pointer-events: none; --line-color: var(--line); } @@ -307,7 +317,6 @@ body { z-index: 3; border-radius: 4px; padding: 3px; - pointer-events: none; } .gridArrow {