Git Product home page Git Product logo

sortable's Introduction

sortable

CRAN version CRAN RStudio mirror downloads R build status sortable downloads per month Codecov test coverage Lifecycle: maturing RStudio Community: Teaching RStudio Ask a question: sortable

The sortable package enables drag-and-drop behaviour in your Shiny apps. It does this by exposing the functionality of the SortableJS JavaScript library as an htmlwidget in R, so you can use this in Shiny apps and widgets, learnr tutorials as well as R Markdown. In addition, provides a custom learnr question type - question_rank() that allows ranking questions with drag-and-drop.

Installation

You can install the released version of sortable from CRAN with:

install.packages("sortable")

And the development version from GitHub with:

# install.packages("remotes")
remotes::install_github("rstudio/sortable")

Examples

Rank list

You can create a drag-and-drop input object in Shiny, using the rank_list() function.

#> Warning in file(con, "r"): file("") only supports open = "w+" and open = "w+b":
#> using the former
#> Warning in knitr::read_chunk(system.file("shiny-examples/rank_list/app.R", :
#> code is empty

Bucket list

With a bucket list you can have more than one rank lists in a single object. This can be useful for bucketing tasks, e.g. asking your students to classify objects into multiple categories.

#> Warning in file(con, "r"): file("") only supports open = "w+" and open = "w+b":
#> using the former
#> Warning in knitr::read_chunk(system.file("shiny-examples/bucket_list/app.R", :
#> code is empty

Add drag-and-drop to any HTML element

You can also use sortable_js() to drag and drop other widgets:

library(DiagrammeR)
library(htmltools)

html_print(tagList(
  tags$p("You can drag and drop the diagrams to switch order:"),
  tags$div(
    id = "aUniqueId",
    tags$div(
      style = "border: solid 0.2em gray; float:left; margin: 5px",
      mermaid("graph LR; S[SortableJS] -->|sortable| R ",
              height = 250, width = 300)
    ),
    tags$div(
      style = "border: solid 0.2em gray; float:left; margin: 5px",
      mermaid("graph TD; JavaScript -->|htmlwidgets| R ",
              height = 250, width = 150)
    )
  ),
  sortable_js("aUniqueId") # the CSS id
))

Related work

I learnt about the following related work after starting on sortable:

  • The esquisse package:

    “The purpose of this add-in is to let you explore your data quickly to extract the information they hold. You can only create simple plots, you won’t be able to use custom scales and all the power of ggplot2.”

  • There is also the shinyjqui package:

    “An R wrapper for jQuery UI javascript library. It allows user to easily add interactions and animation effects to a shiny app.”

  • The shinyDND package:

    Adds functionality to create drag and drop div elements in shiny.

sortable's People

Contributors

andrie avatar cderv avatar ocstringham avatar schloerke avatar timelyportfolio avatar wurli avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

sortable's Issues

Make Shiny input widget initialize before `onSort` fires

Related to #31 , the input$my_input_id reference in Shiny initializes as NULL. The values are inaccessible until onSort is called at the first client interaction.

In most cases this will not be a problem, but it could potentially cause some issues in downstream reactivity if the "current state" of the DOM is desired before onSort has fired. Would be nice if the client fired the onSort callback (or had an onInitialize callback) or something of that nature whenever the client finishes initializing.

Split parsons() into a separate package

Proposal: Make separate package for sortable and parsons.

  • sortable will include HTML widget, as well as rank_list and a custom learnr question type question_rank(). Thus sortable can still be the poster child for custom question types.

  • parsons will include the parsons questions.

This will allow us to:

  • Get to CRAN rapidly with sortable, and also keep this package fairly light-weight.

  • Allow us to experiment with the API for parsons(), and take additional dependencies, including on the grader package.

cc @schloerke

Change options

Hi,
I would like to disable items of bucket_list when a condition is TRUE.
I tried to find the variable in the JS side via the debugging tool on my browser, but I'm lost.
What would be the proper way to do it?
Best

Need a way to update the labels in rank_list

For my app, I need a rank_list where the labels are determined at run-time. However, it only seems to be able to handle a character vector that is defined before the app starts.

Is there a way to update the labels of a rank_list?

onSort not capturing changes in order when new elements are inserted with insertUI

