---
title: "Getting Started"
author: "David Granjon"
date: "`r Sys.Date()`"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Getting Started}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r echo=FALSE}
knitr::include_url("https://rencontresr2021.rinterface.com/")
```

```{r setup, echo=FALSE, message=FALSE}
library(bslib)
```

# Introduction

`{shinyMobile}` is built on top of the [framework7](https://framework7.io) template (V5.7.14) and has different purposes:

  - Develop __mobile-ready__ shinyapps.
  - Develop __progressive web shinyapps__ (PWA, similar to native apps).
  - Develop desktop shinyapps.
  
## Custom skins

`{shinyMobile}` offers 3 skins:

  - __aurora__ for desktop apps.
  - __ios__ and __md__ for mobile apps.

It automatically detects if the app is running with android or iOS and accordingly adapts the layout. It is of course possible to apply the iOS skin on an android device and inversely, although not recommended.

`{shinyMobile}` also provides 2 themes, namely light and dark.

## Layouts

`{shinyMobile}` brings 3 out of the box layouts:

  - `f7SingleLayout()`: develop __simple__ apps (best choice for iOS/android Apps).
  - `f7TabLayout()`: develop complex __multi-tabbed__ apps (best choice for iOS/android Apps).
  - `f7SplitLayout()`: for __tablets__ and desktop with a sidebar, navbar and a main panel

## UI elements
### Inputs: brief comparison side by side with `{shiny}`

`{shinyMobile}` has its own custom input widgets with unique design for each skin (iOS/android/aurora). Below we summarise all known shiny inputs and their
equivalent with `{shinyMobile}`.

|  Features (sample)  |      shiny      |  shinyMobile |
|----------|:-------------:|------:|
| Range slider | `sliderInput()` | `f7Slider()` |
| Text input | `textInput()`, `textAreaInput()` | `f7Text()`, `f7Password()`, `f7TextArea()` |
| Checkbox | `checkboxInput()`, `checkboxGroupInput()` | `f7Checkbox()`, `f7CheckboxGroup()`|
| Radio | `radioButtons()` | `f7Radio()` |
| Toggle switch | ❌ (see `{bslib}`) | `f7Toggle()` |
| Numeric | `numericInput()` | `f7Stepper()` |
| Select | `selectInput()` | `f7Select()`, `f7SmartSelect()`, `f7Picker()` |
| Autocomplete | ❌ | `f7AutoComplete()` |
| Action button | `actionButton()` | `f7Button()` `f7Fab()`|
| Date | `dateInput()`, `dateRangeInput()` | `f7DatePicker()`|
| Color | ❌ | `f7ColorPicker()`|
| Download | `downloadButton()` | `f7DownloadButton()`|

### Containers
  
# Create your first App

## Page
It is the main template skeleton.

```{r, eval=FALSE}
f7Page(
  ..., 
  options = list(...),
  title = NULL,
  allowPWA = FALSE
)
```

__options__ sets up the app look and feel (See dedicated section below). `f7Page()` accepts any `{shinyMobile}` layout (See below).

### Navbar
The __navbar__ is a mandatory element of any `{shinyMobile}` layout.
It contains a title, a subtitle and triggers for both right and left panels (`f7Panel()`).

```{r, eval=FALSE}
f7Navbar(
  ..., 
  subNavbar = NULL, 
  title = NULL, 
  subtitle = NULL,
  hairline = TRUE, 
  shadow = TRUE, 
  bigger = FALSE,
  transparent = FALSE,
  leftPanel = FALSE, 
  rightPanel = FALSE
)
```

For complex apps, you can even add it a sub-navbar with `f7SubNavbar(...)`, which may contain any element like `f7Button()` or text. `f7Navbar()` exposes styling parameters such as shadow and hairline.

### The Toolbar

This is an option if you decide not to embed a `f7SubNavbar()`. The toolbar is the
right place to add `f7Button()`, `f7Link()`, `f7Badge()`... Its location is controlled with the position parameter (either top or bottom).

```{r, eval=FALSE}
f7Toolbar(
  ..., 
  position = c("top", "bottom"), 
  hairline = TRUE,
  shadow = TRUE, 
  icons = FALSE, 
  scrollable = FALSE
)
```

Under the hood, `f7Tabs()` is a custom `f7Toolbar()`.

### Panels

Panels are also called sidebars, `f7Panel()` being the corresponding function.

```{r, eval=FALSE}
f7Panel(
  ..., 
  id = NULL,
  title = NULL, 
  side = c("left", "right"),
  theme = c("dark", "light"), 
  effect = c("reveal", "cover"),
  resizable = FALSE
)
```

`f7Panel()` has a __theme__ option, regardless of the main app theme.
For instance, it is entirely possible to create a dark `f7Panel()` while the page
theme is light, and conversely. Its behavior is controlled via the effect argument:

  - __reveal__ makes the body content move and resize.
  - __cover__ covers the body content.
  
The __resizable__ argument allows to dynamically resize the panel.

Note that for the moment, there is no option to control the width of each panel.
  
As stated previously for the `f7SplitLayout()`, the `f7Panel()` may also be considered as a sidebar. In that case, we may include `f7PanelMenu()`.
Finally do not forget to set up the `f7Navbar()` so that panels are allowed!

### The appbar

`f7Appbar()` is displayed on top of the `f7Navbar()`. It is a best choice to embed
`f7Searchbar()`. `f7Appbar()` may also trigger `f7Panel()`. 

```{r, eval=FALSE}
f7Appbar(
  ..., 
  leftPanel = FALSE, 
  rightPanel = FALSE,
  maximizable = FALSE
)
```

## Select a template

This choice is crucial when you are developing an App. It depends on the complexity of your visualizations and content. If your plan is to develop a simple graph or table, you should go for the `f7SingleLayout()` option. For more complex design, the best is `f7TabLayout()`. `f7SplitLayout()` is specific for tablets and desktop apps.

### Simple Layout

`f7SingleLayout()` is dedicated to build simple, one-page apps or gadgets.

```{r, eval=FALSE}
f7SingleLayout(
  ..., 
  navbar, 
  toolbar = NULL, 
  panels = NULL,
  appbar = NULL
)
```

While only the navbar is mandatory, other components such as the toolbar are 
optional for the `f7SingleLayout()`.

The app below runs with specific options:

```r
f7Page(
  options = list(dark = FALSE, filled = FALSE, theme = "md"),
  ...
)
```

```{r, eval=TRUE, echo=FALSE}
card(
  shinyMobile:::create_app_link(
    " ", #nolint
    "app",
    header = FALSE
  ),
  full_screen = TRUE
)
```
<div class="text-center">
<a class="btn btn-primary" data-bs-toggle="collapse" href="#singleLayout" role="button" aria-expanded="false" aria-controls="singleLayout">
  Code
</a>
</div>

<div class="collapse" id="singleLayout">
```{r, eval=FALSE}
library(shiny)
library(shinyMobile)
library(apexcharter)
library(dplyr)
library(ggplot2)

