In running the below reproducible code, I'm trying to extract specific node elements from a jsTree
(using the jsTreeR
package) into a data frame. Similar to what was done in related post that used sortable
DnD instead of jstree
at
CodePudding user response:
First, unrelated to this question, I added the option inside_pos="last"
in the drag-and-drop handler:
dnd <- list(
always_copy = TRUE,
inside_pos = "last",
is_draggable = JS(
"function(node) {",
" return node[0].type === 'moveable';",
"}"
)
)
With this option, you can drop a node on the node "Drag here" and it automatically goes to the last position (see the GIF). Very convenient.
Now, for your question. This is a job for Shiny.setInputValue
. Modify the script:
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);
})
});
'
And here is the Shiny app:
ui <- fluidPage(
tags$head(tags$script(HTML(script))),
fluidRow(
column(
width = 6,
jstreeOutput("mytree")
),
column(
width = 6,
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"]])
)
)
})
output[["choices"]] <- renderPrint({
choices()
})
}
EDIT: deletion
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
"}"
)
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()
})
}
Full app, with icons and the proton theme:
library(jsTreeR)
nodes <- list(
list(
text = "Menu",
state = list(opened = TRUE),
a_attr = list(style = "font-weight: bold;"),
children = list(
list(
text = "Dog",
type = "moveable",
state = list(disabled = TRUE),
icon = "fas fa-dog"
),
list(
text = "Cat",
type = "moveable",
state = list(disabled = TRUE),
icon = "fas fa-cat"
),
list(
text = "Fish",
type = "moveable",
state = list(disabled = TRUE),
icon = "fas fa-fish"
)
)
),
list(
text = ">>> Drag here <<<",
type = "target",
state = list(opened = TRUE),
a_attr = list(style = "font-weight: bold;")
)
)
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';",
"}"
)
)
customMenu <- JS(
"function customMenu(node) {",
" var tree = $('#mytree').jstree(true);", # 'mytree' is the Shiny id or the elementId
" var items = {",
" 'delete' : {",
" 'label' : 'Delete',",
" 'action' : function (obj) { tree.delete_node(node); },",
" 'icon' : 'glyphicon glyphicon-trash'",
" }",
" }",
" return items;",
"}")
mytree <- jstree(
nodes, dragAndDrop = TRUE, dnd = dnd, checkCallback = checkCallback,
types = list(moveable = list(), target = list()),
contextMenu = list(items = customMenu),
theme = "proton"
)
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)