Skip to content

Commit 3d28e1f

Browse files
committed
fix(?) arrow constraint issue
1 parent 413a3f3 commit 3d28e1f

File tree

5 files changed

+128
-23
lines changed

5 files changed

+128
-23
lines changed

css/style.css

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,30 @@ header > button:hover {
178178
opacity:0.5;
179179
}
180180

181+
body > #tree-svg {
182+
box-shadow: 0 0 1em #aaa;
183+
background:hsl(5,5%,98%);
184+
position:fixed;
185+
bottom:0;
186+
font-size:14px;
187+
height:250px;
188+
max-height:40vh;
189+
}
190+
body > #tree-svg.hidden {
191+
display:none;
192+
}
193+
body > #tree-svg #tree-close {
194+
display:inline-block;
195+
}
196+
#tree-close {
197+
display:none;
198+
}
199+
#tree-popout, #tree-close {
200+
font-size: 0.9em;
201+
text-decoration: underline;
202+
cursor: pointer;
203+
fill: steelblue;
204+
}
181205

182206
#tooltip {
183207
width:175px;

index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,12 @@
3636
</header>
3737
<div class="page active" id="taxonomy"></div>
3838
<div class="page" id="tree">
39-
<svg>
4039
</div>
4140
<div class="page" id="graph"></div>
4241
<div class="page" id="options">
4342
<p><input type="checkbox" data-option="syntax"> Show syntax tree</p>
4443
<p><input type="checkbox" data-option="links"> Show links when moving words</p>
44+
<p><input type="checkbox" data-option="tree"> Open tree view in modal</p>
4545
</div>
4646
</div>
4747
</div>

js/components/link.js

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ class Link {
7979
svg.fire('link-label-right-click', { object: this, type: 'text', event: e });
8080
};
8181
text.click((e) => svg.fire('link-label-edit', { object: this, text, event: e }));
82+
text.dblclick((e) => svg.fire('build-tree', { object: this, event: e }));
8283
});
8384

8485
this.line = this.svg.path()
@@ -116,10 +117,17 @@ class Link {
116117
draggedHandle.offset += dx;
117118
let anchor = draggedHandle.anchor;
118119
if (anchor instanceof Link) {
119-
let handles = anchor.handles.map(h => h.x).sort();
120+
let handles = anchor.handles
121+
.filter(h => h.anchor.row.idx === anchor.handles[0].anchor.row.idx)
122+
.sort((a,b) => a.x - b.x);
123+
124+
let min = handles[0].x;
125+
let max = handles[handles.length - 1].x;
126+
if (handles.length < anchor.handles.length) {
127+
max = this.mainSVG.width();
128+
}
120129
let cx = draggedHandle.anchor.cx;
121-
122-
draggedHandle.offset = Math.min(handles[handles.length - 1] - cx, Math.max(handles[0] - cx, draggedHandle.offset));
130+
draggedHandle.offset = Math.min(max - cx, Math.max(min - cx, draggedHandle.offset));
123131
}
124132
else {
125133
let halfWidth = anchor.boxWidth / 2;
@@ -134,6 +142,25 @@ class Link {
134142
? Math.max(-halfWidth + 3, draggedHandle.offset)
135143
: Math.min(halfWidth - 3, draggedHandle.offset);
136144
}
145+
146+
// also constrain links above this link
147+
let handles = this.handles
148+
.filter(h => h.anchor.row.idx === this.handles[0].anchor.row.idx)
149+
.sort((a, b) => a.x - b.x);
150+
151+
let min = handles[0].x;
152+
let max = handles[handles.length - 1].x;
153+
if (handles.length < this.handles.length) {
154+
max = this.mainSVG.width();
155+
}
156+
let cx = this.cx;
157+
this.links.forEach(link => {
158+
link.handles.forEach(h => {
159+
if (h.anchor === this) {
160+
h.offset = Math.min(max - cx, Math.max(min - cx, h.offset));
161+
}
162+
});
163+
});
137164
this.draw(draggedHandle.anchor);
138165
}
139166
})

