---
title: "Understanding the interface to sortable.js"
author: "Andrie de Vries and Kenton Russell"
date: "`r Sys.Date()`"
output: rmarkdown::html_vignette
description: >
  Understanding the key idea of `sortable.js` will help you to write your own custom sortable widgets.
vignette: >
  %\VignetteIndexEntry{Understanding the interface to sortable.js}
  %\VignetteEngine{knitr::rmarkdown}
  %\usepackage[utf8]{inputenc}
---

```{r, echo = FALSE}
## get knitr just the way we like it

knitr::opts_chunk$set(
  message = FALSE,
  warning = FALSE,
  error = FALSE,
  tidy = FALSE,
  cache = FALSE
)
```


```{css, echo=FALSE}
pre {
  max-height: 20em;
  overflow-y: auto;
}

pre[class] {
  max-height: 20em;
}
```


With the `sortable` [`htmlwidget`](http://www.htmlwidgets.org) you can use powerful, dependency-free interactivity from [`SortableJS`](https://sortablejs.github.io/Sortable/) in the browser, RStudio Viewer, or Shiny apps.

```{r}
library(sortable)
library(htmltools)
```

## The central idea

The key idea to understand about `sortable`, and `SortableJS` in particular, is that the JavaScript will manipulate an HTML object based on it's CSS `id`.

Using `sortable` in markdown is a little tricky since markdown does not provide an easy way to provide an `id` that we'll need.  We can overcome this by using bare `HTML` or using `htmltools::tags`.  Let's make a simple `ul` list.  Note, however, that `sortable` works with nearly any `HTML` element, such as `div`.

### An example using raw HTML

The following example uses HTML to construct an unordered list (`<ul>`), and then uses `sortable_js()` to link the JavaScript required to create interactivity.

Note:

1. The HTML `id` matches the `css_id` argument of `sortable_js()`.
2. You can drag and drop the list entries. Try it!

```html
<p>You can drag and drop these items in any order (try it!):</p>
<ul id = "example01">
  <li>Move</li>
  <li>Or drag</li>
  <li>Each of the items</li>
  <li>To different positions</li>
</ul>
```

````r
```{r}`r ''`
sortable_js(css_id = "example01")
```
````


<p>You can drag and drop these items in any order (try it!):</p>
<ul id = "example01">
  <li>Move</li>
  <li>Or drag</li>
  <li>Each of the items</li>
  <li>To different positions</li>
</ul>

```{r, echo=FALSE}
sortable_js(css_id = "example01")
```





### Use a tag list to achieve the same, but from R

You can use the functions `tags()` and `tagList()`, both from the `htmltools` package, to create HTML.

This means you can construct the sortable list using:

<!-- the following chunk fails to build on CRAN, so use raw code instead -->

```{r}
library(htmltools)
tagList(
  tags$ul(
    id = "example02",
    tags$li("drag me"),
    tags$li("sort me"),
    tags$li("any way you like")
  ),
  sortable_js("example02")
)
```


### Little harder but better example

The `SortableJS` functionality works with any HTML object, not just lists.

In this next example, you can see how to drag and drop images (`<img>`).  To embed the plots on the page, you can use the `base64::img()` function to encode the png images into a format that HTML understands.


```{r}
library(base64enc)
library(withr)

# create two plots for demo purposes
pngfile_1 <- tempfile(fileext = ".png")
with_png(pngfile_1, width = 300, height = 200,{
  plot(1:100, rnorm(100), pch = 21, bg = "red")
  title(main = "Moves Like Jagger")
})

pngfile_2 <- tempfile(fileext = ".png")
with_png(pngfile_2, width = 300, height = 200,{
  barplot(1:9, col = blues9)
  title(main = "I Like the Way You Move")
})
```

Again, notice that the HTML `id` matches the `css_id`.

```{r}
tagList(
  tags$div(
    id = "example03",
    tags$image(src = base64enc::dataURI(file = pngfile_1, mime = "image/png")),
    tags$image(src = base64enc::dataURI(file = pngfile_2, mime = "image/png"))
  ),
  sortable_js(css_id = "example03")
)
```



## The power of groups

Looking at the [`SortableJS`](https://github.com/SortableJS/Sortable#options) excites me about the potential to use `sortable` as an important UI element in both a Shiny and non-Shiny context.  We could potentially demo a plot builder with something like this example.  You'll notice that it doesn't really do anything, but I hope the intent and direction is clear.



```{r shiny-drag-vars-to-plot, eval=FALSE}
knitr::read_chunk(
  system.file("shiny/drag_vars_to_plot/app.R", package = "sortable")
)
```

![](figures/drag_vars_to_plot.gif)

## Dragging and dropping shiny tabs

The `sortable` JS library allows movable tabs inside a Shiny (and also not Shiny) app.  
By adding just one line of code and an `id` to this RStudio 
[Tabset example](https://github.com/rstudio/shiny/blob/main/inst/examples-shiny/06_tabsets/app.R), 
you get tabs that the user can re-arrange.
You can copy and paste to see it for yourself, or `runGist("2dbe45f77b65e28acab9")`.

The modified code snippet is:

```r
# 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"))
    )
  )
),
sortable_js("sortTab")
```

And the full code:

```{r echo=FALSE, cache=FALSE}
knitr::read_chunk(
  system.file("shiny/shiny_tabset/app.R", package = "sortable")
)

```

```{r shiny-tabset-app, eval=FALSE}
```


![](figures/sortable_tabs.gif)
