% glasstabs cheatsheet  v0.3.1
% Compile with: pdflatex glasstabs-cheatsheet.tex
\documentclass[9pt,a4paper]{article}

\usepackage[a4paper,margin=0.9cm,landscape]{geometry}
\usepackage{multicol}
\usepackage[T1]{fontenc}
\usepackage[utf8]{inputenc}
\usepackage{xcolor}
\usepackage{listings}
\usepackage{hyperref}
\usepackage[expansion=false]{microtype}
\usepackage{enumitem}
\usepackage{booktabs}
\usepackage{array}

% ── Colours ──────────────────────────────────────────────────────────────────
\definecolor{gtblue}{RGB}{30,64,175}
\definecolor{gtaccent}{RGB}{126,195,247}
\definecolor{gtgray}{RGB}{100,116,139}
\definecolor{gtgreen}{RGB}{22,163,74}
\definecolor{gtyellow}{RGB}{180,120,0}
\definecolor{codebg}{RGB}{241,245,249}
\definecolor{codefg}{RGB}{15,23,42}
\definecolor{codecomment}{RGB}{100,116,139}
\definecolor{codestring}{RGB}{21,128,61}
\definecolor{codekw}{RGB}{30,64,175}
\definecolor{sectionbg}{RGB}{30,64,175}

% ── Fonts ────────────────────────────────────────────────────────────────────
\renewcommand{\familydefault}{\sfdefault}

% ── Listings (R code blocks) ─────────────────────────────────────────────────
\lstdefinestyle{Rstyle}{
  language=R,
  basicstyle=\ttfamily\tiny,
  backgroundcolor=\color{codebg},
  commentstyle=\color{codecomment}\itshape,
  stringstyle=\color{codestring},
  keywordstyle=\color{codekw}\bfseries,
  breaklines=true,
  breakatwhitespace=false,
  columns=flexible,
  keepspaces=true,
  frame=none,
  xleftmargin=3pt,
  xrightmargin=3pt,
  aboveskip=2pt,
  belowskip=1pt,
  showstringspaces=false
}
\lstset{style=Rstyle}