js/main.js

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ const Main = (function() {
1515
let tree = {};
1616
let options = {
1717
showSyntax: false,
18-
showLinksOnMove: false
18+
showLinksOnMove: false,
19+
showTreeInModal: false
1920
};
2021

2122
//--------------------------------
@@ -35,7 +36,7 @@ const Main = (function() {
3536
rm = new RowManager(svg);
3637
lm = new LabelManager(svg);
3738
tm = new Taxonomy('taxonomy');
38-
tree = new TreeLayout(document.querySelector('#tree > svg'));
39+
tree = new TreeLayout('#tree', svg);
3940

4041
// load and render initial dataset by default
4142
changeDataset(2);
@@ -75,8 +76,19 @@ const Main = (function() {
7576
});
7677

7778
svg.on('build-tree', function(e) {
78-
setActiveTab('tree');
79-
tree.graph(e.detail.object);
79+
tree.svg.classed('hidden', false);
80+
if (tree.isInModal) {
81+
setActiveTab('tree');
82+
}
83+
else {
84+
setActiveTab(null);
85+
}
86+
if (e.detail) {
87+
tree.graph(e.detail.object);
88+
}
89+
else {
90+
tree.resize();
91+
}
8092
});
8193

8294
// window event listeners
@@ -107,19 +119,28 @@ const Main = (function() {
107119
case 'links':
108120
options.showLinksOnMove = this.checked;
109121
break;
122+
case 'tree':
123+
options.showTreeInModal = this.checked;
124+
// document.querySelector('.tab[data-id="tree"]').style.display = this.checked ? 'inline-block' : 'none';
125+
break;
110126
default: ;
111127
}
112128
};
113129
});
114130

115131
function setActiveTab(pageId, modalId="modal") {
116132
let m = document.getElementById(modalId);
117-
m.classList.add('open');
133+
if (pageId == null) {
134+
m.classList.remove('open');
135+
}
136+
else {
137+
m.classList.add('open');
118138

119-
m.querySelector('.tab.active').classList.remove('active');
120-
m.querySelector('.page.active').classList.remove('active');
121-
m.querySelector('header span[data-id="' + pageId + '"]').classList.add('active');
122-
document.getElementById(pageId).classList.add('active');
139+
m.querySelector('.tab.active').classList.remove('active');
140+
m.querySelector('.page.active').classList.remove('active');
141+
m.querySelector('header span[data-id="' + pageId + '"]').classList.add('active');
142+
document.getElementById(pageId).classList.add('active');
143+
}
123144
}
124145

125146
let modalHeader = document.querySelector('#modal header');

js/treelayout.js

Lines changed: 43 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ const TreeLayout = (function() {
22

33
// depth of recursion
44
let maxDepth;
5+
const rh = 50; // row height
56

67
// recursively build hierarchy from a root word or link
78
function addNode(node, depth, source) {
@@ -57,12 +58,45 @@ const TreeLayout = (function() {
5758
};
5859

5960
class TreeLayout {
60-
constructor(el) {
61+
constructor(el, mainSVG, openInModal) {
6162
// container element
62-
this.svg = d3.select(el);
63+
this.isInModal = openInModal;
64+
this.svg = openInModal ? d3.select(el) : d3.select(document.body).append('svg')
65+
.attr('id', 'tree-svg');
6366
this.draggable = this.svg.append('g');
6467
this.g = this.draggable.append('g');
6568

69+
let self = this;
70+
this.svg.append('text')
71+
.text(openInModal ? 'Show in main window' : 'Pop into modal')
72+
.attr('id', 'tree-popout')
73+
.attr('x', 15)
74+
.attr('y', 25)
75+
.on('click', function() {
76+
let node = document.getElementById('tree-svg');
77+
let parent = node.parentNode;
78+
if (parent === document.body) {
79+
d3.select(this).text('Show in main window');
80+
d3.select(el).node().appendChild(node);
81+
self.isInModal = true;
82+
}
83+
else {
84+
d3.select(this).text('Pop into modal');
85+
document.body.appendChild(node);
86+
self.isInModal = false;
87+
}
88+
mainSVG.fire('build-tree');
89+
});
90+
this.svg.append('text')
91+
.text('Close')
92+
.attr('id', 'tree-close')
93+
.attr('x', this.svg.node().getBoundingClientRect().width - 15)
94+
.attr('text-anchor', 'end')
95+
.attr('y', 25)
96+
.on('click', () => {
97+
this.svg.classed('hidden', true);
98+
});
99+
66100
// add zoom/pan events
67101
this.svg.call(d3.zoom()
68102
.scaleExtent([1 / 2, 4])
@@ -74,10 +108,12 @@ const TreeLayout = (function() {
74108
// selected words to generate graph around
75109
this.word = null;
76110
this.maxDepth = 20; // default value for max dist from root
111+
this.maxWidth = 0;
112+
this.layers = [];
77113
}
78114
resize() {
79-
// let bounds = this.svg.node().getBoundingClientRect();
80-
// this.g.attr('transform', `translate(${bounds.width / 2}, 30)`);// ${bounds.height / 2})`);
115+
let bounds = this.svg.node().getBoundingClientRect();
116+
this.g.attr('transform', 'translate(' + [bounds.width / 2 - this.maxWidth / 2, bounds.height / 2 - (this.layers.length - 1) * rh / 2] + ')');
81117
}
82118
clear() {
83119
this.word = null;
@@ -89,8 +125,6 @@ const TreeLayout = (function() {
89125
* Word or Link "root" nodes
90126
*/
91127
graph(selected) {
92-
this.resize();
93-
94128
maxDepth = this.maxDepth;
95129

96130
let data = [];
@@ -220,6 +254,8 @@ const TreeLayout = (function() {
220254
});
221255
}// end for
222256

257+
this.maxWidth = maxWidth;
258+
this.layers = layers;
223259

224260
let nodeSVG = this.g.selectAll('.node')
225261
.data(nodes, d => d.node);
@@ -230,8 +266,6 @@ const TreeLayout = (function() {
230266
let edgeSVG = this.g.selectAll('.edge')
231267
.data(links, d => d.source.node);
232268

233-
//layout constants
234-
const rh = 50; // row height
235269
nodeSVG.exit().remove();
236270
nodeSVG.enter().append('text')
237271
.attr('class','node')
@@ -243,8 +277,7 @@ const TreeLayout = (function() {
243277
.attr('transform', d => 'translate(' + [d.offset, d.depth * rh] + ')');
244278

245279
// resize
246-
let bounds = this.svg.node().getBoundingClientRect();
247-
this.g.attr('transform', 'translate(' + [bounds.width / 2 - maxWidth / 2, bounds.height / 2 - layers.length * rh / 2] + ')');
280+
this.resize();
248281

249282
edgeSVG.exit().remove();
250283
edgeSVG.enter().append('path')

0 commit comments

Comments
 (0)