---
title: "glasstabs Cheatsheet"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{glasstabs Cheatsheet}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r setup, include=FALSE}
knitr::opts_chunk$set(eval = FALSE)
```

## One rule

Call `useGlassTabs()` once somewhere in your UI before using any widget.

```{r}
ui <- fluidPage(
  useGlassTabs(),
  # widgets go here
)
```

## Tabs: basic pattern

```{r}
ui <- fluidPage(
  useGlassTabs(),
  glassTabsUI(
    "main",
    glassTabPanel("overview", "Overview", selected = TRUE, h3("Overview")),
    glassTabPanel("details", "Details", h3("Details"))
  )
)

server <- function(input, output, session) {
  active_tab <- glassTabsServer("main")
}
```

## Tabs: inside a Shiny module

The key rule: use `ns()` in the UI, use the **bare** id in the server.

```{r}
# UI function
my_ui <- function(id) {
  ns <- NS(id)
  tagList(
    useGlassTabs(),
    glassTabsUI(
      ns("tabs"),                          # ns() wraps the id in the UI
      glassTabPanel("summary", "Summary", selected = TRUE, h3("Summary")),
      glassTabPanel("detail",  "Detail",  h3("Detail"))
    )
  )
}

# Server function
my_server <- function(id) {
  moduleServer(id, function(input, output, session) {
    active <- glassTabsServer("tabs")    # bare id — no ns() here

    observeEvent(active(), {
      message("Active tab: ", active())
    })
  })
}

# App
ui     <- fluidPage(my_ui("explorer"))
server <- function(input, output, session) my_server("explorer")
if (interactive()) shinyApp(ui, server)
```

## Tabs: dynamic values and selected

```{r}
# Build panels from data
tab_defs <- list(
  list(value = "revenue", label = "Revenue"),
  list(value = "orders",  label = "Orders"),
  list(value = "returns", label = "Returns")
)

panels <- lapply(tab_defs, function(t) {
  glassTabPanel(t$value, t$label, h3(t$label))
})

ui <- fluidPage(
  useGlassTabs(),
  do.call(glassTabsUI, c(list("metrics", selected = "orders"), panels))
)

server <- function(input, output, session) {
  active <- glassTabsServer("metrics")
}
```

> Note: tab `value` strings must be unique — `glassTabsUI()` will error on duplicates. Values are plain strings; they do not need to match labels.

## Tabs: server actions

```{r}
# Switch active tab
updateGlassTabsUI(session, "main", "details")

# Hide or show a tab
hideGlassTab(session, "main", "admin")
showGlassTab(session, "main", "admin")

# Append or remove a tab at runtime
appendGlassTab(
  session, "main",
  glassTabPanel("compare", "Compare", h3("Compare")),
  select = TRUE
)
removeGlassTab(session, "main", "compare")
```

## Tabs: common options

```{r}
glassTabsUI(
  "main",
  glassTabPanel("a", "A", selected = TRUE, p("A")),
  glassTabPanel("b", "B", p("B")),
  selected = "a",
  wrap = TRUE,
  compact = FALSE,      # set TRUE inside dashboard cards for tighter layout
  extra_ui = tags$div("Right side UI"),
  theme = "light"
)
```

## Tabs: compact mode (dashboard cards)

Use `compact = TRUE` when embedding glasstabs inside a tight layout such as a `bs4Dash::bs4Card()`. It reduces margins, padding, and font size so the widget does not overflow the card.

```{r}
bs4Card(
  title = "Explorer", width = 12,
  glassTabsUI(
    "tabs",
    glassTabPanel("a", "A", selected = TRUE, p("Content")),
    glassTabPanel("b", "B", p("More content")),
    compact = TRUE      # reduced spacing for card context
  )
)
```

## Multi-select: basic pattern

```{r}
choices <- c(Revenue = "revenue", Orders = "orders", Returns = "returns")

ui <- fluidPage(
  useGlassTabs(),
  glassMultiSelect("metric", choices),
  glassFilterTags("metric")
)

server <- function(input, output, session) {
  metric <- glassMultiSelectValue(input, "metric")
}
```

## Multi-select: update from server

```{r}
updateGlassMultiSelect(
  session,
  "metric",
  selected = c("revenue", "orders"),
  check_style = "filled"
)

# Clear selection
updateGlassMultiSelect(session, "metric", selected = character(0))
```

## Multi-select: useful arguments

```{r}
glassMultiSelect(
  "metric",
  choices,
  selected = unname(choices),
  label = "Metrics",
  placeholder = "Choose metrics",
  all_label = "All metrics",
  check_style = "checkbox",
  show_style_switcher = TRUE,
  show_select_all = TRUE,
  show_clear_all = TRUE,
  theme = "dark"
)
```

## Single-select: basic pattern

```{r}
regions <- c("All Regions" = "all", North = "north", South = "south")

