Title: | Drag-and-Drop in 'shiny' Apps with 'SortableJS' |
---|---|
Description: | Enables drag-and-drop behaviour in Shiny apps, by exposing the functionality of the 'SortableJS' <https://sortablejs.github.io/Sortable/> JavaScript library as an 'htmlwidget'. 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. |
Authors: | Andrie de Vries [cre, aut], Barret Schloerke [aut], Kenton Russell [aut, ccp] (Original author), RStudio [cph, fnd], Lebedev Konstantin [cph] ('SortableJS', https://sortablejs.github.io/Sortable/) |
Maintainer: | Andrie de Vries <[email protected]> |
License: | MIT + file LICENSE |
Version: | 0.5.0.9000 |
Built: | 2024-12-11 05:57:53 UTC |
Source: | https://github.com/rstudio/sortable |
Since a bucket_list can contain more than one rank_list, you need an easy way to define the contents of each individual rank list. This function serves as a specification of a rank list.
add_rank_list(text, labels = NULL, input_id = NULL, css_id = input_id, ...)
add_rank_list(text, labels = NULL, input_id = NULL, css_id = input_id, ...)
text |
Text to appear at top of list. |
labels |
A character vector with the text to display inside the widget.
This can also be a list of html tag elements. The text content of each
label or label name will be used to set the shiny |
input_id |
output variable to read the plot/image from. |
css_id |
This is the css id to use, and must be unique in your shiny
app. This defaults to the value of |
... |
Other arguments passed to |
A list of class add_rank_list
bucket_list()
, rank_list()
and update_rank_list()
A bucket list can contain more than one rank_list and allows drag-and-drop of items between the different lists.
bucket_list( header = NULL, ..., group_name, css_id = group_name, group_put_max = rep(Inf, length(labels)), options = sortable_options(), class = "default-sortable", orientation = c("horizontal", "vertical") )
bucket_list( header = NULL, ..., group_name, css_id = group_name, group_put_max = rep(Inf, length(labels)), options = sortable_options(), class = "default-sortable", orientation = c("horizontal", "vertical") )
header |
Text that appears at the top of the bucket list. (This is
encoded as an HTML |
... |
One or more specifications for a rank list, and must be defined by add_rank_list. |
group_name |
Passed to |
css_id |
This is the css id to use, and must be unique in your shiny
app. This defaults to the value of |
group_put_max |
Not yet implemented |
options |
Options to be supplied to sortable_js object. See sortable_options for more details |
class |
A css class applied to the bucket list and rank lists. This can be used to define custom styling. |
orientation |
Either |
A list with class bucket_list
## -- example-bucket-list --------------------------------------------- ## bucket list if(interactive()) { 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 ) ) } ## bucket list with three columns if(interactive()) { bucket_list( header = c("Sort these items into Letters and Numbers"), add_rank_list( text = "Drag from here", labels = sample(c(1:3, letters[1:2])) ), add_rank_list( text = "Letters" ), add_rank_list( text = "Numbers" ) ) } ## drag items between bucket lists if(interactive()) { ui <- shiny::fluidPage( shiny::column(4, bucket_list(NULL, group_name = "foo", add_rank_list( text = "Drag from here...", labels = sample(c(1:3, letters[1:2])) ) )), shiny::column(4, "Some empty space"), shiny::column(4, bucket_list(NULL, group_name = "foo", add_rank_list( text = "...To here" ) )) ) server <- function(input, output, session) {} shiny::shinyApp(ui, server) } ## Example of a shiny app if (interactive()) { app <- system.file( "shiny/bucket_list/app.R", package = "sortable" ) shiny::runApp(app) }
## -- example-bucket-list --------------------------------------------- ## bucket list if(interactive()) { 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 ) ) } ## bucket list with three columns if(interactive()) { bucket_list( header = c("Sort these items into Letters and Numbers"), add_rank_list( text = "Drag from here", labels = sample(c(1:3, letters[1:2])) ), add_rank_list( text = "Letters" ), add_rank_list( text = "Numbers" ) ) } ## drag items between bucket lists if(interactive()) { ui <- shiny::fluidPage( shiny::column(4, bucket_list(NULL, group_name = "foo", add_rank_list( text = "Drag from here...", labels = sample(c(1:3, letters[1:2])) ) )), shiny::column(4, "Some empty space"), shiny::column(4, bucket_list(NULL, group_name = "foo", add_rank_list( text = "...To here" ) )) ) server <- function(input, output, session) {} shiny::shinyApp(ui, server) } ## Example of a shiny app if (interactive()) { app <- system.file( "shiny/bucket_list/app.R", package = "sortable" ) shiny::runApp(app) }
SortableJS does not have an event based system. To be able to call multiple JavaScript events under the same event execution, they need to be executed one after another.
chain_js_events(...)
chain_js_events(...)
... |
JavaScript functions defined by htmlwidgets::JS |
A single JavaScript function that will call all methods provided with the event
Other JavaScript functions:
sortable_js_capture_input()
Check if object is sortable options.
is_sortable_options(x)
is_sortable_options(x)
x |
Object to test |
Logical vector. TRUE if the object inherits from sortable_options
is_sortable_options("foo") # returns FALSE
is_sortable_options("foo") # returns FALSE
Add interactive ranking tasks to your learnr
tutorials. The student can
drag-and-drop the answer options into the desired order.
question_rank( text, ..., correct = "Correct!", incorrect = "Incorrect", loading = c("**Loading:** ", text, "<br/><br/><br/>"), submit_button = "Submit Answer", try_again_button = "Try Again", allow_retry = FALSE, random_answer_order = TRUE, options = sortable_options() )
question_rank( text, ..., correct = "Correct!", incorrect = "Incorrect", loading = c("**Loading:** ", text, "<br/><br/><br/>"), submit_button = "Submit Answer", try_again_button = "Try Again", allow_retry = FALSE, random_answer_order = TRUE, options = sortable_options() )
text |
Question or option text |
... |
parameters passed onto |
correct |
For |
incorrect |
Text to print for an incorrect answer (defaults to
"Incorrect") when |
loading |
Loading text to display as a placeholder while the question is loaded. If not provided, generic "Loading..." or placeholder elements will be displayed. |
submit_button |
Label for the submit button. Defaults to |
try_again_button |
Label for the try again button. Defaults to |
allow_retry |
Allow retry for incorrect answers. Defaults to |
random_answer_order |
Display answers in a random order. |
options |
Options to be supplied to sortable_js object. See sortable_options for more details |
Each set of answer options must contain the same set of answer options. When the question is completed, the first correct answer will be displayed.
Note that, by default, the answer order is randomized.
A custom learnr
question, with type = sortable_rank
.
See learnr::question()
.
## Example of rank problem inside a learnr tutorial if (interactive()) { learnr::run_tutorial("question_rank", package = "sortable") }
## Example of rank problem inside a learnr tutorial if (interactive()) { learnr::run_tutorial("question_rank", package = "sortable") }
Creates a ranking item list using the SortableJS
framework,
and generates an htmlwidgets
element. The elements of this list can be
dragged and dropped in any order.
You can embed a ranking question inside a learnr
tutorial, using
question_rank()
.
To embed a rank_list
inside a shiny app, see the Details section.
rank_list( text = "", labels, input_id, css_id = input_id, options = sortable_options(), orientation = c("vertical", "horizontal"), class = "default-sortable" )
rank_list( text = "", labels, input_id, css_id = input_id, options = sortable_options(), orientation = c("vertical", "horizontal"), class = "default-sortable" )
text |
Text to appear at top of list. |
labels |
A character vector with the text to display inside the widget.
This can also be a list of html tag elements. The text content of each
label or label name will be used to set the shiny |
input_id |
output variable to read the plot/image from. |
css_id |
This is the css id to use, and must be unique in your shiny
app. This defaults to the value of |
options |
Options to be supplied to sortable_js object. See sortable_options for more details |
orientation |
Set this to "horizontal" to get horizontal orientation of the items. |
class |
A css class applied to the rank list. This can be used to define custom styling. |
You can embed a rank_list
inside a Shiny app, to capture the preferred
ranking order of your user.
The widget automatically updates a Shiny output, with the matching
input_id
.
update_rank_list, sortable_js, bucket_list and question_rank
## - example-rank-list ------------------------------------------------ if (interactive()) { rank_list( text = "You can drag, drop and re-order these items:", labels = c("one", "two", "three", "four", "five"), input_id = "example_2" ) } ## - example-rank-list-multidrag ------------------------------------------ if (interactive()) { rank_list( text = "You can select multiple items and drag as a group:", labels = c("one", "two", "three", "four", "five"), input_id = "example_2", options = sortable_options( multiDrag = TRUE ) ) } ## - example-rank-list-swap ----------------------------------------------- if (interactive()) { rank_list( text = "You can re-order these items, and notice the swapping behaviour:", labels = c("one", "two", "three", "four", "five"), input_id = "example_2", options = sortable_options( swap = TRUE ) ) } ## Example of a shiny app if (interactive()) { app <- system.file("shiny/rank_list/app.R", package = "sortable") shiny::runApp(app) }
## - example-rank-list ------------------------------------------------ if (interactive()) { rank_list( text = "You can drag, drop and re-order these items:", labels = c("one", "two", "three", "four", "five"), input_id = "example_2" ) } ## - example-rank-list-multidrag ------------------------------------------ if (interactive()) { rank_list( text = "You can select multiple items and drag as a group:", labels = c("one", "two", "three", "four", "five"), input_id = "example_2", options = sortable_options( multiDrag = TRUE ) ) } ## - example-rank-list-swap ----------------------------------------------- if (interactive()) { rank_list( text = "You can re-order these items, and notice the swapping behaviour:", labels = c("one", "two", "three", "four", "five"), input_id = "example_2", options = sortable_options( swap = TRUE ) ) } ## Example of a shiny app if (interactive()) { app <- system.file("shiny/rank_list/app.R", package = "sortable") shiny::runApp(app) }
Widget render function for use in Shiny.
render_sortable(expr, env = parent.frame(), quoted = FALSE)
render_sortable(expr, env = parent.frame(), quoted = FALSE)
expr |
An expression |
env |
The environment in which to evaluate |
quoted |
Is |
Creates an htmlwidget
that provides
SortableJS to use for
drag-and-drop interactivity in Shiny apps and R Markdown.
sortable_js( css_id, options = sortable_options(), width = 0, height = 0, elementId = NULL, preRenderHook = NULL )
sortable_js( css_id, options = sortable_options(), width = 0, height = 0, elementId = NULL, preRenderHook = NULL )
css_id |
|
options |
Options to be supplied to sortable_js object. See sortable_options for more details |
width |
Fixed width for widget (in css units). The default is
|
height |
Fixed height for widget (in css units). The default is
|
elementId |
Use an explicit element ID for the widget (rather than an automatically generated one). Useful if you have other JavaScript that needs to explicitly discover and interact with a specific widget instance. |
preRenderHook |
A function to be run on the widget, just prior to rendering. It accepts the entire widget object as input, and should return a modified widget object. |
## -- example-sortable-js ------------------------------------------------- # Simple example of sortable_js. # Important: set the tags CSS `id` equal to the sortable_js `css_id` if (interactive()) { if (require(htmltools)) { html_print( tagList( tags$p("You can drag and reorder the items in this list:"), tags$ul( id = "example_1", tags$li("Move"), tags$li("Or drag"), tags$li("Each of the items"), tags$li("To different positions") ), sortable_js(css_id = "example_1") ) ) } }
## -- example-sortable-js ------------------------------------------------- # Simple example of sortable_js. # Important: set the tags CSS `id` equal to the sortable_js `css_id` if (interactive()) { if (require(htmltools)) { html_print( tagList( tags$p("You can drag and reorder the items in this list:"), tags$ul( id = "example_1", tags$li("Move"), tags$li("Or drag"), tags$li("Each of the items"), tags$li("To different positions") ), sortable_js(css_id = "example_1") ) ) } }
This captures the state of a sortable
list. It will look for a data-rank-id
attribute of the first child for each element. If no? attribute exists for
that particular item's first child, the inner text will be used as an
identifier.
sortable_js_capture_input(input_id) sortable_js_capture_bucket_input(input_id, input_ids, css_ids)
sortable_js_capture_input(input_id) sortable_js_capture_bucket_input(input_id, input_ids, css_ids)
input_id |
Shiny input name to set |
input_ids |
Set of Shiny input ids to set corresponding to the provided
|
css_ids |
Set of SortableJS |
This method is used with the onSort
option of sortable_js
. See
sortable_options()
.
A character vector with class JS_EVAL
. See htmlwidgets::JS()
.
sortable_js and rank_list.
Other JavaScript functions:
chain_js_events()
## -- example-sortable-js-capture ----------------------------------------- # Simple example of sortable_js_capture. # Important: set the tags CSS `id` equal to the sortable_js `css_id` if(interactive()) { 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_js( css_id = "sortable", options = sortable_options( onSort = sortable_js_capture_input(input_id = "selected") ) ) ) server <- function(input, output){ output$chosen <- renderPrint(input$selected) } shinyApp(ui, server) } ## ------------------------------------ # For an example, see the Shiny app at system.file("shiny/drag_vars_to_plot/app.R", package = "sortable")
## -- example-sortable-js-capture ----------------------------------------- # Simple example of sortable_js_capture. # Important: set the tags CSS `id` equal to the sortable_js `css_id` if(interactive()) { 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_js( css_id = "sortable", options = sortable_options( onSort = sortable_js_capture_input(input_id = "selected") ) ) ) server <- function(input, output){ output$chosen <- renderPrint(input$selected) } shinyApp(ui, server) } ## ------------------------------------ # For an example, see the Shiny app at system.file("shiny/drag_vars_to_plot/app.R", package = "sortable")
Use this function to define the options for sortable_js and rank_list,
which will pass these in turn to the SortableJS
JavaScript library.
sortable_options( ..., swap = NULL, multiDrag = NULL, group = NULL, sort = NULL, delay = NULL, disabled = NULL, animation = NULL, handle = NULL, filter = NULL, draggable = NULL, swapThreshold = NULL, invertSwap = NULL, direction = NULL, scrollSensitivity = NULL, scrollSpeed = NULL, onStart = NULL, onEnd = NULL, onAdd = NULL, onUpdate = NULL, onSort = NULL, onRemove = NULL, onFilter = NULL, onMove = NULL, onLoad = NULL )
sortable_options( ..., swap = NULL, multiDrag = NULL, group = NULL, sort = NULL, delay = NULL, disabled = NULL, animation = NULL, handle = NULL, filter = NULL, draggable = NULL, swapThreshold = NULL, invertSwap = NULL, direction = NULL, scrollSensitivity = NULL, scrollSpeed = NULL, onStart = NULL, onEnd = NULL, onAdd = NULL, onUpdate = NULL, onSort = NULL, onRemove = NULL, onFilter = NULL, onMove = NULL, onLoad = NULL )
... |
other arguments passed onto |
swap |
If |
multiDrag |
If |
group |
To drag elements from one list into another, both lists must
have the same group value. See
Sortable#group-option
for more details. [ |
sort |
Boolean that allows sorting inside a list. [ |
delay |
Time in milliseconds to define when the sorting should start.
[ |
disabled |
Boolean that disables the |
animation |
Millisecond duration of the animation of items when sorting
[ |
handle |
CSS selector used for the drag handle selector within list
items. [ |
filter |
CSS selector or JS function used for elements that cannot be
dragged. [ |
draggable |
CSS selector of which items inside the element should be
draggable. [ |
swapThreshold |
Percentage of the target that the swap zone will take
up, as a number between |
invertSwap |
Set to |
direction |
Direction of |
scrollSensitivity |
Number of pixels the mouse needs to be to an edge to
start scrolling. [ |
scrollSpeed |
Number of pixels for the speed of scrolling. [ |
onStart , onEnd
|
JS function called when an element dragging starts or ends |
onAdd |
JS function called when an element is dropped into the list from another list |
onUpdate |
JS function called when the sorting is changed within a list |
onSort |
JS function called by any change to the list (add / update / remove) |
onRemove |
JS function called when an element is removed from the list into another list |
onFilter |
JS function called when an attempt is made to drag a filtered element |
onMove |
JS function called when an item is moved in a list or between lists |
onLoad |
JS function dispatched on the "next tick" after SortableJS has initialized |
Many of the SortableJS
options will accept a JavaScript function. You can
do this using the htmlwidgets::JS
function.
A list with class sortable_options
https://github.com/sortablejs/Sortable/
sortable_options(sort = FALSE)
sortable_options(sort = FALSE)
Widget output function for use in Shiny.
sortable_output(input_id, width = "0px", height = "0px")
sortable_output(input_id, width = "0px", height = "0px")
input_id |
output variable to use for the sortable object |
width |
Fixed width for widget (in css units). The default is
|
height |
Fixed height for widget (in css units). The default is
|
You can only update the header
of the bucket_list
.
To update any of the labels or rank list text, use update_rank_list()
instead.
update_bucket_list( css_id, header = NULL, session = shiny::getDefaultReactiveDomain() )
update_bucket_list( css_id, header = NULL, session = shiny::getDefaultReactiveDomain() )
css_id |
This is the css id to use, and must be unique in your shiny
app. This defaults to the value of |
header |
Text that appears at the top of the bucket list. (This is
encoded as an HTML |
session |
The |
## Example of a shiny app that updates a bucket list and rank list if (interactive()) { app <- system.file( "shiny/update/app.R", package = "sortable" ) shiny::runApp(app) }
## Example of a shiny app that updates a bucket list and rank list if (interactive()) { app <- system.file( "shiny/update/app.R", package = "sortable" ) shiny::runApp(app) }
Change the text or labels of a rank list.
update_rank_list( css_id, text = NULL, labels = NULL, session = shiny::getDefaultReactiveDomain() )
update_rank_list( css_id, text = NULL, labels = NULL, session = shiny::getDefaultReactiveDomain() )
css_id |
This is the css id to use, and must be unique in your shiny
app. This defaults to the value of |
text |
Text to appear at top of list. |
labels |
A character vector with the text to display inside the widget.
This can also be a list of html tag elements. The text content of each
label or label name will be used to set the shiny |
session |
The |
## Example of a shiny app that updates a bucket list and rank list if (interactive()) { app <- system.file( "shiny/update_rank_list/app.R", package = "sortable" ) shiny::runApp(app) }
## Example of a shiny app that updates a bucket list and rank list if (interactive()) { app <- system.file( "shiny/update_rank_list/app.R", package = "sortable" ) shiny::runApp(app) }