---
title: "Introduction to inshiny"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{inshiny}
  %\VignetteEncoding{UTF-8}
  %\VignetteEngine{knitr::rmarkdown}
editor_options: 
  markdown: 
    wrap: 72
---

```{r, include = FALSE}
knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>"
)
```

`inshiny` provides a basic set of compact Shiny input widgets that are
designed to be displayed within surrounding text without breaking the
spacing between lines. You can use `inshiny` to integrate Shiny input
within text paragraphs, or just to get user input for your Shiny app
without taking up as much space on the page compared to standard Shiny
inputs.

By default, Shiny uses [Bootstrap 3](https://getbootstrap.com/docs/3.4/)
for layouts and styling. `inshiny` requires [Bootstrap
5](https://getbootstrap.com/docs/5.3/getting-started/introduction/),
which just means that you will need to use the `bslib` package (which
enables updating to Bootstrap 5) in addition to `shiny` for `inshiny` to
work.

```{r, message = FALSE}
library(shiny)
library(bslib)
library(inshiny)
```

To use Bootstrap 5, when building your UI, you should use one of
`bslib`'s page layout functions instead of Shiny's built-in page
layouts. In other words, use

-   `bslib::page()` instead of `shiny::basicPage()` or `bootstrapPage()`
-   `page_fluid()` instead of `fluidPage()`
-   `page_fixed()` instead of `fixedPage()`
-   `page_fillable()` instead of `fillPage()`
-   `page_sidebar()` instead of `sidebarLayout()` within `fluidPage()`
-   `page_navbar()` instead of `navbarPage()`.

You should also set a theme using `bslib::bs_theme()`; this allows you
to specify `version = 5` so that your app will continue to use Bootstrap
5 even after Bootstrap 6 is released and `bslib` starts to support it.
(We may add support for Bootstrap 6 to `inshiny` in the future, but
better to make sure your app stays future-proof either way!)

All `inshiny` widgets should be wrapped in a call to `inline()`, which
creates a "paragraph" or "line" of text in which you can mix in widgets
and arbitrary HTML elements. You can then use widgets such as
`inline_text()` or `inline_slider()` within this element. Here is a
simple demo of a Shiny UI that uses most of the widgets provided by
`inshiny`:

```{r}
ui <- page_fixed(
    theme = bs_theme(version = 5, preset = "quartz"),

    h1("Temperature plot"),

    plotOutput("plot", width = 480, height = 320),

    br(),

    inline("Start on ", inline_date("start_date", "2025-01-01"),
        " and plot for ", inline_number("num_days", 365), " days."),

    inline("Average temperature: ", inline_slider("avg_temp", 20, 0, 40),
        " °C. Range: ±", inline_select("temp_range", c(5, 10, 15), 10), " °C."),

    inline("Hemisphere: ", inline_switch("southern", FALSE,
        on = "Southern", off = "Northern")),

    inline(inline_button("colour", "Change colour"), " ",
        inline_link("reset", "Reset"))
)
```

The page starts with a title (`h1`) and a `shiny::plotOutput()` that
we'll use to plot average temperatures according to the settings below.
There's a line break (`br`) to space things out, then four `inline`
lines containing the `inline_*` widgets provided by `inshiny`. It looks
something like this:

![](images/vignette_1.jpg){width="450"}

Compare this to a similar layout with basic Shiny inputs:

```{r}
ui <- page_fixed(
    theme = bs_theme(version = 5, preset = "quartz"),

    h1("Temperature plot"),

    plotOutput("plot", width = 480, height = 320),

    br(),

    dateInput("start_date", "Start date", "2025-01-01"),
    numericInput("num_days", "Number of days", 365),
    sliderInput("avg_temp", "Average temperature (°C)", 0, 40, 20),
    selectInput("temp_range", "Temperature range (± °C)", c(5, 10, 15), 10),
    checkboxInput("southern", "Southern hemisphere", FALSE),
    actionButton("colour", "Change colour"),
    actionLink("reset", "Reset"),
)
```

The controls for this version take up far more space:

![](images/vignette_2.jpg){width="450"}

Of course, you could save some vertical space by laying out the elements
in a grid, but they are still rather large compared to the `inshiny`
widgets.

We complete the app using server code to handle the inputs:

```{r}
server <- function(input, output, session)
{
    output$plot <- renderPlot({
        date <- input$start_date + seq_len(input$num_days) - 1;
        xpts <- as.POSIXlt(date)$yday; # day number, 0-365
        temperature <- cos(2 * pi * xpts / 364) *
            ifelse(input$southern, 1, -1) * as.numeric(input$temp_range) +
            input$avg_temp;
        oldpar <- par(mar = c(5, 5, 1, 2))
        plot(date, temperature, type = "l", ylim = c(-15, 55),
            col = input$colour %% 16 + 1, lwd = 3)
        abline(h = 0, col = 8, lty = 2)
        par(oldpar)
    })

    observeEvent(input$reset, {
        update_inline("start_date", value = "2025-01-01")
        update_inline("num_days", value = 365)
        update_inline("avg_temp", value = 20)
        update_inline("temp_range", value = "10")
        update_inline("southern", value = FALSE)
    })
}
```

This illustrates the use of `update_inline()` to reset the widgets to
their original values. You would then launch the app with
`shinyApp(ui, server)`.
