# Introduction to shinyjqui

#### Yang Tang

#### 2020-02-03

The `shinyjqui` package provides APIs to `jQuery UI` JavaScript library that can be used to add mouse-interactions and animation effects to shiny ui. This vignette provides general introductions and examples of them.

## Mouse interactions

There are five kinds of mouse interactions in jQuery UI library:

- Draggable: Allow elements to be moved using the mouse.

- Droppable: Create targets for draggable elements.

- Resizable: Change the size of an element using the mouse.

- Selectable: Use the mouse to select elements, individually or in a group.

- Sortable: Reorder elements in a list or grid using the mouse.

The corresponding R wrappers in `shinyjqui` are `jqui_draggable()`, `jqui_droppable()`, `jqui_resizable()`, `jqui_selectable()` and `jqui_sortable()`. They can be used to add interaction effects to ui element(s) in different context of a shiny app.

In shiny `ui`, the functions accept a shiny ui element (tag, tagList, input or output) and return the same one with interaction attached. For example:

    # create a draggable textInput in shiny ui
    ui <- fluidPage(
      jqui_draggable(textInput('foo', 'Input'))
    )

In shiny `server`, the functions modify the interactivity of the existing (pre-defined in `ui`) elements:

    # create a textInput in shiny ui
    ui <- fluidPage(
      textInput('foo', 'Input')
    )

    # make the textInput with id "foo" draggable in shiny server
    server <- function(input, output) {
      jqui_draggable(ui = '#foo')
    }

The first way is straight forward, however, the second way is more flexible:

1. You can reactively control the element’s interactivity by using them inside an `observe()` or `ovservEvent()`.
2. In the context of shiny `server`, the functions are more flexible in selecting the target shiny html element(s). Their first parameter `ui` accepts either a string of jQuery selector or a `JS()` wrapped JavaScript expression that returns a jQuery object. In the above example, which used the jQuery selector mode, the string `#foo` refers to the element with id `foo`. To select a set of elements, their `class name` can be used, e.g., use `jqui_draggable(selector = ".shiny-bound-input")` to make all the shiny inputs draggable. The JavaScript expression mode gives even more freedom to make your selection. You can write your own JavaScript expression that returns a jQuery object to refer to any element(s) you want, e.g., use `jqui_resizable(selector = JS("$('#foo').parent()"))` to make the parent of element with id `foo` resizable.
3. In addition to `enable` the interactions, in the context of shiny `server`, the functions also support `disable`, `destory`, `save` and `load` the interactions. This behavior is controlled by the `operation` parameter with the default value `enable`. The difference between `disable` and `destory` is that the `disable` operation only disable the effect of interaction temporarily without removing the corresponding settings, whereas the `destory` operation removes the interaction totally. The `save` and `load` operations are used to save and restore the interaction state (e.g., the current position of a draggable element). Please see the vignette `Save and restore` for more details.

To make multiple elements interactive, you can either use the `tagList()` function in shiny `ui`:

    # in shiny ui, make each element in the tagList draggable
    ui <- fluidPage(
      jqui_draggable(
        tagList(
          selectInput('sel', 'Select', choices = month.abb),
          checkboxGroupInput('chbox', 'Checkbox', choices = month.abb),
          plotOutput('plot', width = '400px', height = '400px')
        )
      )
    )

or use jQuery selector:

    server <- function(input, output) {
      jqui_draggable("#sel,#chbox,#plot")
    }

The `options` parameter can be used to further specify the behavior of interaction. The full list of the available options can be found in jQuery UI’s API Documentation page. The example sections below showed how to use them.

In shiny, the user input values are send back to server in the form of `input$<id>`. Similarly, once enabled, some interaction-specific state values are returned to server and can be accessed by `input$<id>_<suffix>`, where the `id` is the element id (`id` attribute for shiny tag, `inputId` for shiny inputs, `outputId` for shiny outputs) and the `suffix` depends on the type of interaction enabled. The currently deployed interaction state values are listed here:

Interaction_type | suffix | The_returned_shiny_input_value
--- | --- | ---
draggable | position | A list of the element’s left and top distances (in pixels) to its parent element
draggable | is_dragging | TRUE or FALSE that indicate whether the element is dragging
droppable | dragging | The id of an acceptable element that is now dragging
droppable | over | The id of the last acceptable element that is dragged over
droppable | drop | The id of the last acceptable element that is dropped
droppable | dropped | The ids of all acceptable elements that is currently dropped
droppable | out | The id of the last acceptable element that is dragged out
resizable | size | A list of the element’s current size
resizable | is_resizing | TRUE or FALSE that indicate whether the element is resizing
selectable | selected | A dataframe containing the id and innerText of curently selected items
selectable | is_selecting | TRUE or FALSE that indicate whether the element is selecting (e.g. during lasso selection)
sortable | order | A dataframe containing the id and innerText of items in the current order

You can even customize what value to be send back by including a `shiny` option in the `options` parameter. This option should be created in the following format. You may combine it with other interaction-specific options before passing to the `options` parameter:

    shiny_opt = list(

      # define shiny input value input$id_suffix1
      suffix1 = list(
        # on event_type1 run callback1 and send the returned value to input$id_suffix1
        event_type1 = JS(callback1),
        # on event_type2 or event_type3 run callback2 and send the returned value to input$id_suffix1
        `event_type2 event_type3` = JS(callback2),
        ...
      ),

      # define another shiny input value input$id_suffix2
      suffix2 = list(
        ...
      ),

      # define more shiny input values

    )

    # pass the shiny option to draggable
    jqui_draggable('#foo', options = list(
      shiny = shiny_opt,
      #other draggable-specific options
    ))

The shiny option list is composed by multiple `suffix` definition units. The unit name is used as `suffix` in `input$<id>_<suffix>`. Within each unit, there are multiple `JS()` wrapped JavaScript callback functions named with corresponding event types. The event triggers the callback and the returned value is send to server in the form of `input$<id>_<suffix>`. The valid event types for each interactions can be found in the API Documentation of jQuery UI. The callback functions take two parameters, `event` and `ui`. The definition of the `ui` parameter can also be found in the document too. Here is an example:

    # server
    jqui_draggable('#foo', options = list(
      shiny = list(
        # By default, draggable element has a shiny input value showing the
        # element's position (relative to the parent element). Here, another shiny
        # input value (input$foo_offset) is added. It gives the element's offset
        # (position relative to the document).
        offset = list(
          # return the updated offset value when the draggable is created or dragging
          `dragcreate drag` = JS('function(event, ui) {return $(event.target).offset();}'),
        )
      )
    ))

When customizing shiny input values by shiny option and callbacks, you may want to get the id of certain element in JavaScript. For simple shiny tag (e.g. `tags$div`), `element.attr("id")` just works fine, however, things become more complicated for shiny inputs (e.g. `textInput`). The id attribute of most shiny inputs is hidden inside a div container. You may use jQuery function `.find()` to locate it. The `shinyjqui` package comes with an internal JavaScript function `shinyjqui.getId()` which will take care of this. You can just simply pass in any shiny element, either simple tag, shiny input or shiny output. It will use the appropriate way to find out the id.

The following are some examples to demonstrate how to use each interactions

### Draggable

Draggable element can be moved by mouse. You can custom its movement by some options:

    # drag only horizontally
    jqui_draggable('#foo', options = list(axis = 'x'))
    # make movement snapping to a 80 x 80 grid
    jqui_draggable('#foo', options = list(grid = c(80, 80)))

### Droppable

With the droppable interaction enabled, the element can sense the behavior of accepted draggable elements and make changes (e.g. change display style) accordingly:

    jqui_droppable('#foo', options = list(
      accept = '#bar', # jQuery selector to define which draggable element to monitor. Accept anything if not set.
      classes = list(
        `ui-droppable-active` = 'ui-state-focus', # change class when draggable element is dragging
        `ui-droppable-hover` = 'ui-state-highlight' # change class when draggable element is dragging over
      ),
      drop = JS(
        'function(event, ui){$(this).addClass("ui-state-active");}'
      ) # a javascrip callback to change class when draggable element is dropped in
    ))