data("economics_long")
economics_long <- economics_long %>%
  group_by(variable) %>%
  slice((n()-100):n())


shinyApp(
  ui = f7Page(
    title = "My app",
    options = list(dark = FALSE, filled = FALSE, theme = "md"),
    f7SingleLayout(
      navbar = f7Navbar(
        title = "Single Layout",
        hairline = TRUE,
        shadow = TRUE
      ),
      toolbar = f7Toolbar(
        position = "bottom",
        f7Link(label = "Link 1", href = "https://www.google.com"),
        f7Link(label = "Link 2", href = "https://www.google.com")
      ),
      # main content
      f7Shadow(
        intensity = 16,
        hover = TRUE,
        f7Card(
          title = "Card header",
          apexchartOutput("areaChart")
        )
      )
    )
  ),
  server = function(input, output) {
    output$areaChart <- renderApexchart({
      apex(
        data = economics_long,
        type = "area",
        mapping = aes(
          x = date,
          y = value01,
          fill = variable
        )
      ) %>%
        ax_yaxis(decimalsInFloat = 2) %>% # number of decimals to keep
        ax_chart(stacked = TRUE) %>%
        ax_yaxis(max = 4, tickAmount = 4)
    })
  }
)
```
</div>

### Tabs Layout

Choose this layout to develop complex __multi-tabbed__ apps (best choice for iOS/android Apps).

```{r, eval=FALSE}
f7TabLayout(
  ..., 
  navbar,
  messagebar = NULL,
  panels = NULL, 
  appbar = NULL
)
```
  
The ... argument requires `f7Tabs(..., id = NULL, swipeable = FALSE, animated = TRUE)`. The id argument is mandatory if you want to exploit the `updateF7Tabs()` function. `f7Tabs()` expect to have `f7Tab(..., tabName, icon = NULL, active = FALSE)` passed inside.

The app below runs with specific options:

```r
f7Page(
  options = list(dark = FALSE, filled = FALSE, theme = "md"),
  ...
)
```

```{r, eval=TRUE, echo=FALSE}
card(
  shinyMobile:::create_app_link(
    " ", #nolint
    "app",
    header = FALSE
  ),
  full_screen = TRUE
)
```

<div class="text-center">
<a class="btn btn-primary" data-bs-toggle="collapse" href="#tabLayout" role="button" aria-expanded="false" aria-controls="tabLayout">
  Code
</a>
</div>

<div class="collapse" id="tabLayout">
```{r, eval=FALSE}
library(shiny)
library(shinyMobile)
library(apexcharter)

