I have a D3 text added inside a rectangle , where the value of text field is updated programtically. Now I need to expand the width based on the text value length but making sure to keep the default width as min width.
I have tried getting the new value.length and updating the d3 text width like below.
var length = //length of the updated text value
var elm = d3.selectAll("[id=s1]");
elm.attr('width', function(d) { return length; });
This works fine but when i delete the text D3 text box also shrinks all the way. I need to keep a default width and then just increase if the value is longer than that. Any idea how to do this?
You can use a ternary operator to check for a minimum value:
elm.attr('width', function() {
return length < minimumValue ? minimumValue : length;
});
Where minimumValue is, of course, your minimum value.
Related
I have an heatmap that show some data and a sparkline for each line of the heatmap.
If the user click on a row label, then the data are ordered in decreasing order, so each rect is placed in the right position.
Viceversa, if the user click on a column label.
Each react is placed in the right way but I'm not able to place the sparkline.
Here the code.
When the user click on a row label, also the path inside the svg containing the sparkline should be updated.
And then, when the user click on a column label, the svg containing the sparkline should be placed in the correct line.
To place the svg in the right place, I try to use the x and y attributes of svg. They are updated but the svg doesn't change its position. Why?
Here is a piece of code related to that:
var t = svg.transition().duration(1000);
var values = [];
var sorted;
sorted = d3.range(numRegions).sort(function(a, b) {
if(sortOrder) {
return values[b] - values[a];
}
else {
return values[a] - values[b];
}
});
t.selectAll('.rowLabel')
.attr('y', function(d, k) {
return sorted.indexOf(k) * cellSize;
});
Also, I don't know how to change the path of every sparkline svg. I could take the data and order them manually, but this is only good for the row on which the user has clicked and not for all the others.
How can I do?
The vertical and horizontal re-positioning/redrawing of those sparklines require different approaches:
Vertical adjustment
For this solution I'm using selection.sort, which:
Returns a new selection that contains a copy of each group in this selection sorted according to the compare function. After sorting, re-inserts elements to match the resulting order.
So, first, we set our selection:
var sortedSVG = d3.selectAll(".data-svg")
Then, since selection.sort deals with data, we bind the datum, which is the index of the SVG regarding your sorted array:
.datum(function(d){
return sorted.indexOf(+this.dataset.r)
})
Finally, we compare them in ascending order:
.sort(function(a,b){
return d3.ascending(a,b)
});
Have in mind that the change is immediate, not a slow and nice transition. This is because the elements are re-positioned in the DOM, and the new structure is painted immediately. For having a slow transition, you'll have to deal with HTML and CSS inside the container div (which may be worth a new specific question).
Horizontal adjustment
The issue here is getting all the relevant data from the selection:
var sel = d3.selectAll('rect[data-r=\'' + k + '\']')
.each(function() {
arr.push({value:+d3.select(this).attr('data-value'),
pos: +d3.select(this).attr('data-c')});
});
And sorting it according to data-c. After that, we map the result to a simple array:
var result = arr.sort(function(a,b){
return sorted.indexOf(a.pos) - sorted.indexOf(b.pos)
}).map(function(d){
return d.value
});
Conclusion
Here is the updated Plunker: http://next.plnkr.co/edit/85fIXWxmX0l42cHx or http://plnkr.co/edit/85fIXWxmX0l42cHx
PS: You'll need to re-position the circles as well.
I want to modify to the textbox width according to the longest line that user enters, dynamically. I initialize the width = width of my canvas but as soon as user exits editing, the width must shorten to the width of the longest line in user's text written in the textBox.
obj.on(("editing:exited"),function() {
can.setActiveObject(textBox);
var largest = Math.max.apply(Math, can.getActiveObject().__lineWidths);
can.getActiveObject().set("width",largest);
})
Problem is that each time I exit editing, the width reduces and the top line is shifted below. I just cant understand what is happening. Please help at the earliest. Thanks in advance. Please also upload a demo so that I can see the effect of your code.
Update 1 : If I change the line
can.getActiveObject().set("width",largest);
to
can.getActiveObject().set("width",(largest + 1));
It works as desired. I have no idea why this is happening.
I have found the answer to this question by some coding myself.
https://jsfiddle.net/xpntggdo/8/
To all those who might be facing a similar issue, here is the fiddle and here is the code.
textBox.on(("changed"),function(){
var actualWidth = textBox.scaleX * textBox.width;
var largest = canvas.getActiveObject().__lineWidths[0];
var tryWidth = (largest + 15) * textBox.scaleX;
canvas.getActiveObject().set("width",tryWidth);
if((textBox.left + actualWidth) >= canvas.width - 10)
{
textBox.set("width", canvas.width - left - 10)
}
canvas.renderAll();
});
textBox.on(("modified"),function(){
left = textBox.left;
canvas.renderAll();
});
I have a rectangle and a text created through D3 which are grouped together like below.
<g>
<rect></rect>
<text></text>
</g>
In a javascript function I'm expanding this rectangle's width to allow the the value of text to be displayed in one go. (If the value is too long as the text value is set dynamically). This is working fine. However even though the rectangle width is expanding depending on the text value size , D3 text element is not showing the complete text value that is added, eventually the text is moved to left and the first bit of it can't be seen.
Do I need to increase the text element's width too or what would be the solution for this?
[UPDATE]
Following is the current code.
var elms = d3.selectAll("[id=s]"); // has the rectangle and text elements
var s = d3.selectAll("[id=s]").selectAll("text"); // just the text elements
var minimumValue = 100;
// updating rectangle width
elms.attr('width', function() { return dynamic < minimumValue ? minimumValue : dynamic;});
Now I need a way to update the position of the text element when the rectangle is expanded. Hope this is more clear. I just tried manually updating 'x' property of text element that works. But can I derive this value dynamically to set it within this function?
In case someone is looking for this, was able to get the text new 'x' value by x of rectangle + half of rectangle's width (and similar for y but with the height).
Got the answer from this link (How to place and center text in an SVG rectangle)
All nvd3 examples (which I've found) look like this:
return [
{
values: sin, //values - represents the array of {x,y} data points
key: 'Sine Wave', //key - the name of the series.
color: '#ff7f0e' //color - optional: choose your own line color.
}, ...]
I want to use a function which would use different keys based on the size of the chart / drawing area.
So if I have a large drawing area I have space for the whole name Sine Wave and in small areas I'd just display sin.
Yes, I could go through the series and update the key property, but it would be easier to put all the necessary data into the object and choose on render time, which key should be used.
You can use chart.legend.key()
chart.legend.key(function(d){
// Return the key you want for series d here based on screen realestate
// FYI: The default would return d.key
});
So it could look something like this:
function setLegendKeys(d){
var width = document.body.clientWidth;
var abbreviations = {
'Sine Wave': 'sin',
'Cosine Wave': 'cos'
};
if (width < 500){
return abbreviations[d.key];
}
return d.key;
}
chart.legend.key(setLegendKeys)
See this Plunk for a live example:
http://plnkr.co/edit/lisKuZ5ivj25QhyjlQUW?p=preview
The legend currently auto-sizes according to the max width of the labels it contains, which is throwing my layout. I need to set a width or min-width. Here is my legend creation method:
public Legend CreateLegend()
{
var legend = new Legend();
legend.Enabled = true;
legend.Font = new Font("Arial", 11F);
legend.ForeColor = Color.FromArgb(102, 102, 102);
return legend;
}
It seems like anything I do which accesses the Position object for the legend (defines X an Y coords, width and height) makes the legend disappear completely.
So legend.Position.Width = 5 would make it disappear...
Okay, turns out that if you do not define all four properties of the Position object, it sets them to zero...so height was zero...so it disappeared. Awesome.