ui <- fluidPage(
  useGlassTabs(),
  glassSelect("region", regions, selected = "all")
)

server <- function(input, output, session) {
  region <- glassSelectValue(input, "region")
}
```

## Single-select: update from server

```{r}
updateGlassSelect(session, "region", selected = "south")

# Clear value
updateGlassSelect(session, "region", selected = character(0))
```

## Single-select: useful arguments

```{r}
glassSelect(
  "region",
  regions,
  selected = "all",
  label = "Region",
  placeholder = "Pick a region",
  searchable = TRUE,
  clearable = TRUE,
  include_all = FALSE,
  check_style = "checkbox",
  theme = "light"
)
```

## Tabs: inside bs4Dash

`glassTabsUI()` works inside `bs4Dash::tabItem()`. Place `useGlassTabs()` in the `dashboardBody()` (or anywhere before the first widget — once is enough).

```{r}
library(shiny)
library(bs4Dash)

ui <- bs4DashPage(
  header  = bs4DashNavbar(),
  sidebar = bs4DashSidebar(
    bs4SidebarMenu(
      bs4SidebarMenuItem("Explorer", tabName = "explorer", icon = icon("table"))
    )
  ),
  body = bs4DashBody(
    useGlassTabs(),                     # place once, anywhere in the body
    bs4TabItems(
      bs4TabItem(
        tabName = "explorer",
        bs4Card(
          title = "Data explorer",
          width = 12,
          glassTabsUI(
            "s3_tabs",
            glassTabPanel("buckets", "Buckets", selected = TRUE,
              p("List of S3 buckets here.")
            ),
            glassTabPanel("objects", "Objects",
              p("Object browser here.")
            ),
            glassTabPanel("preview", "Preview",
              p("File preview here.")
            )
          )
        )
      )
    )
  )
)

server <- function(input, output, session) {
  active <- glassTabsServer("s3_tabs")
}

if (interactive()) shinyApp(ui, server)
```

> `useGlassTabs()` only needs to appear once. Inside a module that renders into a bs4Dash layout, call it in your module's UI function.

## Theme helpers

```{r}
# Tabs
glassTabsUI(
  "main",
  glassTabPanel("a", "A", selected = TRUE, p("A")),
  theme = glass_tab_theme(
    halo_bg = "rgba(251,191,36,0.15)",
    tab_active_text = "#fef3c7"
  )
)

# Select widgets
glassMultiSelect(
  "metric", choices,
  theme = glass_select_theme(
    mode = "dark",
    accent_color = "#38bdf8"
  )
)
```

## Input values at a glance

| Widget | Server value |
|----|----|
| `glassTabsUI("main", ...)` | `input[["main-active_tab"]]` or `glassTabsServer("main")()` |
| `glassMultiSelect("metric", ...)` | `input$metric` or `glassMultiSelectValue(input, "metric")$selected()` |
| multi-select style | `input$metric_style` or `glassMultiSelectValue(input, "metric")$style()` |
| `glassSelect("region", ...)` | `input$region` or `glassSelectValue(input, "region")()` |

## conditionalPanel integration

Use `glassTabCondition()` to avoid constructing the input key manually:

```{r}
# Instead of: condition = "input['main-active_tab'] === 'details'"
conditionalPanel(
  condition = glassTabCondition("main", "details"),
  p("Only visible on the Details tab.")
)

# Inside a module — pass the same id as glassTabsUI():
# glassTabsUI(ns("tabs"), ...)
conditionalPanel(
  condition = glassTabCondition(NS("mymod")("tabs"), "details"),
  p("Only visible on the Details tab.")
)
```

## View the changelog

```{r, eval=FALSE}
glasstabs_news()      # prints changelog to the console
```

## Common gotchas

-   `useGlassTabs()` must be called once in the UI.
-   In a module, use `ns("tabs")` in the UI but pass the bare `"tabs"` to `glassTabsServer()` — not `ns("tabs")`. Passing a namespaced id (containing `-`) will produce a warning with a suggested fix.
-   `glassTabPanel()` values must be unique within a `glassTabsUI()` call — duplicates cause an error naming the offending value.
-   `selected` in `glassTabsUI()` refers to the tab **value**, not the label.
-   `glassSelectValue()` returns a reactive function, not a list.
-   `glassMultiSelectValue()` returns a list with `selected()` and `style()`.
-   For `glassMultiSelect()`, `selected` should use choice values, not labels.
-   Use `compact = TRUE` inside bs4Dash cards or any tight layout to reduce widget spacing.
-   If you add new JS/CSS behavior during development, reinstall or `load_all()` before retesting.