poll <- data.frame(
  answer = c("Yes", "No"),
  n = c(254, 238)
)

shinyApp(
  ui = f7Page(
    options = list(dark = FALSE, filled = FALSE, theme = "md"),
    title = "My app",
    f7TabLayout(
      panels = tagList(
        f7Panel(title = "Left Panel", side = "left", theme = "light", "Blabla", effect = "cover"),
        f7Panel(title = "Right Panel", side = "right", theme = "dark", "Blabla", effect = "cover")
      ),
      navbar = f7Navbar(
        title = "Tabs",
        hairline = TRUE,
        shadow = TRUE,
        leftPanel = TRUE,
        rightPanel = TRUE
      ),
      f7Tabs(
        animated = TRUE,
        #swipeable = TRUE,
        f7Tab(
          title = "Tab 1",
          tabName = "Tab1",
          icon = f7Icon("folder"),
          active = TRUE,

          f7Shadow(
            intensity = 10,
            hover = TRUE,
            f7Card(
              title = "Card header",
              apexchartOutput("pie")
            )
          )
        ),
        f7Tab(
          title = "Tab 2",
          tabName = "Tab2",
          icon = f7Icon("keyboard"),
          active = FALSE,
          f7Shadow(
            intensity = 10,
            hover = TRUE,
            f7Card(
              title = "Card header",
              apexchartOutput("scatter")
            )
          )
        ),
        f7Tab(
          title = "Tab 3",
          tabName = "Tab3",
          icon = f7Icon("layers_alt"),
          active = FALSE,
          f7Shadow(
            intensity = 10,
            hover = TRUE,
            f7Card(
              title = "Card header",
              f7SmartSelect(
                "variable",
                "Variables to show:",
                c("Cylinders" = "cyl",
                  "Transmission" = "am",
                  "Gears" = "gear"),
                openIn = "sheet",
                multiple = TRUE
              ),
              tableOutput("data")
            )
          )
        )
      )
    )
  ),
  server = function(input, output, session) {

    # river plot
    dates <- reactive(seq.Date(Sys.Date() - 30, Sys.Date(), by = input$by))

    output$pie <- renderApexchart({
      apex(
        data = poll,
        type = "pie",
        mapping = aes(x = answer, y = n)
      )
    })

    output$scatter <- renderApexchart({
      apex(
        data = mtcars,
        type = "scatter",
        mapping = aes(
          x = wt,
          y = mpg,
          fill = cyl
        )
      )
    })


    # datatable
    output$data <- renderTable({
      mtcars[, c("mpg", input$variable), drop = FALSE]
    }, rownames = TRUE)
  }
)
```
</div>

### Split Layout

`f7SplitLayout()` is the third layout introduced with `{shinyMobile}`, similar to `sidebarLayout` with {shiny}. This template is focused for 
tablet/desktop use. It is composed of a sidebar, and a main panel.

```{r, eval=FALSE}
f7SplitLayout(
  ..., 
  navbar, 
  sidebar, 
  toolbar = NULL, 
  panels = NULL,
  appbar = NULL
)
```

The main content goes in the __...__ parameter. Navigation items are gathered in the sidebar slot. The sidebar expect a `f7Panel()`. Importantly, the side parameter must be set to `left` and the style to `reveal`. The navigation menu is organized as follows:

```{r, eval=FALSE}
f7PanelMenu(
  id = "menu",
  f7PanelItem(
    tabName = "tab1", 
    title = "Tab 1", 
    icon = f7Icon("email"), 
    active = TRUE
  ),
  f7PanelItem(
    tabName = "tab2", 
    title = "Tab 2", 
    icon = f7Icon("home")
  )
)
```

The __id__ argument is important if you want to get the currently selected item or
update the select tab. Each `f7PanelItem()` has a mandatory __tabName__. The associated input
will be `input$menu` in that example, with `tab1` for value since the first tab was set to an active state. To adequately link the body and the sidebar, you must wrap the body content in `f7Items()` containing as many `f7Item()` as sidebar items. The __tabName__ must correspond.

```{r, eval=TRUE, echo=FALSE}
card(
  shinyMobile:::create_app_link(
    " ", #nolint
    "app",
    header = FALSE
  ),
  full_screen = TRUE
)
```

<div class="text-center">
<a class="btn btn-primary" data-bs-toggle="collapse" href="#splitLayout" role="button" aria-expanded="false" aria-controls="splitLayout">
  Code
</a>
</div>

<div class="collapse" id="splitLayout">

```{r, eval=FALSE}
library(shiny)
library(ggplot2)
library(shinyMobile)
library(apexcharter)
library(thematic)

