Search code examples
rshinydrag-and-dropjstreejstreer

How to add secondary sequential numbering to elements dragged in to a hierarchy tree node using jstree?


I am trying to add secondary sequential numbering to elements dragged in to a hierarchy tree node, using the package jsTreeR, as shown in the illustration at the bottom. Below is the reproducible code used for the illustration. This sequential numbering (via letters not numbers) represents the number of times a particular element was dragged into the target node.

Any ideas for how to do this?

Note that in a related post, In R Shiny how to append to each list element the sequential number of times it appears in the list using sortable js?, the same issue was addressed but using HTML/CSS with the sortable package instead.

Reproducible code:

library(jsTreeR)
library(shiny)

nodes <- list(
  list(
    text = "Menu",
    state = list(opened = TRUE),
    children = list(
      list(
        text = "Mickey",
        type = "moveable",
        state = list(disabled = TRUE)
      ),
      list(
        text = "Mouse",
        type = "moveable",
        state = list(disabled = TRUE)
      )
    )
  ),
  list(
    text = ">>> Drag here <<<",
    type = "target",
    state = list(opened = TRUE)
    )
)

checkCallback <- JS(
  "function(operation, node, parent, position, more) { ",
  "  if(operation === 'copy_node') {",
  "    if(parent.id === '#' || node.parent !== 'j1_1' || parent.type !== 'target') {",
  "      return false;", # prevent moving an item above or below the root
  "    }",               # and moving inside an item except a 'target' item
  "  }",
  "  if(operation === 'delete_node') {",
  "    Shiny.setInputValue('deletion', position + 1);",
  "  }",
  "  return true;",      # allow everything else
  "}"
)

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,
  types = list(moveable = list(), target = list())
)

script <- '
$(document).ready(function(){
  $("#mytree").on("copy_node.jstree", function(e, data){
    var instance = data.new_instance;
    var node = data.node;
    var id = node.id;
    var index = $("#"+id).index() + 1;
    var text = index + ". " + node.text;
    Shiny.setInputValue("choice", text);
    instance.rename_node(node, text);
  });
});
'

library(shiny)
ui <- fluidPage(
  tags$head(tags$script(HTML(script))),
  fluidRow(
    column(width = 4,jstreeOutput("mytree")),
    column(width = 8,verbatimTextOutput("choices"))
  )
)

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"]], {Choices(Choices()[-input[["deletion"]], , drop = FALSE])})
  output[["choices"]] <- renderPrint({Choices()})
}

shinyApp(ui, server)

Illustration:

enter image description here


Solution

  • script <- '
    $(document).ready(function(){
      var LETTERS = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"];
      var Visited = {};
      $("#mytree").on("copy_node.jstree", function(e, data){
        var oldid = data.original.id;
        var visited = Object.keys(Visited);
        if(visited.indexOf(oldid) === -1){
          Visited[oldid] = 0;
        }else{
          Visited[oldid]++;
        }
        var letter = LETTERS[Visited[oldid]];
        var node = data.node;
        var id = node.id;
        var index = $("#"+id).index() + 1;
        var text = index + ". " + node.text + " " + letter;
        Shiny.setInputValue("choice", text);
        var instance = data.new_instance;
        instance.rename_node(node, text);
      });
    });
    '