How to interactively add nodes to a hierarchy tree rendered using jsTreeR?

Viewed 23

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):

enter image description here

CSV file download after clicking "Save built tree":

enter image description here

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?

enter image description here

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)
0 Answers
Related