Here is a draft on how to add html elements according to the element being clicked. Maybe you could elaborate a bit more where you want them to be placed or if you are also fine adding other shiny elements instead (would be easier).
The idea in the code below is to add an onclick listener. Shortcut is to implement it with shinyjs
. Identifier of the elements seems to be node1
, node2
, etc.
Then there are multiple options. Giving that information back to "R/Shiny" and add shiny elements via Shiny.OnInputChanged(...)
or appending html elements via javascript.
Using Shiny.OnInputChanged(...)
is more easy, so i tried if i get the second one to work as well. An example is given below.
It was not straightforward to add the new html delements within the graph and place them in front of it. One could experiment by calibrating the css to show it in front (style="z-index: -1"
), but maybe it would be a good point to further specify the optimal solution.
(For sake of completeness, the added elements can of course be removed as well, so that only one "tooltip" is shown at a time,...)
Reproducible example:
library(DiagrammeR)
library(shiny)
library(shinyjs)
texts <- c("Great div for A", "Even better div for B")
jsCode <- paste0("
elem = document.getElementById('graphV');
var node = document.createElement('div');
var textnode = document.createTextNode('", texts,"');
node.appendChild(textnode);
elem.appendChild(node);
")
ui = shinyUI(
fluidPage(
useShinyjs(),
grVizOutput('graphV')
)
)
server = function(input, output, session) {
observe({
for(nodeNr in 1:length(jsCode)){
local({
jsToAdd <- jsCode[nodeNr]
shinyjs::onclick(paste0("node", nodeNr), runjs(jsToAdd))
})
}
})
output$graphV <- renderGrViz({
grViz( "digraph test{
A[tooltip='A word'];
B[tooltip='Another word'];
A -> B;}" )
})}
shinyApp(ui = ui, server = server)
Failed attempt to let tooltip overlay the diagram:
library(DiagrammeR)
library(shiny)
library(shinyjs)
texts <- c("Great div for A", "Even better div for B")
jsCode <- paste0("
elem = document.getElementById('node1');
var node = document.createElement('div');
var textnode = document.createTextNode('", texts,"');
node.appendChild(textnode);
node.classList.add('mystyle');
elem.appendChild(node);
")
ui = shinyUI(
fluidPage(
useShinyjs(),
tags$style("
.mystyle {
z-index: 100 !important;
background-color: coral;
font-size: 25px;
}
#node1 {
width: 50px;
z-index: unset !important;
background-color: blue;
}
"),
grVizOutput('graphV')
)
)
server = function(input, output, session) {
observe({
for(nodeNr in 1:length(jsCode)){
local({
jsToAdd <- jsCode[nodeNr]
shinyjs::onclick(paste0("node", nodeNr), runjs(jsToAdd))
})
}
})
output$graphV <- renderGrViz({
grViz( "digraph test{
A[tooltip='A word'];
B[tooltip='Another word'];
A -> B;}" )
})}
shinyApp(ui = ui, server = server)
Edit for question in comments:
For example, if you have a 40 node graph, is there a way to automatically link a node id to a node description (in the case above, the 'Great div for A')? Or will i just need to order the texts file in the order the nodes are listed in the graphviz code?
Correct. In the diagrammeR code, it seems to just counts upwards for the ids: "node1, node2, node3,...).
So i just do the same in shiny:
for(nodeNr in 1:length(jsCode)){
local({
jsToAdd <- jsCode[nodeNr]
shinyjs::onclick(paste0("node", nodeNr), runjs(jsToAdd))
})
}
As you can see for node number paste0("node", nodeNr)
the javascript code jsCode[nodeNr]
will be assigned for the click event.
And jsCode[nodeNr]
will include texts[nodeNr]
. (Note that jsCode
will be a vector of strings according to the length of texts
.