I place figures on the screen in the mxgraph editor.
How can I access the names of these shapes I placed?
For example, there are 2 ellipses and 1 square on the screen. When I press the button, I want it to say there are 2 ellipses and 1 square.
How can I do that?
The shape of the built-in shapes is defined by its style. So, you can use style to figure it out. In the editor, you can access the (raw) style using "Edit -> Style", it's a list of semicolon-separated values somewhat like trinagle;html=1; or shape=prallalelogram;whiteSpace=wrap;html=1;
To get it you just get the style of the shape with javascript:
const cells = graph.model.getChildCells(graph.getDefaultParent(), true, true);
for (const cell of cells) {
const styles = cell && cell.style && cell.style.split(';') || [];
if (styles.indexOf('ellipse') >= 0) {
console.log(`ellipse found: ${cell.id}`)
}
if (styles.indexOf('triangle') >= 0) {
console.log(`triangle found: ${cell.id}`)
}
}
I am uncertain if this is the best option, but it should work. For your custom shapes that you have created yourself, you can just add some property to your shape to detect the shape by that property.
Related
I'd like to have a specific coordinate area reserved for a legend in my GoJS diagram which is using a ForceDirectedLayout for node layouts. However, the legend shape doesn't affect the layout of nodes when applied as a Part, and using a placeholder Node unfortunately doesn't allow placement based on document coordinates. This leads to the legend content overlapping node content depending on how the forces are randomly applied.
How can you create a region in a ForceDirectedLayout which affects/applies forces to nodes as they're being calculated but in turn either isn't a node or is a node with a fixed position and no links?
Ideally it'd be possible to define a specific Rect in the diagram which isn't accessible for nodes, or have a Part which can apply forces.
The closest I could find to defining a specific rect is the total bounding box via the layout's boundsComputation which would just narrow the area and not allow for cutting a specific region out.
Here's a specific illustration of the challenge, where the legend will generally overlap node content since there's no force to repel the nodes:
It appears it may be possible with ForceDirectedLayout.isFixed based on the following, but it's unclear how one would go about setting a specific node position on that basis:
https://forum.nwoods.com/t/maintain-existing-graph-shape-when-appending-items/7159/6
You could try a variation of this class:
class FixedForceDirectedLayout extends go.ForceDirectedLayout {
isFixed(v) {
return v.node.category === "Fixed";
}
electricalCharge(v) {
return (v.node.category === "Fixed") ? 300 : this.defaultElectricalCharge;
}
}
You may want to fiddle with the value returned by the electricalCharge method.
To install, just set Diagram.layout to an instance of this custom ForceDirectedLayout class.
const myDiagram =
$(go.Diagram, "myDiagramDiv",
{
layout: new FixedForceDirectedLayout(),
. . .
Now where you want there to be an empty area, put a node of category "Fixed" covering that area. For example,
myDiagram.nodeTemplateMap.add("Fixed",
$(go.Node,
{ background: "whitesmoke" }, // just for demo purposes, so you can see it
new go.Binding("position", "bounds", b => go.Rect.parse(b).position),
new go.Binding("desiredSize", "bounds", b => go.Rect.parse(b).size)
));
Example model data:
myDiagram.model = new go.GraphLinksModel(
[
{ category: "Fixed", bounds: "20 20 100 100" },
. . .
I am using a c3 chart. I have a button to download it using canvg. I also have it toggle bars when legends are clicked. The download works and so do the toggles.
The only issue in the downloads is that I can either always show the legends (even if the bars they correspond to aren't being shown) or as soon as I toggle a legend the legend never appears again in the downloads (although it does on the chart itself).
What I would like is for the legends to only appear if their corresponding bars are actually being shown. I also don't want the legends to be hidden if their bar is being shown. (Legend Shown <=> Bar Shown kind of relationship)
I had issues with IE in the past so following https://github.com/c3js/c3/issues/2528 the display is 'Block'.
var string = ".c3-legend-item-hidden";//hides legends that are unselected in the download. Will KEEP them hidden even if retoggled :(
d3.selectAll(string).each(function() {
var element = this;
var computedStyle = getComputedStyle(element, null);
for (var i = 0; i < computedStyle.length; i++) {
var property = computedStyle.item(i);
element.style[property] = computedStyle.getPropertyValue(property);
}
});
//removing this section makes all legends appear permanently regardless of whether the bar does
Expected: a graph that has the correct bars and legends shown in the downloads
Actual:
(with code segment) hidden legends that do not reappear when needed
(without code segment) legends that are never hidden
Update: Just to clarify, this works when converting the graph to a downloaded svg file (adding xmlns etc.), just not when using canvg and downloading to a png file (which is what I need it to do).
Instead of using the computed style, manually set the styles you need.
This solution worked for me ('hidden' legends are slightly visible but it is now identical to my actual chart which is all I personally needed):
var string = ".c3-legend-item-hidden";
d3.selectAll(string).each(function() {
//this.style['visibility']='hidden'; // uncomment this out and it will completely hide the legend if that is what you want
this.style['opacity']= 0.15;
// set opacity as 0 to make the legend transparent to the point you can't see it (if that is what you want)
// or 0.15 to make it like the chart (as done here and in c3.css)
});
The computed styles give a lot more styles than you need and can override the styles you want.
I have multiple groups of svg elements in one viewport. I want users to click on one group which will hide the other groups and enlarge the selected group to fill the viewport.
So far I have:
var continents = $(".continents")
for (var i = 0; i < continents.length; i++) {
continents[i].addEventListener('click', function(){
$(".continents").css("display","none");
var currentContinent=this;
currentContinent.setAttribute("transform","scale(1.0)")
})
}
Where the groups are classed ".continents". But this does nothing.
Here is a jsfiddle
Is it possible to create a zoom effect or simply enlarge a selected group?
There are two issues with the code:
Not all the groups have the class .continents, so not all of the continents will hide when you do this:
`$(".continents").css("display","none");`
only Asia and Africa do have that class, so only those two will hide.
When you set the attribute transform here:
currentContinent.setAttribute("transform","scale(1.0)")
you are not only modifying the value of the scale(), but you are also overwriting/deleting the value of the translation.
How to fix these issues:
Add the class .continents to all the groups.
Update both the values of scale and translate for the continent that is clicked, and not only the scale. And this is the tricky part: those values may not be the same for all the continents. For example, for Asia, the target values will be: translate(-400,439) scale(0.032,-0.032), but those values will not work for the other continents. You need to play with different values to find the ones that will work for each particular group.
You can see it working on this JSFiddle (notice that only Asia will work, the other continents will be displayed outside of the picture until you adjust the translate/scale values).
To make things as generic as possible, you could store the new values in a data- attribute (e.g.: data-transform), and then update the value of the transform by using the value of that data- attribute.
You don't have class defined on all of your group elements so the click handler and css is only applied to 2 of the groups.
Also, you set all of the displays to none, and never set the display of the selected group back to inline.
The transform is no good since the paths are much larger, have an inverted y axes and are positioned absolutely, so changing the scale from 0.017, -0.017 to 1.0, 1.0 moves them far off the viewport.
JSFiddle
var prevTransform = null;
var continents = $("g");
for (var i = 0; i < continents.length; i++) {
continents[i].addEventListener('click', function () {
var currentContinent = this;
if (prevTransform == null) {
console.log("Click!");
$("g").css("display", "none");
prevTransform = currentContinent.getAttribute("transform");
currentContinent.setAttribute("transform", "translate(-20,220) scale(0.025, -0.025)");
$(currentContinent).css("display", "inline");
} else {
currentContinent.setAttribute("transform", prevTransform);
prevTransform = null;
$("g").css("display", "inline");
}
});
}
In this example, South America works best, the others move too far up and right. Australia moves out of view.
I want to print something like this
each bubble u see is a "li" element
so i have made float=left so that i can see them horizontally. I am not able to understand, how should i display two different colors dynamically.
Eg: If it is 60% and 40%, then I need to show more blue bubbles and less orange ones and vice versa.
Can't offer specifics without some specific code of yours, but: use the modulo operator (%) together with > or <.
For instance:
var idx = $('ul li').index();
to get the index, and then
var color = (idx % 11 < 6) ? "blue" : "orange";
to pick the color.
I have a chart created with Birt and I want to add some label on it to specify the 4 regions delimited by the 2 red markers (see image below), one label in each quadrant (centered if possible).
I am looking for a solution to do that, directly using birt chart editor or by using a javascript (like I have done for the red markers).
In order to dynamically center some type of label in each quadrant of the graph, you'll have to have some way of calculating the coordinates. Of course, I'm not really familiar with Birt and I'm making the assumption that the graph's red markers will vary.
Anyway, assuming that you can get the coordinates, you could write a function that would dynamically generate the label based on a couple of parameters:
function generateLabel(sContent, iXoffset, iYoffset) {
var eLabel = document.createElement('span');
eLabel.appendChild(document.createTextNode(sContent));
var eDivision = document.createElement('div');
eDivision.appendChild(eLabel);
eDivision.style.left = iXoffset + 'px';
eDivision.style.top = iYoffset + 'px';
// include other styles here...
return eDivision;
}
and then from there, you can call this function with the set label content and offset coordinates:
var eQuadrantOneLabel = generateLabel('Quadrant One', 10, 25);
// and so on...
Then just add the element to the graph's container (assuming that is has an id of, say, graph):
var eGraphContainer = document.getElementById('graph');
eGraphContainer.appendChild(eQuadrantOneLabel);
// and so on for each label...