Note: When passing a JavaScript callback function to the `opations` parameter, please wrap it with `JS()` so that it can be evaluated correctly.

### Resizable

You can change the size of a resizable element by dragging the resize-handles around it. Several examples are listed here:

    # keep aspect ratio when resizing
    jqui_resizable('#foo', options = list(aspectRatio = TRUE))

    # Limit the resizable element to a maximum or minimum height or width
    jqui_resizable('#foo', options = list(minHeight = 100, maxHeight = 300,
                                          minWidth = 200, maxWidth = 400))

    # make the two plotOutputs resize synchronously
    jqui_resizable(plotOutput('plot1', width = '400px', height = '400px'),
                      options = list(alsoResize = '#plot2')),
    plotOutput('plot2', width = '400px', height = '400px')

### Selectable

The selectable interaction make target element’s children selectable. You can select by click, Ctrl+click or dragging a box (lasso selection). The selected elements may change display styles if specified in `options`:

    # highlight the selected plotOutput
    jqui_selectable(
      div(
        plotOutput('plot1', width = '400px', height = '400px'),
        plotOutput('plot2', width = '400px', height = '400px')
      ),
      options = list(classes = list(`ui-selected` = 'ui-state-highlight'))
    )

### Sortable

The sortable interaction makes target element’s children sortable. You can re-arrange them by drag and drop. Some examples here:

    # change opacity while sorting
    jqui_sortable('#foo', options = list(opacity = 0.5))

    # only items with class "items" inside the element become sortable
    jqui_sortable('#foo', options = list(items = '> .items'))

    # connect two sortable elements, so that items in one element can be dragged to another
    jqui_sortable('#foo1', options = list(connectWith = '#foo2'))
    jqui_sortable('#foo2', options = list(connectWith = '#foo1'))

`shinyjqui` has a function called `orderInput()` which takes advantage of sortable interaction. You can use it to display a list of items in shiny and can change their order by drag and drop. The changed items order is send back to server as an input value. Please see the Vignette `orderInput` for details.

## Animation effects

jQuery UI library comes with 15 internal animation effects. You can get a full list of them by R function `get_jqui_effects()`:

    ##  [1] "blind"     "bounce"    "clip"      "drop"      "explode"   "fade"
    ##  [7] "fold"      "highlight" "puff"      "pulsate"   "scale"     "shake"
    ## [13] "size"      "slide"     "transfer"

There is a live demo for each effect here. By use following functions, you can apply these effects to a shiny element:

Functions | Description | Where_to_use
--- | --- | ---
jqui_effect | Let element(s) to show an animation immediately. | server
jqui_show | Display hidden element(s) with an animation | server
jqui_hide | Hide element(s) with an animation | server
jqui_toggle | Display or hide element(s) with an animation | server

The above functions use jQuery selector to locate the target element(s) and should be used in `server`.

The `effect` parameter accept a string that defines which animation effect to apply. Note: The `transfer` effect can only be used in `jqui_effect()`.

The `options` parameter accept a list of effect specific options. Please find more details here.

The `complete` parameter accept a JavaScript callback function which will be called after the animation. Please wrap it with `JS()`.

Here are some examples:

    # ui
    plotOutput('foo', width = '400px', height = '400px')

    # server
    jqui_effect('#foo', effect = 'bounce') # bounces the plot
    jqui_effect('#foo', effect = 'scale', options = list(percent = 50)) # scale to 50%
    jqui_hide('#foo', effect = 'size', options = list(width = 200, height = 60)) # resize then hide
    jqui_show('#foo', effect = 'clip') # show the plot by clipping

## Classes animation

These functions can be used to change shiny element’s class(es) while animating all style changes:

Functions | Description | Where_to_use
--- | --- | ---
jqui_add_class | Add class(es) to element(s) while animating all style changes. | server
jqui_remove_class | Remove class(es) from element(s) while animating all style changes. | server
jqui_switch_class | Add and remove class(es) to element(s) while animating all style changes. | server

Similar to the animation effects functions, these functions need a jQuery selector and please use them in `server`.

The `easing` parameter defines the speed style of the animation progresses. More details can be found here