% ── Inline helpers ───────────────────────────────────────────────────────────
\newcommand{\code}[1]{\texttt{\footnotesize\color{codefg}#1}}
\newcommand{\gtfn}[1]{\texttt{\footnotesize\color{codekw}#1()}}
\newcommand{\gtarg}[1]{\texttt{\footnotesize\color{gtgreen}#1}}
\newcommand{\gtval}[1]{\texttt{\footnotesize\color{gtyellow}#1}}

% ── Section header (no titlesec — avoids # conflicts) ────────────────────────
\newcommand{\mysection}[1]{%
  \vspace{4pt}%
  \noindent\colorbox{sectionbg}{%
    \parbox{\dimexpr\linewidth-2\fboxsep\relax}{%
      \strut\textbf{\small\color{white}#1}\strut%
    }%
  }%
  \vspace{2pt}\par%
}

% ── List tweaks ───────────────────────────────────────────────────────────────
\setlist[itemize]{
  noitemsep, topsep=1pt, leftmargin=*,
  label=\textcolor{gtaccent}{\textbullet}
}

% ── Page layout ──────────────────────────────────────────────────────────────
\setlength{\columnseprule}{0.4pt}
\setlength{\columnsep}{8pt}
\setlength{\parskip}{2pt}
\setlength{\parindent}{0pt}
\pagestyle{empty}
\hypersetup{hidelinks}

% ─────────────────────────────────────────────────────────────────────────────
\begin{document}
\begin{multicols}{4}

% ── Header ───────────────────────────────────────────────────────────────────
{\large\bfseries\color{gtblue}glasstabs}%
\hfill{\footnotesize\color{gtgray}v0.3.1}\\[1pt]
{\footnotesize\color{gtgray}Glass-morphism Shiny widgets}\\[-3pt]
\rule{\linewidth}{0.6pt}

% ══════════════════════════════════════════════════════
\mysection{Setup}

Call once in every UI:
\begin{lstlisting}
library(glasstabs)
ui <- fluidPage(
  useGlassTabs(),
  # widgets go here
)
\end{lstlisting}

Run a built-in demo:
\begin{lstlisting}
runGlassExample()       # list all
runGlassExample("smoke-test")
runGlassExample("basic")
runGlassExample("bs4dash")
\end{lstlisting}

View changelog in console:
\begin{lstlisting}
glasstabs_news()
\end{lstlisting}

% ══════════════════════════════════════════════════════
\mysection{Tab widget --- UI}

\begin{lstlisting}
glassTabsUI(
  id,              # module id
  ...,             # glassTabPanel()s
  selected = NULL, # initial tab value
  wrap     = TRUE,
  compact  = FALSE,# reduced spacing
  extra_ui = NULL, # right-side widget
  theme    = "dark"# or "light"
)

glassTabPanel(
  value,          # unique tab id
  label,          # button text
  ...,            # pane content
  icon = NULL,    # shiny::icon() etc.
  selected = FALSE
)
\end{lstlisting}

\textbf{Example:}
\begin{lstlisting}
glassTabsUI(
  "main",
  glassTabPanel("a", "Alpha",
    selected = TRUE,
    p("Alpha content")),
  glassTabPanel("b", "Beta",
    icon = icon("star"),
    p("Beta content"))
)
\end{lstlisting}

% ══════════════════════════════════════════════════════
\mysection{Tab widget --- server}

\begin{lstlisting}
server <- function(input, output, session) {
  # Reactive active tab value
  active <- glassTabsServer("main")
  # With bookmarking (default TRUE)
  active <- glassTabsServer("main",
              bookmark = TRUE)
  observe(print(active()))
}
\end{lstlisting}

\textbf{Module pattern:}\\
UI:~\code{glassTabsUI(\textbf{ns("tabs")}, ...)}\\
Server:~\code{glassTabsServer(\textbf{"tabs"})}\\
{\tiny\color{gtgray}Pass bare id to server --- not \code{ns("tabs")}.}

% ══════════════════════════════════════════════════════
\mysection{Tab server actions}

\begin{lstlisting}
# Switch active tab
updateGlassTabsUI(session, "main", "b")

# Show / hide
showGlassTab(session, "main", "admin")
hideGlassTab(session, "main", "admin")

# Disable / enable (stays visible)
disableGlassTab(session, "main", "b")
enableGlassTab(session,  "main", "b")

# Badge counts  (0 or NA = hide)
updateGlassTabBadge(session, "main",
  "inbox", count = 5L)
updateGlassTabBadge(session, "main",
  "inbox", count = 0L)

# Add / remove at runtime
appendGlassTab(session, "main",
  glassTabPanel("new", "New", p("...")),
  select = TRUE)
removeGlassTab(session, "main", "new")
\end{lstlisting}

% ══════════════════════════════════════════════════════
\mysection{conditionalPanel helper}

\gtfn{glassTabCondition} builds the JS condition string:
\begin{lstlisting}
# Instead of:
# "input['main-active_tab'] === 'b'"
conditionalPanel(
  condition =
    glassTabCondition("main", "b"),
  p("Only on tab b")
)

# Inside a module (same id as UI):
glassTabCondition(ns("tabs"), "b")
\end{lstlisting}

% ══════════════════════════════════════════════════════
\mysection{Dynamic tabs}

\begin{lstlisting}
# UI placeholder
glassTabsOutput("dyn_tabs")

# Server render
output$dyn_tabs <- renderGlassTabs({
  panels <- lapply(my_data(),
    function(r)
      glassTabPanel(r$id, r$label,
        p(r$id)))
  do.call(glassTabsUI,
    c(list("dyn"), panels))
})
\end{lstlisting}
{\tiny\color{gtgray}JS re-initialises automatically after each render.}

% ══════════════════════════════════════════════════════
\mysection{Bookmarking}

Enable on the app, \gtfn{glassTabsServer} handles save/restore:
\begin{lstlisting}
shinyApp(ui, server,
  enableBookmarking = "url")
\end{lstlisting}

Opt out per widget:
\begin{lstlisting}
glassTabsServer("tabs",
  bookmark = FALSE)
\end{lstlisting}

% ══════════════════════════════════════════════════════
\mysection{Tab theming}

\begin{lstlisting}
# String preset
glassTabsUI("t", ..., theme = "light")
glassTabsUI("t", ..., theme = "dark")

# Custom object
glassTabsUI("t", ...,
  theme = glass_tab_theme(
    halo_bg = "rgba(251,191,36,.15)",
    tab_active_text = "#fef3c7"
  )
)
\end{lstlisting}

\gtfn{glass\_tab\_theme} args:
\begin{itemize}
  \item \gtarg{tab\_text}, \gtarg{tab\_active\_text}
  \item \gtarg{halo\_bg}, \gtarg{halo\_border}
  \item \gtarg{content\_bg}, \gtarg{content\_border}
  \item \gtarg{card\_bg}, \gtarg{card\_text}
\end{itemize}

% ══════════════════════════════════════════════════════
\mysection{Multi-select --- UI}

\begin{lstlisting}
choices <- c(Revenue = "rev",
             Orders  = "ord")

glassMultiSelect(
  inputId,
  choices,
  selected          = NULL,
  label             = NULL,
  placeholder       = "Select...",
  all_label         = "All",
  check_style       = "checkbox",
  show_style_switcher = FALSE,
  show_select_all   = TRUE,
  show_clear_all    = TRUE,
  searchable        = TRUE,
  theme             = "dark"
)
\end{lstlisting}

% ══════════════════════════════════════════════════════
\mysection{Multi-select --- server}

\begin{lstlisting}
# Reactive: selected() and style()
vals <- glassMultiSelectValue(input, "m")
vals$selected()
vals$style() # "checkbox"|"filled"|"dot"

# Update
updateGlassMultiSelect(session, "m",
  selected    = c("rev", "ord"),
  check_style = "filled")

# Clear all
updateGlassMultiSelect(session, "m",
  selected = character(0))

# Filter-tag display (in UI)
glassFilterTags("m")
\end{lstlisting}

% ══════════════════════════════════════════════════════
\mysection{Single-select --- UI}

\begin{lstlisting}
regions <- c("All" = "all",
             North = "n")

glassSelect(
  inputId,
  choices,
  selected    = NULL,
  label       = NULL,
  placeholder = "Pick one...",
  searchable  = FALSE,
  clearable   = FALSE,
  include_all = FALSE,
  check_style = "checkbox",
  theme       = "dark"
)
\end{lstlisting}

% ══════════════════════════════════════════════════════
\mysection{Single-select --- server}

\begin{lstlisting}
# Reactive value
region <- glassSelectValue(input, "r")
region()

# Update from server
updateGlassSelect(session, "r",
  selected = "south")

# Clear
updateGlassSelect(session, "r",
  selected = character(0))
\end{lstlisting}

% ══════════════════════════════════════════════════════
\mysection{Select theming}

\begin{lstlisting}
glassMultiSelect("m", choices,
  theme = glass_select_theme(
    mode         = "dark",
    accent_color = "#38bdf8",
    bg_color     = "rgba(9,20,42,.97)",
    text_color   = "#cfe6ff"
  )
)
\end{lstlisting}

\gtfn{glass\_select\_theme} args:
\begin{itemize}
  \item \gtarg{mode}: \gtval{"dark"} or \gtval{"light"}
  \item \gtarg{bg\_color}, \gtarg{border\_color}
  \item \gtarg{text\_color}, \gtarg{accent\_color}
  \item \gtarg{label\_color}
\end{itemize}

% ══════════════════════════════════════════════════════
\mysection{Input values}

{\footnotesize
\begin{tabular}{@{}p{1.6cm}p{3.1cm}@{}}
\toprule
\textbf{Widget} & \textbf{Input key} \\
\midrule
tabs         & \code{\tiny input[["id-active\_tab"]]} \\
multi-select & \code{input\$inputId} \\
multi style  & \code{\tiny input\$inputId\_style} \\
single       & \code{input\$inputId} \\
\bottomrule
\end{tabular}
}

\vspace{3pt}
Reactive helpers:
\begin{itemize}
  \item \gtfn{glassTabsServer} $\to$ \code{active()}
  \item \gtfn{glassMultiSelectValue} $\to$ \code{vals\$selected()}
  \item \gtfn{glassSelectValue} $\to$ \code{region()}
\end{itemize}

% ══════════════════════════════════════════════════════
\mysection{bs4Dash integration}

\begin{lstlisting}
library(bs4Dash)
ui <- bs4DashPage(
  header  = bs4DashNavbar(),
  sidebar = bs4DashSidebar(...),
  body    = bs4DashBody(
    useGlassTabs(), # place once here
    bs4TabItems(
      bs4TabItem("explorer",
        bs4Card(width = 12,
          glassTabsUI("tabs",
            glassTabPanel("a","A",...),
            glassTabPanel("b","B",...))
        )
      )
    )
  )
)
server <- function(input, output, session) {
  active <- glassTabsServer("tabs")
}
\end{lstlisting}

% ══════════════════════════════════════════════════════
\mysection{Common gotchas}

\begin{itemize}
  \item \gtfn{useGlassTabs} once in UI.
  \item Modules: \code{ns("tabs")} in UI, bare \code{"tabs"} in server.
  \item Duplicate \code{glassTabPanel()} values $\to$ error.
  \item \gtarg{selected} = tab \textbf{value}, not label.
  \item \gtfn{glassSelectValue} returns reactive, not list.
  \item \gtfn{glassMultiSelectValue} returns
        \code{list(selected(), style())}.
  \item Multi-select \gtarg{selected} uses values, not labels.
  \item After \code{load\_all()}, reload browser to pick up JS changes.
\end{itemize}

\vfill
{\tiny\color{gtgray}
\href{https://prigasg.github.io/glasstabs/}{prigasg.github.io/glasstabs}
\hfill MIT \copyright\ glasstabs authors}

\end{multicols}
\end{document}
