R/layout-condensed.R

Defines functions layout_condensed

Documented in layout_condensed

#' @title Build condensed layout structure
#' @description Build the HTML structure for the condensed layout. This
#' accepts either a single navbar tag, or a named list with `top` and `side`
#' for compatibility with combo-like callers. Returns the full page tag
#' structure (including optional top navbar, page wrapper, body and footer)
#' @param navbar Navbar component (or list with top/side for combo)
#' @param sidebar Sidebar component (not typically used when passing list)
#' @param body Body content
#' @param footer Footer component
#' @param theme Theme name (light/dark) passed for downstream use
#' @param color Primary color name
#' @param show_theme_button Logical whether to show theme toggles
#' @keywords internal
layout_condensed <- function(navbar, sidebar, body, footer, theme = "light", color = NULL, show_theme_button = TRUE) {
  top_nav <- NULL
  side_nav <- NULL

  # Support both list(navbar = list(top=..., side=...)) and direct tags
  if (is.list(navbar) && !inherits(navbar, "shiny.tag")) {
    top_nav <- navbar$top
    side_nav <- navbar$side
  } else if (!is.null(navbar)) {
    if (inherits(navbar, "shiny.tag")) {
      tag_name <- navbar$name
      if (tag_name == "header") {
        top_nav <- navbar
      } else if (tag_name == "aside") {
        # For condensed (horizontal navbar) layout, aside tag is the source for top navbar
        top_nav <- navbar
      } else if (tag_name == "ul") {
        # treat ul as top navigation for condensed layout
        top_nav <- navbar
      }
    }
  }

  # Build header with proper structure matching official template
  header_tag <- NULL
  if (!is.null(top_nav) && inherits(top_nav, "shiny.tag") && top_nav$name == "aside") {
    # Extract components from aside tag
    container <- NULL
    for (ch in top_nav$children) {
      if (inherits(ch, "shiny.tag") && ch$name == "div" && grepl("container-fluid", ch$attribs$class %||% "")) {
        container <- ch
        break
      }
    }

    brand_tag <- NULL
    nav_items <- list()
    theme_items <- list()

    if (!is.null(container)) {
      for (c2 in container$children) {
        # Skip non-tag children (like HTML comments)
        if (!inherits(c2, "shiny.tag")) next

        # Extract brand
        if (c2$name == "div" && grepl("navbar-brand", c2$attribs$class %||% "")) {
          if (length(c2$children) > 0) brand_tag <- c2$children[[1]]
        }

        # Extract menu items from the collapsible div
        if (c2$name == "div" &&
          grepl("collapse", c2$attribs$class %||% "") &&
          grepl("navbar-collapse", c2$attribs$class %||% "") &&
          !is.null(c2$attribs$id) &&
          c2$attribs$id == "sidebar-menu") {
          for (c3 in c2$children) {
            if (inherits(c3, "shiny.tag") && c3$name == "ul") {
              # The ul children might be wrapped in a list - unwrap them
              all_items <- list()
              for (child in c3$children) {
                if (is.list(child) && !inherits(child, "shiny.tag")) {
                  # Unwrap the list
                  all_items <- c(all_items, child)
                } else if (inherits(child, "shiny.tag")) {
                  all_items <- c(all_items, list(child))
                }
              }

              # Separate theme items from regular nav items
              for (item in all_items) {
                if (inherits(item, "shiny.tag") && item$name == "li") {
                  if (!is.null(item$attribs$class) && grepl("mt-auto", item$attribs$class %||% "")) {
                    # Theme toggle item - extract the <a> tags directly only if show_theme_button is TRUE
                    if (isTRUE(show_theme_button)) {
                      for (child in item$children) {
                        # Child might be wrapped in a list
                        if (is.list(child) && !inherits(child, "shiny.tag")) {
                          for (a_tag in child) {
                            if (inherits(a_tag, "shiny.tag") && a_tag$name == "a") {
                              theme_items <- c(theme_items, list(a_tag))
                            }
                          }
                        } else if (inherits(child, "shiny.tag") && child$name == "a") {
                          theme_items <- c(theme_items, list(child))
                        }
                      }
                    }
                  } else {
                    # Regular nav item - update dropdown aria-expanded
                    if (length(item$children) > 0) {
                      a <- item$children[[1]]
                      if (inherits(a, "shiny.tag") && !is.null(a$attribs[["data-bs-toggle"]]) && a$attribs[["data-bs-toggle"]] == "dropdown") {
                        a$attribs[["aria-expanded"]] <- "false"
                        a$attribs[["data-bs-auto-close"]] <- "outside"
                        item$children[[1]] <- a
                      }
                    }
                    nav_items <- c(nav_items, list(item))
                  }
                }
              }
            }
          }
        }
      }
    }

    # Build header with condensed layout structure
    header_tag <- header(
      class = "navbar navbar-expand-md d-print-none",
      div(
        class = "container-xl",
        # Navbar toggler
        button(
          class = "navbar-toggler",
          type = "button",
          `data-bs-toggle` = "collapse",
          `data-bs-target` = "#navbar-menu",
          `aria-controls` = "navbar-menu",
          `aria-expanded` = "false",
          `aria-label` = "Toggle navigation",
          span(class = "navbar-toggler-icon")
        ),
        # Brand/logo
        if (!is.null(brand_tag)) {
          div(class = "navbar-brand navbar-brand-autodark", brand_tag)
        },
        # Theme buttons on the right (order-md-last)
        if (length(theme_items) > 0) {
          div(
            class = "navbar-nav flex-row order-md-last",
            div(
              class = "d-none d-md-flex",
              div(class = "nav-item", theme_items)
            )
          )
        },
        # Collapsible navbar menu
        div(
          class = "collapse navbar-collapse",
          id = "navbar-menu",
          ul(
            class = "navbar-nav",
            nav_items
          )
        )
      )
    )
  } else if (!is.null(top_nav)) {
    header_tag <- top_nav
  }

  tagList(
    script(src = "dist/js/tabler-theme.min.js"),
    div(
      class = "page",
      # Top navbar
      if (!is.null(header_tag)) header_tag,
      # Page wrapper
      div(
        class = "page-wrapper",
        body,
        if (!is.null(footer)) footer
      )
    )
  )
}

Try the tabler package in your browser

Any scripts or data that you put into this service are public.

tabler documentation built on Nov. 5, 2025, 6:05 p.m.