When I click an action button to insert an element into a div, I want the order of the elements to be updated using sortable_options(onSort = sortable_js_capture_input(input_id = "selected"), but this is not the case now. Please see the following reproducible example. You'll notice that the order is only available inside input$selected after moving an element, not inserting with insertUI.

library(shiny)
library(sortable)

ui <- fluidPage(
  actionButton("add", "add"),
  tags$ul(id = "lst"),
  verbatimTextOutput("text"),
  sortable_js(css_id = "lst",  options = sortable_options(
    onSort = sortable_js_capture_input(input_id = "selected"),
    onLoad = sortable_js_capture_input(input_id = "selected")
  ))
)

server <- function(input, output, session) {
  
  observeEvent(input$add, {
    insertUI(
      selector = "#lst",
      where = "beforeEnd",
      ui = tags$li(paste0("test", input$add))
    )
  })
  
  output$text <- renderText({
    req(input$selected)
    input$selected
  })
  
}

shinyApp(ui, server)

Updating UI elements selection in sortable rank list

Hi!

I am hoping someone can help me with my shiny question/problem below. You will see that we basic have 2 sets. We have a change_radio which when clicked changes the selection made in rb based on a updateSelectInput.

I want to do exactly the same thing but for a rank_list from the sortable package. When you click on change_bucket it should update the selection in the end_bucket. So lets say if clicked it should automatically add "ccc" to that bucket.

I looked at the documentation for sortable but didn't find a similar function like updateSelectInput. Any idea how I would go about doing this?


library(shiny)
library(sortable)

ui <- fluidPage(
  
  
  
  splitLayout(
  radioButtons("rb", "Choose one:",
               choiceNames = list(
                 icon("calendar"),
                 HTML("<p style='color:red;'>Red Text</p>"),
                 "Normal text"
               ),
               choiceValues = list(
                 "icon", "html", "text"
               ),
               selected = "text"
               ),
  textOutput("txt"),
  actionButton("change_radio","Change radio button selection")
),
bucket_list(
  header = "This is a bucket list. You can drag items between the lists.",
  add_rank_list(
    input_id = "start_bucket",
    text = "Drag from here",
    labels = c("a", "bb", "ccc")
  ),
  add_rank_list(
    input_id = "end_bucket",
    text = "to here",
    labels = NULL
  )
),


actionButton("change_bucket", "Change bucket selection")
)


server <- function(input, output, session) {
  output$txt <- renderText({
    paste("You chose", input$rb)
  })
  
  observeEvent(input$change_radio,{
    updateSelectInput(session, "rb", selected = "html")
    
  })
  
  observeEvent(input$change_bucket,{
    # browser()
    # updateSelectizeInput(session,"end_bucket",choices = c("a"), label = c("a"))
    # 
    # 
    # updateSelectInput(session,"end_bucket",choices = c("a"), label = c("a"))
  })
  
}

shinyApp(ui, server)

update rank_list labels

Hi!

I have a bucket list with 3 rank_list. The first of them is connected to the last two, so we move the labels from the first to the second two. When I move one of the values ​​from the first list, this value still remains in the list ... is there any way I can update the list values ​​from the server?

I know updateXXXButton functions exist in other packages, but I haven't found it in this one. I'd appreciate your help.

Thank you very much.

Make dynamic UI from data import

Hi, I need some of your help to build dynamic UI from data import in my Shiny App. I am looking at to modify your drag_vars_to_plot example for taking new data from csv file import so that the variables input can react dynamically for any data I import into the App. However, after I tried it and met a problem that the variables in the UI cannot be seperated to individual ones.
2020-03-06_131140

below is my code:

---- shiny-drag-vars-to-plot -------------------------------------------

Example shiny app to create a plot from sortable inputs

library(shiny)
library(htmlwidgets)
library(sortable)

Must be executed BEFORE rgl is loaded on headless devices.

options(rgl.useNULL=TRUE)

colnames_to_tags <- function(df){
lapply(
colnames(df),
function(co) {
tag(
"p",
list(
class = class(df[, co]),
tags$span(class = "glyphicon glyphicon-move"),
tags$strong(co)
)
)
}
)
}

ui <- fluidPage(
fluidRow(
class = "panel panel-heading",
div(
class = "panel-heading",
h3("3D Data Visualiser"),

  # Input: Select a file ----
  fileInput("file1", "Data Import",
            multiple = FALSE,
            accept = c("text/csv",
                       "text/comma-separated-values,text/plain",
                       ".csv"))
),
fluidRow(
  class = "panel-body",
  column(
    width = 3,
    
    htmlOutput("sort1")
    
  ),
  column(
    width = 3,
    # analyse as x
    tags$div(
      class = "panel panel-default",
      tags$div(
        class = "panel-heading",
        tags$span(class = "glyphicon glyphicon-stats"),
        "Analyze as x (drag here)"
      ),
      tags$div(
        class = "panel-body",
        id = "sort2"
      )
    ),
    # analyse as y
    tags$div(
      class = "panel panel-default",
      tags$div(
        class = "panel-heading",
        tags$span(class = "glyphicon glyphicon-stats"),
        "Analyze as y (drag here)"
      ),
      tags$div(
        class = "panel-body",
        id = "sort3"
      )
    ),
    
    # analyse as z
    tags$div(
      class = "panel panel-default",
      tags$div(
        class = "panel-heading",
        tags$span(class = "glyphicon glyphicon-stats"),
        "Analyze as z (drag here)"
      ),
      tags$div(
        class = "panel-body",
        id = "sort4"
      )
    )

  ),
  column(
    width = 6,
    plotOutput("plot")

  )
)

),
sortable_js(
"sort1",
options = sortable_options(
group = list(
name = "sortGroup1",
put = TRUE
),
swap = TRUE,
swapClass = "sortable-swap-highlight",
sort = FALSE,
onSort = sortable_js_capture_input("sort_vars")
)
),
sortable_js(
"sort2",
options = sortable_options(
group = list(
group = "sortGroup1",
put = htmlwidgets::JS("function (to) { return to.el.children.length < 1; }"),
pull = TRUE
),
swap = TRUE,
swapClass = "sortable-swap-highlight",
onSort = sortable_js_capture_input("sort_x")
)
),
sortable_js(
"sort3",
options = sortable_options(
group = list(
group = "sortGroup1",
put = htmlwidgets::JS("function (to) { return to.el.children.length < 1; }"),
pull = TRUE
),
swap = TRUE,
swapClass = "sortable-swap-highlight",
onSort = sortable_js_capture_input("sort_y")
)
),

sortable_js(
"sort4",
options = sortable_options(
group = list(
group = "sortGroup1",
put = htmlwidgets::JS("function (to) { return to.el.children.length < 1; }"),
pull = TRUE
),
swap = TRUE,
swapClass = "sortable-swap-highlight",
onSort = sortable_js_capture_input("sort_z")
)
)
)

server <- function(input, output) {

data <- reactive({

progress <- shiny::Progress$new()
# Make sure it closes when we exit this reactive, even if there's an error
on.exit(progress$close())

progress$set(message = "Dataset Loading..Kindly wait")

S <- input$file1


if (is.null(S))
  return(NULL)

temp = read.csv (S$datapath)

})

output$sort1 <- renderUI({

tags$div(
class = "panel panel-default",
tags$div(class = "panel-heading", "Variables"),
tags$div(
class = "panel-body",
id = "sort1",
colnames_to_tags(data())
)
)

})

output$variables <- renderPrint(input[["sort_vars"]])
output$analyse_x <- renderPrint(input[["sort_x"]])
output$analyse_y <- renderPrint(input[["sort_y"]])
output$analyse_z <- renderPrint(input[["sort_z"]])

x <- reactive({
x <- input$sort_x
if (is.character(x)) x %>% trimws()
})

y <- reactive({
input$sort_y %>% trimws()
})

z <- reactive({
input$sort_z %>% trimws()
})

output$plot <-
renderPlot({
validate(
need(x(), "Drag a variable to x"),
need(y(), "Drag a variable to y"),
need(z(), "Drag a variable to z")
)
dat <- mtcars[, c(x(), y(),z())]
names(dat) <- c("x", "y","z")
plot(dat)
})

}
shinyApp(ui, server)

sortable_js_capture_input attribute and documentation

The documentation of the sortable_js_capture_input function states:

This captures the state of a ‘sortable’ list. It will look for an ‘id’ attribute of the first child for each element...

However it appears that the function get_child_id_or_text_js_fn looks for the data-rank-id attribute.

get_child_id_or_text_js_fn <- function() {
  paste0(collapse = "\n",
    "function(child) {",
    "  return ",
    #    use child element attribute 'data-rank-id'
    "    $(child).attr('data-rank-id') || ",
    #    otherwise return the inner text of the element
    #    use inner text vs `.text()` to avoid extra white space
    "    $.trim(child.innerText);",
    "}"
  )
}

I'm unsure whether the function or the documentation should be different. Example below.

library(shiny)
library(sortable)

ui <- fluidPage(
  div(
    id = "sortable",
    div(id = 1, `data-rank-id` = "HELLO", class = "well", "Hello"),
    div(id = 2, `data-rank-id` = "WORLD", class = "well", "world")
  ),
  verbatimTextOutput("chosen"),
  sortable::sortable_js(
    "sortable", 
    options = sortable::sortable_options(
      onSort = sortable::sortable_js_capture_input("selected")
    )
  )
)

server <- function(input, output){
  output$chosen <- renderPrint(input$selected)
}

shinyApp(ui, server)

Checklist for getting to CRAN

We're getting ready for a CRAN release, hopefully before the end of June.

House keeping:

  • Migrate repo to https://github.com/rstudio/sortable
  • Update Readme
  • Enable Travis
  • Add code coverage tests
  • Publish pkgdown
  • Review documentation and help for clarity
  • Add hex logo

Add vignettes:

  • Introduction to sortable
  • Using sortable in shiny apps
  • New custom question types for learnr tutorials
  • Creating your own custom sortable widgets
  • Passing JavaScript functions to the underlying sortable JS library

Features:

  • Create parsons question type
  • Modify sortable_list to capture input immediately (not only after first sort)

Won't do:

  • Use shinytest to test functionality of the JavaScript. It seems that shinytest doesn't play well with htmlwidgets :-(

Create vignette to explain how to update a rank list

Based on an idea by @tyluRp in #66

library(shiny)

ui <- fluidPage(
  selectInput("data", "data", c("mtcars", "iris"), "mtcars"),
  uiOutput("sortable"),
  tableOutput("table")
)

server <- function(input, output, session) {
  rv <- reactiveValues(data = data.frame())
  
  observeEvent(input$data, {
    rv$data <- get(input$data)
  })
  
  observeEvent(input$sortable, {
    rv$data <- rv$data[input$sortable]
  })
  
  output$sortable <- renderUI({
    sortable::rank_list("", names(rv$data), "sortable")
  })
  
  output$table <- renderTable({
    rv$data
  })
}

shinyApp(ui, server)

Feature request: allow multiple entity select via Ctrl/Shift-click action

Thanks for this amazingly fun (and useful) 📦!

We used this within a Shiny app recently, and the response was very positive, before the data (to pick from) got longer; at this point, users find it labourious to drag tiles one-by-one, and requested ability to select multiple items at once.

Looking at sortable JS docs, there is a multi-drag plugin; hope it'll be implemented in R wrapper in the near future 🙏

silent str_squish in add_rank_list?

Thanks for this amazing widget! This works perfectly for my ap.

I did run into an issue that took me a while to debug. It appears that there is some str_squish type behavior happening behind the scenes that removes extra white spaces. Did I miss this in the documentation? Is there an argument to turn this off or on? If so, apologies for not seeing it! An example is below. If this isn't yet documented, it would be helpful to make this more obvious to the user.

Thanks again!

library(shiny)
library(sortable)

my_list <- c("   A   A   A   ",
             "  B. B. B.  ",
             " C.  C.  C. ")


shinyApp(
  ui = fluidPage(fluidRow(
    column(
      6,
      sortable::bucket_list(
        header = NULL,
        group_name = "bucket_list_group",
        sortable::add_rank_list(
          text = "Group 1",
          labels = my_list,
          input_id = "rank_list_1"
        ),
        sortable::add_rank_list(
          text = "Group 2",
          labels = NULL,
          input_id = "rank_list_2"
        )
      )
    ),
    column(
      6,
      h2("Original text"),
      verbatimTextOutput("original"),
      br(),
      h2("Group 1"),
      verbatimTextOutput("results_1"),
      br(),
      h2("Group 2"),
      
      verbatimTextOutput("results_2")
    )
  )),
  server = function(input, output) {
    output$original <-  renderPrint({
      my_list
    })
    
    output$results_1 <-  renderPrint({
      input$rank_list_1
    })
    
    output$results_2 <-  renderPrint({
      input$rank_list_2
    })
    
  }
)
#> PhantomJS not found. You can install it with webshot::install_phantomjs(). If it is installed, please make sure the phantomjs executable can be found via the PATH variable.

Shiny applications not supported in static R Markdown documents

Created on 2020-08-21 by the reprex package (v0.3.0)

onLoad fires too eagerly

Related to #34

It seems that onLoad fires more eagerly than it should? It seems to fire twice on initialization and on every onSort, at least so far as I can tell. Is this the expected behavior?

rank_list(
        text = "List 1",
        input_id = session$ns("list_1"),
        labels = data_1(),
        options = sortable_options(
          group = "my-test",
          onLoad = htmlwidgets::JS("function(evt) { console.log('hello'); }"),
          onSort = htmlwidgets::JS("function(evt) { console.log('hello - sort'); }")
        )
      )

image

feature request (or example if already available!) - draggable divs with their own classes?

Hi! I tried creating a sortable rank_list with divs since I want them to have different classes:

library(shiny)
shinyUI(fluidPage(
    column(12,
           sortable::rank_list(
               text = "Drag the items in any desired order",
               labels = c(
                   htmltools::tags$div(class="lesson", "Lesson 1"),
                   htmltools::tags$div(class="part", "Part 1"),
                   htmltools::tags$div(class="section", "Section 1")
               ),
               input_id = "order",
               options = sortable::sortable_options(multiDrag = TRUE)
           )
    )
))

shinyServer(function(input, output) {
})

My mental model was that this would create three sortable divs with the text Lesson 1, Part 1, and Section 1
but this results in the creation of THREE divs per intended div. (One with the text "div", another with the class "lesson", and another with the intended text "Lesson 1". I saw a similar closed issue but it looks like that applies a class to the entire list... is this already possible? I tried using the dev version too just in case the error was on my end. Any help appreciated!

RShiny- DT::renderDataTable works locally but disconnect from server when using reactive function (Sortable: bucketlist - drag and drop)

Hi All,

I am facing issues with DT::renderDataTable with reactive function (which is used to select Drag and Drop columns as and when Business team want to filter selected columns to show on UI). It works locally on console but disconnect when I am running on RServer using the web link at the point when it was trying to populate dataframe on UI.

I have checked the version of sortable package on server and whats loaded in by code and is same, so this doesn't looks to be an issue.

If anyone has any idea to fix this issue. I appreciate your responses.

Create a "matching" learnR question type

Does a matching question type exist yet?

I have made matching questions with question_rank using a bulleted list in the text argument, but I think the vertical arrangement is awkward and adds cognitive burden. Left-right arrangement, per traditional matching questions, would be better. I could see this being put together either using a paired bucketlist or a static list on the left and a sortable on the right.

I may give this a go myself, if there's interest and I can find some time this semester.

Multi-drag is not working

Multi-drag is not working

The following code is an example from this package. Why the mult-drag and swap behavior is not working?

library(shiny); library(sortable)
labels <- list(
  "one",
  "two",
  "three",
  htmltools::tags$div(
    htmltools::em("Complex"), " html tag without a name"
  ),
  "five" = htmltools::tags$div(
    htmltools::em("Complex"), " html tag with name: 'five'"
  )
)

rank_list_basic <- rank_list(
  text = "Drag the items in any desired order",
  labels = labels,
  input_id = "rank_list_basic"
)

rank_list_swap <- rank_list(
  text = "Notice that dragging causes items to swap",
  labels = labels,
  input_id = "rank_list_swap",
  options = sortable_options(swap = TRUE)
)

rank_list_multi <- rank_list(
  text = "You can select multiple items, then drag as a group",
  labels = labels,
  input_id = "rank_list_multi",
  options = sortable_options(multiDrag = TRUE)
)



ui <- fluidPage(
  fluidRow(
    column(
      width = 12,
    tags$h2("Default, multi-drag and swapping behaviour"),
      tabsetPanel(
        type = "tabs",
        tabPanel(
          "Default",
            tags$b("Exercise"),
            rank_list_basic,
            tags$b("Result"),
            verbatimTextOutput("results_basic")
        ),
        tabPanel(
          "Multi-drag",
            tags$b("Exercise"),
            rank_list_multi,
            tags$b("Result"),
            verbatimTextOutput("results_multi")
        ),
        tabPanel(
          "Swap",
            tags$b("Exercise"),
            rank_list_swap,
            tags$b("Result"),
            verbatimTextOutput("results_swap")
        )
      )
    )
  )
)

server <- function(input, output) {
  output$results_basic <- renderPrint({
    input$rank_list_basic # This matches the input_id of the rank list
  })
  output$results_multi <- renderPrint({
    input$rank_list_multi # This matches the input_id of the rank list
  })
  output$results_swap <- renderPrint({
    input$rank_list_swap # This matches the input_id of the rank list
  })
}

shinyApp(ui, server)

A bucket list without a header throws a "writeImpl(text)" error

This code:

bucket_list(
  # header = "This is a bucket list. You can drag items between the lists.",
  add_rank_list(
    text = "Drag from here",
    labels = c("a", "bb", "ccc")
  ),
  add_rank_list(
    text = "to here",
    labels = NULL
  )
)

throws an error:

 Error in writeImpl(text) : 
  Text to be written must be a length-one character vector 

Move `master` branch to `main`

Cc @schloerke

The master branch of this repository will soon be renamed to main, as part of a coordinated change across several GitHub organizations (including, but not limited to: tidyverse, r-lib, tidymodels, and sol-eng). We anticipate this will happen by the end of September 2021.

That will be preceded by a release of the usethis package, which will gain some functionality around detecting and adapting to a renamed default branch. There will also be a blog post at the time of this master --> main change.

The purpose of this issue is to:

  • Help us firm up the list of targetted repositories
  • Make sure all maintainers are aware of what's coming
  • Give us an issue to close when the job is done
  • Give us a place to put advice for collaborators re: how to adapt

message id: entire_lizard

sortable_js_capture_input does not fire on an empty "sortable"

sortable_js_capture_input <- function(input_id) {
inner_text <- "
$.map(this.el.children, function(child){return child.innerText})
"

This is particularly troubling if/when a Shiny app's sortable input has been emptied. This message only goes back to the server side if/when there is at least one element in the list. (i.e. mapping over an empty array / no children means no function firing and no update to the server side).

Perhaps we could even make the callback configurable here (add a parameter "child_node_callback" or someting) that defaults to the current behavior.

EDIT: Although... this may just be a problem with observeEvent(input$my_input, {}). Yeah, because an empty list is getting sent back to R, which is what we expect. So it is just observeEvent not firing on the NULL that is confusing me.

Add `sortableItemlist()` function

The idea is to export some additional functions for specific, commonly used, sortable items. For example a sortable item list.

This function should wrap the sortable javascript call, so it's easier for the R user, i.e. less typing to get stuff done.

Make a bucketable list

A bucketable problem is like sortable, except:

  • you can drag elements between buckets

For example, a task can be:

  • Drag the options into Supported vs Unsupported buckets

This will be the first step towards a Parsons problem solution.

Rewrite vignettes

Objective:

  • Keep the README.Rmd simple, only showcasing what you can do with the package
  • Create a low level "understanding sortable.js" vignette
  • Showcase other functions in dedicated vignettes, especially:
    • parsons
    • learnr custom question types

For loop over add_rank_list within dynamic bucket_list

I'm working on an interface for the output of a route optimization algorithm. The algorithm takes customers that need to be delivered on the next day, clusters them into a desired quantity of tours and optimizes the order within each tour. The result then gets stored in a reactive as a list with each element of the list being one tour.

To enable the user to reorder the customers within a tour or change the assignment to tours I included Drag & Drop. Therefore I created a bucket list as a dynamic UI element wherein the labels of the rank lists represent the assigned customers to the tours. The problem is that beside the labels also the quantity of rank lists needs to be dynamic, but implementing a for loop over add_rank_list within the bucket_list doesn't work (error: is_add_rank_list(x = dot) is not TRUE).

Below are simplistic examples to make the issue more tangible. I would be very grateful if you could help me to figure out how I can achieve what I'm looking for.

App with dynamic labels but static quantity of lists (working):

library(shiny)
library(sortable)

ui <- fluidPage(
  numericInput("quant", label = "Desired quantity of tours", 
               value = 2, min = 2, max = 5, step = 1),
  uiOutput("DnD"),
  verbatimTextOutput("customised_tours")
)

server <- function(input, output){
  Tours = reactive({
    df = data.frame(x = 1:20)
    output = split(df, rep(1:input$quant, length.out = nrow(df), each = ceiling(nrow(df)/input$quant))) 
    return(output)
  })
  output$DnD = renderUI({
    bucket_list(
      header = "Change assignment to tours or order within tours",
      group_name = "bucket_list",
      orientation = "horizontal",
      add_rank_list(
        text = "Tour1",
        labels = as.list(Tours()[[1]][,"x"]),
        input_id = "rank_list_tour1"
      ),
      add_rank_list(
        text = "Tour2",
        labels = as.list(Tours()[[2]][,"x"]),
        input_id = "rank_list_tour2"
      )
    )
  })
  output$customised_tours = renderPrint({
    input$bucket_list 
  })
}
shinyApp(ui = ui, server = server)

App with dynamic labels and dynamic quantity of lists (needed but not working):

library(shiny)
library(sortable)

ui <- fluidPage(
  numericInput("quant", label = "Desired quantity of tours", 
               value = 2, min = 2, max = 5, step = 1),
  uiOutput("DnD"),
  verbatimTextOutput("customised_tours")
)

server <- function(input, output){
  Tours = reactive({
    df = data.frame(x = 1:20)
    output = split(df, rep(1:input$quant, length.out = nrow(df), each = ceiling(nrow(df)/input$quant))) 
    return(output)
  })
  output$DnD = renderUI({
    bucket_list(
      header = "Change assignment to tours or order within tours",
      group_name = "bucket_list",
      orientation = "horizontal",
      for(i in 1:length(Tours())){
        add_rank_list(
          text = paste("Tour", i, sep = ""),
          labels = as.list(Tours()[[i]][,"x"]),
          input_id = paste("rank_list_tour", i, sep = "")
        )
      }
    )
  })
  output$customised_tours = renderPrint({
    input$bucket_list 
  })
}
shinyApp(ui = ui, server = server)

Update sortable.js to version 1.13.0

Are you planning to update sortable.js to its latest version? It might help to fix the loss of drag event bug on Safari 13, as explained in this issue.

Is there another way to use current version of sortable with v1.13.0? of sortable.js?

Move repo to rstudio's GitHub team and update Authors@R

  • Would give the education team admin rights for things like PR reviews and travis easier. Would make submissions to CRAN cleaner.
  • Before next CRAN submission get Kent's approval of progress in package
  • Add person role of cre (Maintainer) for Andrie
  • Add person role for Kent of ccp and note that Kent is original author

Conceptor [ccp]
A person or organization responsible for the original idea on which a work is based, this includes the scientific author of an audio-visual item and the conceptor of an advertisement

cc @timelyportfolio @andrie

Wow!

Wow! Really nice! It works in the sidebarPanel too! See below.

Can you move elements between containers, ala, rpivotTable? Also, is there a way to access the sorting information? Could a cool way to customize a GUI.

library(shiny)
library(sortableR)

# Define UI for random distribution application
shinyUI(fluidPage(

  # Application title
  titlePanel("Tabsets"),

  # Sidebar with controls to select the random distribution type
  # and number of observations to generate. Note the use of the
  # br() element to introduce extra vertical spacing
  sidebarLayout(
    sidebarPanel(id = "sortSidebar",
      radioButtons("dist", "Distribution type:",
                   c("Normal" = "norm",
                     "Uniform" = "unif",
                     "Log-normal" = "lnorm",
                     "Exponential" = "exp")),
      br(),

      sliderInput("n",
                  "Number of observations:",
                   value = 500,
                   min = 1,
                   max = 1000)
    ),

    # Show a tabset that includes a plot, summary, and table view
    # of the generated distribution
    mainPanel(
      tabsetPanel(type = "tabs", id = "sortTab",
        tabPanel("Plot", plotOutput("plot")),
        tabPanel("Summary", verbatimTextOutput("summary")),
        tabPanel("Table", tableOutput("table"))
      )
    )
  )
  ,sortableR("sortSidebar")
  ,sortableR("sortTab")
))

Rename buckets in bucket list (feature proposition)

Hi,
I'd like to be able to rename the rank lists in a bucket list. I had the approach below in mind, does it make sense?

  1. add a update_rank_list_label(inputId, text) in R that sends a message to shiny (sendCustomMessage)
  2. create a script with a Shiny.customMessageHandler which finds the text element and updates it, and find a place where this script would be loaded (in bucket_list I guess?)

We could also add the option in add_rank_list to put a button to the right of the label with its own icon/text and input id, to be clicked to call update_rank_list_label (or any other function) through an observeEvent

Sortable is not defined

I'm trying to recreate the Shiny app you demonstrated in your blog and have been able to successfully do so on a local Windows machine. However, when trying to run the exact same code on a Linux server, I get an error in the console:

ReferenceError: Sortable is not defined
.renderValue()      sortableR.js:23
window.HTMLWidgets.staticRender/</<()    htmlwidgets.js:625
forEach()    self-hosted:232
forEach()    htmlwidgets.js:55
window.HTMLWidgets.staticRender/<()   htmlwidgets.js:551
forEach()   self-hosted:232
forEach()   htmlwidgets.js:55
window.HTMLWidgets.staticRender()    htmlwidgets.js:549
<anonymous>    htmlwidgets.js:638

Here is my session information:

> devtools::session_info()
Session info -------------------------------------------------------------------------------------------------------------
 setting  value                       
 version  R version 3.3.1 (2016-06-21)
 system   x86_64, linux-gnu           
 ui       RStudio (0.99.903)          
 language (EN)                        
 collate  en_US.UTF-8                 
 tz       posixrules                  
 date     2016-09-09 

Packages -----------------------------------------------------------------------------------------------------------------
 package          * version    date       source                                    
 AnomalyDetection * 1.0        2016-08-31 Github (twitter/AnomalyDetection@1f5deaa) 
 assertthat         0.1        2013-12-06 CRAN (R 3.3.1)                            
 colorspace         1.2-6      2015-03-11 CRAN (R 3.3.1)                            
 DBI              * 0.5        2016-08-11 CRAN (R 3.3.1)                            
 devtools           1.12.0     2016-06-24 CRAN (R 3.3.1)                            
 digest             0.6.10     2016-08-02 CRAN (R 3.3.1)                            
 dplyr            * 0.5.0.9000 2016-08-31 Github (hadley/dplyr@167b503)             
 DT               * 0.2        2016-08-09 CRAN (R 3.3.1)                            
 evaluate           0.9        2016-04-29 CRAN (R 3.3.1)                            
 formattable      * 0.2.0.1    2016-08-05 CRAN (R 3.3.1)                            
 ggplot2          * 2.1.0      2016-03-01 CRAN (R 3.3.1)                            
 ggthemes         * 3.2.0      2016-07-11 CRAN (R 3.3.1)                            
 ggvis            * 0.4.2.9000 2016-08-31 Github (rstudio/ggvis@d9cbbf5)            
 googleVis        * 0.6.0      2016-07-05 CRAN (R 3.3.1)                            
 gtable             0.2.0      2016-02-26 CRAN (R 3.3.1)                            
 htmltools          0.3.5      2016-03-21 CRAN (R 3.3.1)                            
 htmlwidgets      * 0.7        2016-08-02 CRAN (R 3.3.1)                            
 httpuv             1.3.3      2015-08-04 CRAN (R 3.3.1)                            
 jsonlite           1.0        2016-07-01 CRAN (R 3.3.1)                            
 knitr              1.14       2016-08-13 CRAN (R 3.3.1)                            
 leaflet          * 1.0.1      2016-02-27 CRAN (R 3.3.1)                            
 lubridate        * 1.5.6      2016-04-06 CRAN (R 3.3.1)                            
 magrittr           1.5        2014-11-22 CRAN (R 3.3.1)                            
 markdown           0.7.7      2015-04-22 CRAN (R 3.3.1)                            
 memoise            1.0.0      2016-01-29 CRAN (R 3.3.1)                            
 mime               0.5        2016-07-07 CRAN (R 3.3.1)                            
 munsell            0.4.3      2016-02-13 CRAN (R 3.3.1)                            
 plyr               1.8.4      2016-06-08 CRAN (R 3.3.1)                            
 R6                 2.1.2      2016-01-26 CRAN (R 3.3.1)                            
 RColorBrewer     * 1.1-2      2014-12-07 CRAN (R 3.3.1)                            
 Rcpp               0.12.6     2016-07-19 CRAN (R 3.3.1)                            
 RJSONIO            1.3-0      2014-07-28 CRAN (R 3.3.1)                            
 rmarkdown        * 1.0        2016-07-08 CRAN (R 3.3.1)                            
 RODBC            * 1.3-13     2016-04-14 CRAN (R 3.3.1)                            
 RSQLite          * 1.0.0      2014-10-25 CRAN (R 3.3.1)                            
 rstudioapi         0.6        2016-06-27 CRAN (R 3.3.1)                            
 scales             0.4.0      2016-02-26 CRAN (R 3.3.1)                            
 shiny            * 0.13.2     2016-03-28 CRAN (R 3.3.1)                            
 shinydashboard   * 0.5.1      2015-09-09 CRAN (R 3.3.1)                            
 shinyFiles       * 0.6.0      2015-03-26 CRAN (R 3.3.1)                            
 sortableR        * 0.1        2016-09-09 Github (timelyportfolio/sortableR@f3bcbb0)
 stringi            1.1.1      2016-05-27 CRAN (R 3.3.1)                            
 stringr            1.0.0      2015-04-30 CRAN (R 3.3.1)                            
 tibble             1.1        2016-07-04 CRAN (R 3.3.1)                            
 visNetwork       * 1.0.1      2016-06-20 CRAN (R 3.3.1)                            
 withr              1.0.2      2016-06-20 CRAN (R 3.3.1)                            
 xtable             1.8-2      2016-02-05 CRAN (R 3.3.1)                            
 yaml               2.1.13     2014-06-12 CRAN (R 3.3.1) 

Any thoughts on what might be going wrong? Thanks in advance!

Shiny render functions

How do the shiny render functions work? I couldn't find any documentation on the topic. Below is my attempt:

library(shiny)
library(sortable)

ui <- fluidPage(
  sortable_output("sortable")
)

server <- function(input, output, session) {
  output$sortable <- render_sortable({
    rank_list(input_id = "test", labels = letters)
  })
}

shinyApp(ui, server)

Experiment with different API for `bucketable_list`

Continued from #17 .

The current API is:

bucketable_list(
  text = "This is a bucketable list. You can drag items between the lists.",
  group_name = "bucketable",
  add_sortable_list(
    text = "Drag from here",
    labels = c("a", "bb", "ccc")
  ),
  add_sortable_list(
    text = "to here",
    labels = NULL
  )
)

It would be useful to be able to insert any object into the bucketable_list, so the API becomes:

bucketable_list(
  text = "",
  ...
)

Where the ... can be replaced by one or more sortable_list() calls.

This would require either:

  1. intercept the call and modify the call, or
  2. modify the (deeply nested) list constructed by sortable_list().

I experimented with both approaches and came to the conclusion:

  1. is dangerous, because the user may have constructed the call in any of a number of ways
  2. is very difficult, since the underlying structure of the sortable_list can easily change in future versions, so knowing exactly where to modify the list is hard.

@schloerke is going to look at this.

Rename the package to `sortable`

This will simplify the package name as well as the main functions:

  • sortable()
  • sortableOutput()
  • renderSortable()

And would make the namespace easier for creating special cases, e.g:

  • sortableItemlist()
  • sortableItemlistOutput()
  • renderSortableItemlist()

question for storing box positions for shiny dashboard plus using sortable

I've been able to use sortable_js on two boxes from shinydashboardplus. Is there a way to store the order of the sort after a user has repositioned boxes, so when they go back into the app , it'll be as they had sorted before?

I found the example of sortable_js_capture_input

So perhaps the ids could be stored in some form user context upon reopening, the values could be used to generate the tags in the order you would want?

Using the latest features from SortableJS

I'm using the latest version of sortable on CRAN. This version does not seem to support the most recent features present in SortableJS, such as the option 'avoidImplicitDeselect'. Would it be possible to merge this project with the latest version of SortableJS, which should make those features available? Many thanks in advance!

Add example to README of rich content in rank_list

It took me a little while to understand enough to build a (silly) example, so I think it would be worthwhile showing how to build rich content inside of a rank_list! (Probably with a more interesting example)

library(shiny)
library(sortable)

ui <- fluidPage(

    titlePanel("Old Faithful Geyser Data"),

    fluidRow(
        bucket_list(
            header = "Hello",
            add_rank_list(
                text = "List 1",
                input_id = "list_1",
                labels = c(
                    list(tags$div(tags$b("Hi"), tags$br(), tags$b("Two"))), 
                    "Hello"
                    )
            ),
            add_rank_list(
                text = "List 2",
                input_id = "list_2",
                labels = NULL
            ),
            add_rank_list(
                text = "List 3",
                input_id = "list_3",
                labels = c("two")
            ),
            add_rank_list(
                text = "List 4",
                input_id = "list_4",
                labels = c("three")
            )
        )
    ),
    fluidRow(textOutput("presentation"))
)

server <- function(input, output) {
  output$presentation <- renderText(
      capture.output(str(input$list_1))
  )
}

shinyApp(ui = ui, server = server)

However, I will say that the Shiny input does not seem to be initialized properly at the start (requires moving something to get the inputs working), and it would be nice to have some hooks into what is returned. I.e. if I could define my own ID for each label and have that returned.

image

After moving something:

chr [1:2] "Hi \nTwo\n" "Hello"

(I believe this is because of the following, which does not fire until sorted:)

sortable_options(onSort = sortable_js_capture_input(input_id)),

However, if you want a "hidden" ID or something, you can use font-size: 0 as a trick 😄

add_rank_list(
                text = "List 1",
                input_id = "list_1",
                labels = c(
                    list(tags$div(tags$span(id = "test", "test", style = "font-size: 0"), tags$b("Hi"), tags$br(), tags$b("Two"))), 
                    "Hello"
                    )
            )

# results in:
chr [1:2] "test Hi \nTwo\n" "Hello"

additional pull: 'clone' functionality

I firstly wanted to thank you for the great library! I've been fiddling with the JS because I'd like to add implement sortable JS's cloning with the additional ability to delete list items (I just added a button but you might have a more elegant solution?)

I made a CodePen here, but I'm only just learning how to put JS and Shiny https://codepen.io/mayagans/pen/MWYvVQx?editors=0110

I'll keep tinkering with the JS but figured it may be a feature request others would like as well.

Thanks again!

RShiny- Sortable- DT::renderDataTable works on server environment console (No Error Logs) but disconnect when someone using link

I am facing issues with DT::renderDataTable with reactive function (which I am using to select Drag and Drop columns to filter selected columns to show on UI). It works on server local environment console but disconnect when I am running on RServer using link. It breaks when it was trying to populate dataframe on UI. All package versions are same and I am using R 3.6.1. There are no Error logs in console while running on locally.

This was working fine few days back, but not working now.

Below is the snippet of code.

library(dplyr)
library(shiny)
library(shinyBS)
library(sortable)
library(shinyWidgets)
library(tidyr)
library(tidyverse)
library(tidyselect)
                    
rv <- reactiveValues(data = NULL)  

      bsModal(id = "modalExample",title =  'Select Features and Re-Arrange ', 
      trigger =  "tabBut", size = "large",

      radioButtons('radio', 'Select Action', choices = 
                      list('Default' = 'Default', 'Custom' = 'Custom'),
                      selected = 'Default', inline = T),
      bucket_list(
        header = "Select Features and Re-Arrange", group_name = "bucket_list_group",
            add_rank_list(
              text = "Drag from here",
              labels =
                c("Code","To", "Name", "From","Desc","Distance"),input_id = "rank_list_1"
            ),
            add_rank_list(
              text = "To here", labels = NULL,
              input_id = "rank_list_2"),orientation = 'horizontal')
        )

main_df <- reactive({
  selected_action = input$radio

  if(selected_action == 'Custom' ){
    result <- rv$df %>%
              select(all_of(input$rank_list_2))
  }
  else{result <- rv$df}
  return(as.data.frame(result))
})

output$content <- DT::renderDataTable({
  if(is.null(main_df())) return()      
  DT::datatable(main_df(),
    selection = 'none', filter = 'top', editable = FALSE, options = list(autoWidth = TRUE)) 
}) 

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.