fruits <- data.frame(
  name = c('Apples', 'Oranges', 'Bananas', 'Berries'),
  value = c(44, 55, 67, 83)
)

thematic_shiny(font = "auto")

new_mtcars <- reshape(
  data = head(mtcars),
  idvar = "model",
  varying = list(c("drat", "wt")),
  times = c("drat", "wt"),
  direction = "long",
  v.names = "value",
  drop = c("mpg", "cyl", "hp", "dist", "qsec", "vs", "am", "gear", "carb")
)

shinyApp(
  ui = f7Page(
    title = "My app",
    options = list(
      theme = "aurora",
      dark = TRUE,
      filled = FALSE,
      color = "#007aff",
      touch = list(
        tapHold = TRUE,
        tapHoldDelay = 750,
        iosTouchRipple = FALSE
      ),
      iosTranslucentBars = FALSE,
      navbar = list(
        iosCenterTitle = TRUE,
        hideNavOnPageScroll = TRUE
      ),
      toolbar = list(
        hideNavOnPageScroll = FALSE
      ),
      pullToRefresh = FALSE
    ),
    f7SplitLayout(
      sidebar = f7Panel(
        title = "Sidebar",
        side = "left",
        theme = "light",
        f7PanelMenu(
          id = "menu",
          f7PanelItem(
            tabName = "tab1",
            title = "Tab 1",
            icon = f7Icon("equal_circle"),
            active = TRUE
          ),
          f7PanelItem(
            tabName = "tab2",
            title = "Tab 2",
            icon = f7Icon("equal_circle")
          ),
          f7PanelItem(
            tabName = "tab3",
            title = "Tab 3",
            icon = f7Icon("equal_circle")
          )
        ),
        uiOutput("selected_tab"),
        effect = "reveal"
      ),
      navbar = f7Navbar(
        title = "Split Layout",
        hairline = FALSE,
        shadow = TRUE
      ),
      toolbar = f7Toolbar(
        position = "bottom",
        f7Link(label = "Link 1", href = "https://www.google.com"),
        f7Link(label = "Link 2", href = "https://www.google.com")
      ),
      # main content
      f7Items(
        f7Item(
          tabName = "tab1",
          f7Button("toggleSheet", "Plot parameters"),
          f7Sheet(
            id = "sheet1",
            label = "Plot Parameters",
            orientation = "bottom",
            swipeToClose = TRUE,
            backdrop = TRUE,
            f7Slider(
              "obs",
              "Number of observations:",
              min = 0, max = 1000,
              value = 500
            )
          ),
          br(),
          plotOutput("distPlot")
        ),
        f7Item(
          tabName = "tab2",
          apexchartOutput("radar")
        ),
        f7Item(
          tabName = "tab3",
          f7Toggle(
            inputId = "plot_show",
            label = "Show Plot?",
            checked = TRUE
          ),
          apexchartOutput("multi_radial")
        )
      )
    )
  ),
  server = function(input, output, session) {

    observeEvent(input$toggleSheet, {
      updateF7Sheet(id = "sheet1")
    })

    observeEvent(input$obs, {
      if (input$obs < 500) {
        f7Notif(
          text = paste0("The slider value is only ", input$obs, ". Please
                        increase it"),
          icon = f7Icon("bolt_fill"),
          title = "Alert",
          titleRightText = Sys.Date()
        )
      }
    })

    output$radar <- renderApexchart({
      apex(
        data = new_mtcars,
        type = "radar",
        mapping = aes(
          x = model,
          y = value,
          group = time)
      )
    })

    output$selected_tab <- renderUI({
      HTML(paste0("Access the currently selected tab: ", strong(input$menu)))
    })

    output$distPlot <- renderPlot({
      dist <- rnorm(input$obs)
      hist(dist)
    })

    output$multi_radial <- renderApexchart({
      if (input$plot_show) {
        apex(data = fruits, type = "radialBar", mapping = aes(x = name, y = value))
      }
    })

  }
)
```
</div>

## App options

This is where you can customize the global app behavior: 

```{r, eval=FALSE}
options = list(
    theme = c("ios", "md", "auto", "aurora"),
    dark = TRUE,
    filled = FALSE,
    color = "#007aff",
    touch = list(
      tapHold = TRUE,
      tapHoldDelay = 750,
      iosTouchRipple = FALSE
    ),
    iosTranslucentBars = FALSE,
    navbar = list(
      iosCenterTitle = TRUE,
      hideOnPageScroll = TRUE
    ),
    toolbar = list(
      hideOnPageScroll = FALSE
    ),
    pullToRefresh = FALSE
  )
```

As stated above, you may choose between 3 skins and 2 color themes. There is a third option called __filled__ that allows to fill the navbar and toolbar if enabled. The __color__ options simply changes the color of elements such as buttons, panel triggers, tabs triggers, ... 
`{shinyMobile}` brings a lot of different colors. __hideOnPageScroll__ allows to hide/show the navbar and toolbar which is useful to focus on the content. The __tapHold__ parameter ensure that the "long-press" feature is activated. __preloader__ is useful in case you want to display a loading screen.
Framework7 has many more [options](https://v5.framework7.io/docs/app#app-parameters) which can be passed through this __options__ parameter.

# Gadgets

`{shinyMobile}` is particularly well suited to build shiny __gagdets__. 
To convert an existing app to a gadget, wrap it in the `shiny::runGadget()` function. 

```{r, eval = FALSE}
library(shiny)
library(shinyMobile)
runGadget(shinyAppDir(system.file("examples/tab_layout", package = "shinyMobile")))
```
