The code presented at the bottom allows the user to build a custom drag/drop hierarchy tree, as shown in this image (note that all code is presented at the bottom of this post, there is no code embedded in the images):
CSV file download after clicking "Save built tree":
How can the code be modified so that when the user clicks the "Save built tree" button, the tree is saved under the "Saved trees:" node and assuming the user saves it as "Tree 1", and it renders and re-renders for the current session and even future sessions, as mocked-up in this image?
I'll deal with deleting saved trees and activating saved trees so they're rendered in that DF box later... Here's the code:
library(jsTreeR)
library(shiny)
nodes <- list(
list(
text = "Menu",
state = list(opened = TRUE),
children = list(
list(text = "Dog",type = "moveable"),
list(text = "Cat",type = "moveable")
)
),
list(
text = "Drag from menu to build tree:",
type = "target",
state = list(opened = TRUE)
),
list(
text = "Saved trees:",
state = list(opened = TRUE)
)
)
checkCallback <- JS("
function(operation, node, parent, position, more) {
if(operation === 'copy_node') {
var n = parent.children.length;
if(position !== n || parent.id === '#' || node.parent !== 'j1_1' || parent.type !== 'target') {
return false;
}
}
if(operation === 'delete_node') {
if (node.type == 'item'){
text = node.text;
Shiny.setInputValue('deletion', text, {priority: 'event'});
} else if (node.type == 'subitem'){
text = parent.text;
Shiny.setInputValue('deletionsub', text, {priority: 'event'});
}
}
return true;
}"
)
customMenu <- JS("
function customMenu(node) {
var tree = $('#mytree').jstree(true);
var items = {
'delete' : {
'label' : 'Delete',
'action' : function (obj) {
parent = tree.get_node(node.parent);
nodetype = node.type;
orgid = node.orgid;
tree.delete_node(node);
},
'icon' : 'fa fa-trash'
},
};
if (node.type == 'item') {return {'delete':items.delete}}
else return {}
}
"
)
dnd <- list(
always_copy = TRUE,
inside_pos = "last",
is_draggable = JS(
"function(node) {",
" return node[0].type === 'moveable';",
"}"
)
)
mytree <- jstree(
nodes,
dragAndDrop = TRUE, dnd = dnd,
checkCallback = checkCallback,
contextMenu = list(items = customMenu),
types = list(moveable = list(), target = list())
)
script <- '
var LETTERS = ["A", "B", "C", "D", "E", "F"];
var Visited = {};
function getSuffix(orgid){
if (Object.keys(Visited).indexOf(orgid) === -1){Visited[orgid] = 0;}
else{Visited[orgid]++;}
return LETTERS[Visited[orgid]];
}
$(document).ready(function(){
$("#mytree").on("copy_node.jstree", function(e, data){
var orgid = data.original.id;
var node = data.node;
var id = node.id;
var basename= node.text;
var text = basename + " " + getSuffix(orgid);
Shiny.setInputValue("choice", text, {priority: "event"});
var instance = data.new_instance;
instance.rename_node(node, text);
node.type = "item"
node.basename = basename;
node.orgid = orgid;
var tree = $("#mytree").jstree(true);
});
});
'
ui <- fluidPage(
tags$div(class = "header", checked = NA,tags$p(tags$script(HTML(script)))),
fluidRow(
column(width = 4,jstreeOutput("mytree")),
column(width = 8,fluidRow(verbatimTextOutput("choices"),
downloadButton("download_DF", "Save built tree")
)
)
)
)
server <- function(input, output, session){
output[["mytree"]] <- renderJstree(mytree)
Choices <- reactiveVal(data.frame(choice = character(0)))
observeEvent(input[["choice"]], {Choices(rbind(Choices(), data.frame(choice = input[["choice"]])))} )
observeEvent(input[["deletion"]], {
item = input[["deletion"]]
matched = which(Choices()$choice == item)
if (length(matched)>0) Choices(Choices()[-matched, , drop = FALSE])
})
output[["choices"]] <- renderPrint({Choices()})
output$download_DF <- downloadHandler(
filename = paste("TreeDF","csv",sep="."),
content = function(file){write.csv(Choices(),file,row.names=FALSE)}
)
}
shinyApp(ui=ui, server=server)


