diff --git a/NEWS.md b/NEWS.md index bdaba30..0a03a90 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,12 @@ # breedverse NEWS +## breedverse 0.3.2 + +* Add styles to CSS +* Add custom.js for collapsible headers +* Add breedverse logo +* Add header to help page + ## breedverse 0.3.1 * Remove Qploidy while it is going throught repository transition diff --git a/R/app_ui.R b/R/app_ui.R index b59e714..b5a6252 100644 --- a/R/app_ui.R +++ b/R/app_ui.R @@ -13,12 +13,21 @@ app_ui <- function(request) { tagList( # Leave this function for adding external resources golem_add_external_resources(), + # Dynamic sidebar color theme — only sets the :root CSS variables + # ── Sidebar color theme ────────────────────────────────────────────────────── + # Change this value to switch the active sidebar menu item color. + # Available options: "azure", "green", "yellow", "grey", "purple", "red" + # ───────────────────────────────────────────────────────────────────────────── + tags$head(tags$style(HTML(sprintf( + ":root { --sidebar-core: var(--%s-core); --sidebar-lite: var(--%s-lite); --sidebar-deep: var(--%s-deep); }", + "azure", "azure", "azure" + )))), # Your application UI logic bs4DashPage( skin = "black", bs4DashNavbar( title = tagList( - tags$img(src = 'www/BreedingInsight.png', height = '40', width = '50'), + tags$img(src = 'www/breedverse_logo.png', height = '40', width = '35'), ), rightUi = tags$li( class = "dropdown", @@ -54,9 +63,11 @@ app_ui <- function(request) { expandOnHover = TRUE, sidebarMenu(id = "MainMenu", flat = FALSE, - tags$li(class = "header", style = "color: grey; margin-top: 10px; margin-bottom: 10px; padding-left: 15px;", "Menu"), + tags$li(class = "header","Menu"), menuItem("Home", tabName = "welcome", icon = icon("house"),startExpanded = FALSE), menuItem("Install modules", tabName = "install", icon = icon("share-from-square")), + tags$li(class = "header", "Available Modules"), + # conditionalPanel( # condition = "output.qploidyInstalled == true", @@ -66,7 +77,6 @@ app_ui <- function(request) { conditionalPanel( condition = "output.familiaInstalled == true", - tags$li(class = "header", style = "color: grey; margin-top: 18px; margin-bottom: 10px; padding-left: 15px;", "Ancestry (R/familia)"), menuItem( "familia", icon = icon("seedling"), @@ -78,13 +88,11 @@ app_ui <- function(request) { conditionalPanel( condition = "output.allomateInstalled == true", - tags$li(class = "header", style = "color: grey; margin-top: 18px; margin-bottom: 10px; padding-left: 15px;", "Mating Estimation (R/AlloMate)"), menuItem("AlloMate", tabName = "allomate", icon = icon("diagram-project")) ), conditionalPanel( condition = "output.BIGappInstalled == true", - tags$li(class = "header", style = "color: grey; margin-top: 18px; margin-bottom: 10px; padding-left: 15px;", "Genotype Processing"), menuItem( "BIGapp", icon = icon("dna"), @@ -102,7 +110,6 @@ app_ui <- function(request) { ), conditionalPanel( condition = "output.genobrewInstalled == true", - tags$li(class = "header", style = "color: grey; margin-top: 18px; margin-bottom: 10px; padding-left: 15px;", "Marker Panel Test &\n CNV Profiles"), menuItem( "GenoBrew", icon = icon("dna"), @@ -119,27 +126,27 @@ app_ui <- function(request) { ), footer = dashboardFooter( right = div( - style = "display: flex; align-items: center;", # Align text and images horizontally + class = "dashboard-footer-right", # Align text and images horizontally div( - style = "display: flex; flex-direction: column; margin-right: 15px; text-align: right;", + class = "dashboard-footer-text", # Style the text div("2026 Breeding Insight"), div("Funded by USDA through UF|IFAS") ), div( a( img(src = "www/usda-logo-color.png", height = "45px"), - style = "margin-right: 15px;" + class = "dashboard-footer-logo" ), a( - img(src = "www/cornell_seal_simple_web_b31b1b.png", height = "45px") + img(src = "www/IFAS.jpg", height = "45px") ), a( - img(src = "www/IFAS.jpg", height = "45px") + img(src = "www/cornell_seal_simple_web_b31b1b.png", height = "45px") ) ) ), left = div( - style = "display: flex; align-items: center; height: 100%;", + class = "dashboard-footer-left", sprintf("v%s", as.character(utils::packageVersion("Breedverse")))) ), dashboardBody( @@ -255,26 +262,11 @@ golem_add_external_resources <- function() { path = app_sys("app/www"), app_title = "Breedverse" ), - # Add here other external resources - # for example, you can add shinyalert::useShinyalert() - tags$style(HTML(" - /* Ensure box collapse/expand buttons are always on top */ - .card-tools { position: relative; z-index: 10; } - /* Make collapse/expand icons visible on white box headers */ - .card-tools .btn-tool { color: #495057 !important; } - .card-tools .btn-tool:hover { color: #212529 !important; } - ")), - tags$script(HTML(" - $(document).ready(function() { - // On page load: mirror active class from
  • onto for CSS targeting - $('#cnv_1-sample_select_tabs li.active > a').addClass('active'); - - // After each tab switch (content already swapped): sync active on only - $(document).on('shown.bs.tab', '#cnv_1-sample_select_tabs a[data-toggle=\"tab\"]', function(e) { - $('#cnv_1-sample_select_tabs a[data-toggle=\"tab\"]').removeClass('active'); - $(e.target).addClass('active'); - }); - }); - ")) + tags$link( + rel = "stylesheet", + type = "text/css", + href = "www/custom.css" + ), + tags$script(src = "www/custom.js") ) } diff --git a/R/mod_help.R b/R/mod_help.R index 9bb508d..d53e368 100644 --- a/R/mod_help.R +++ b/R/mod_help.R @@ -7,12 +7,22 @@ #' @noRd #' #' @importFrom shiny NS tagList includeMarkdown +#' mod_help_ui <- function(id){ ns <- NS(id) tagList( fluidPage( column(width=12), column(width=12, + div( + style = "padding: 20px;", + div( + style = "text-align: center; margin-bottom: 25px; padding-bottom: 15px; border-bottom: 2px solid #17a2b8;", + tags$h2("Help Documentation", style = "color: #17a2b8; margin-bottom: 10px;"), + tags$p("Click a module to expand its help section.", + style = "color: #666; font-size: 16px;") + )), + # conditionalPanel( # condition = "output.qploidyInstalled == true", # box(title="Ploidy Estimation", id = "Qploidy_box",width = 12, collapsible = TRUE, collapsed = TRUE, status = "info", solidHeader = TRUE, diff --git a/R/mod_home.R b/R/mod_home.R index a4f65dc..e419915 100644 --- a/R/mod_home.R +++ b/R/mod_home.R @@ -22,6 +22,9 @@ mod_Home_ui <- function(id){ box( title = "Breedverse", status = "info", solidHeader = FALSE, width = 12, collapsible = FALSE, HTML( + "
    + Breedverse +
    ", paste0( "

    About Breedverse. Breedverse is a user-friendly, easy-to-install interface ", "that brings together a curated catalog of applications developed by ", @@ -53,7 +56,7 @@ mod_Home_ui <- function(id){

  • Software Tools
  • Analysis
  • - Breeding Insight is funded by the U.S. Department of Agriculture (USDA) Agricultural Research Service (ARS) through Cornell University. + Breeding Insight is funded by the U.S. Department of Agriculture (USDA) Agricultural Research Service (ARS) through University of Florida (UF) - Institute of Food and Agricultural Sciences (IFAS).
    Breeding Insight
    " diff --git a/R/run_app.R b/R/run_app.R index 0e204a4..3355218 100644 --- a/R/run_app.R +++ b/R/run_app.R @@ -10,8 +10,8 @@ #' @importFrom shiny shinyApp #' @importFrom golem with_golem_options #' -#' @export -#' +#' @export +#' run_app <- function( onStart = NULL, options = list(), diff --git a/inst/app/www/breedverse_logo.png b/inst/app/www/breedverse_logo.png new file mode 100644 index 0000000..6eca84a Binary files /dev/null and b/inst/app/www/breedverse_logo.png differ diff --git a/inst/app/www/custom.css b/inst/app/www/custom.css index ea177d5..5cb9856 100644 --- a/inst/app/www/custom.css +++ b/inst/app/www/custom.css @@ -1,3 +1,350 @@ +/* ── Custom Box Colors ── */ + +:root { + --azure-core: #48A9C5; + --azure-lite: #B5DDE8; + --azure-deep: #2A6576; + --green-core: #319B42; + --green-lite: #A3D9AC; + --green-deep: #1E5D28; + --yellow-core: #EFB526; + --yellow-lite: #F9E1A8; + --yellow-deep: #8F6D17; + --grey-core: #707372; + --grey-lite: #C8CACA; + --grey-deep: #434544; + --purple-core: #512C85; + --purple-lite: #A896C2; + --purple-deep: #311A50; + --red-core: #E43F4F; + --red-lite: #F2A7AE; + --red-deep: #8E1E2A; + --saddleBrown: #8B4513; + --rosybrown: #BC8F8F; + --coral: #FF7F50; + --lightcoral: #F08080; + --darkorange: #FF8C00; +} + +/* Override all bs4Dash status colors with custom colors */ + +/* Info -> Azure Core */ +.card-info:not(.card-outline) .card-header { + background-color: var(--azure-core) !important; + color: white !important; + border: none !important; + margin: -1px -1px 0 -1px !important; +} +.card-info:not(.card-outline) { + border-top: 3px solid var(--azure-core) !important; +} + +/* Secondary -> Grey Core */ +.card-secondary:not(.card-outline) .card-header { + background-color: var(--grey-core) !important; + color: white !important; + border: none !important; + margin: -1px -1px 0 -1px !important; +} +.card-secondary:not(.card-outline) { + border-top: 3px solid var(--grey-core) !important; +} + +/* Primary -> Azure Lite */ +.card-primary .card-header { + background-color: var(--azure-lite) !important; + color: #333 !important; + border: none !important; + margin: -1px -1px 0 -1px !important; +} +.card-primary:not(.card-outline) { + border-top: 3px solid var(--azure-lite) !important; +} + +/* Success -> Green Core */ +.card-success:not(.card-outline) .card-header { + background-color: var(--green-core) !important; + color: white !important; + border: none !important; + margin: -1px -1px 0 -1px !important; +} +.card-success:not(.card-outline) { + border-top: 3px solid var(--green-core) !important; +} + +/* Warning -> Yellow Core */ +.card-warning:not(.card-outline) .card-header { + background-color: var(--yellow-core) !important; + color: #333 !important; + border: none !important; + margin: -1px -1px 0 -1px !important; +} +.card-warning:not(.card-outline) { + border-top: 3px solid var(--yellow-core) !important; +} + +/* Danger -> Red Core */ +.card-danger:not(.card-outline) .card-header { + background-color: var(--red-core) !important; + color: white !important; + border: none !important; + margin: -1px -1px 0 -1px !important; +} +.card-danger:not(.card-outline) { + border-top: 3px solid var(--red-core) !important; +} + +/* Gray-dark -> Grey Deep */ +.card-gray-dark:not(.card-outline) .card-header { + background-color: var(--grey-deep) !important; + color: white !important; + border: none !important; + margin: -1px -1px 0 -1px !important; +} +.card-gray-dark:not(.card-outline) { + border-top: 3px solid var(--grey-deep) !important; +} + +/* Gray -> Grey Lite */ +.card-gray:not(.card-outline) .card-header { + background-color: var(--grey-lite) !important; + color: #333 !important; + border: none !important; + margin: -1px -1px 0 -1px !important; +} +.card-gray:not(.card-outline) { + border-top: 3px solid var(--grey-lite) !important; +} + +/* Indigo -> Purple Core */ +.card-indigo:not(.card-outline) .card-header { + background-color: var(--purple-core) !important; + color: white !important; + border: none !important; + margin: -1px -1px 0 -1px !important; +} +.card-indigo:not(.card-outline) { + border-top: 3px solid var(--purple-core) !important; +} + +/* Lightblue -> Azure Deep */ +.card-lightblue:not(.card-outline) .card-header { + background-color: var(--azure-deep) !important; + color: white !important; + border: none !important; + margin: -1px -1px 0 -1px !important; +} +.card-lightblue:not(.card-outline) { + border-top: 3px solid var(--azure-deep) !important; +} + +/* Navy -> Saddle Brown */ +.card-navy:not(.card-outline) .card-header { + background-color: var(--saddleBrown) !important; + color: white !important; + border: none !important; + margin: -1px -1px 0 -1px !important; +} +.card-navy:not(.card-outline) { + border-top: 3px solid var(--saddleBrown) !important; +} + +/* Purple -> Purple Lite */ +.card-purple:not(.card-outline) .card-header { + background-color: var(--purple-lite) !important; + color: #333 !important; + border: none !important; + margin: -1px -1px 0 -1px !important; +} +.card-purple:not(.card-outline) { + border-top: 3px solid var(--purple-lite) !important; +} + +/* Fuchsia -> Purple Deep */ +.card-fuchsia:not(.card-outline) .card-header { + background-color: var(--purple-deep) !important; + color: white !important; + border: none !important; + margin: -1px -1px 0 -1px !important; +} +.card-fuchsia:not(.card-outline) { + border-top: 3px solid var(--purple-deep) !important; +} + +/* Pink -> Red Lite */ +.card-pink:not(.card-outline) .card-header { + background-color: var(--red-lite) !important; + color: #333 !important; + border-top-left-radius: 0.25rem !important; + border-top-right-radius: 0.25rem !important; +} +.card-pink:not(.card-outline) { + border-top: 3px solid var(--red-lite) !important; +} + +/* Maroon -> Red Deep */ +.card-maroon:not(.card-outline) .card-header { + background-color: var(--red-deep) !important; + color: white !important; + border: none !important; + margin: -1px -1px 0 -1px !important; +} +.card-maroon:not(.card-outline) { + border-top: 3px solid var(--red-deep) !important; +} + +/* Orange -> Yellow Deep */ +.card-orange:not(.card-outline) .card-header { + background-color: var(--yellow-deep) !important; + color: white !important; + border: none !important; + margin: -1px -1px 0 -1px !important; +} +.card-orange:not(.card-outline) { + border-top: 3px solid var(--yellow-deep) !important; +} + +/* Lime -> Green Lite */ +.card-lime:not(.card-outline) .card-header { + background-color: var(--green-lite) !important; + color: #333 !important; + border: none !important; + margin: -1px -1px 0 -1px !important; +} +.card-lime:not(.card-outline) { + border-top: 3px solid var(--green-lite) !important; +} + +/* Teal -> Yellow Lite */ +.card-teal:not(.card-outline) .card-header { + background-color: var(--yellow-lite) !important; + color: #333 !important; + border: none !important; + margin: -1px -1px 0 -1px !important; +} +.card-teal:not(.card-outline) { + border-top: 3px solid var(--yellow-lite) !important; +} + +/* Olive -> Green Deep */ +.card-olive:not(.card-outline) .card-header { + background-color: var(--green-deep) !important; + color: white !important; + border: none !important; + margin: -1px -1px 0 -1px !important; +} +.card-olive:not(.card-outline) { + border-top: 3px solid var(--green-deep) !important; +} + + +/* ── ValueBox Gradient Color Overrides ── */ + +/* Info -> Azure Core */ +.small-box.bg-gradient-info { + background: linear-gradient(135deg, var(--azure-core), var(--azure-deep)) !important; + color: white !important; +} +.small-box.bg-gradient-info .icon { + color: rgba(255,255,255,0.3) !important; +} + +/* Secondary -> Grey Core */ +.small-box.bg-gradient-secondary { + background: linear-gradient(135deg, var(--grey-core), var(--grey-deep)) !important; + color: white !important; +} +.small-box.bg-gradient-secondary .icon { + color: rgba(255,255,255,0.3) !important; +} + +/* Info -> Azure Lite */ +.small-box.bg-gradient-primary { + background: linear-gradient(135deg, var(--azure-lite), var(--azure-core)) !important; + color: #333 !important; +} +.small-box.bg-gradient-primary .icon { + color: rgba(51,51,51,0.3) !important; +} + +/* Success -> Green Core */ +.small-box.bg-gradient-success { + background: linear-gradient(135deg, var(--green-core), var(--green-deep)) !important; + color: white !important; +} +.small-box.bg-gradient-success .icon { + color: rgba(255,255,255,0.3) !important; +} + +/* Warning -> Yellow Core */ +.small-box.bg-gradient-warning { + background: linear-gradient(135deg, var(--yellow-core), var(--yellow-deep)) !important; + color: #333 !important; +} +.small-box.bg-gradient-warning .icon { + color: rgba(51,51,51,0.3) !important; +} + +/* Danger -> Red Core */ +.small-box.bg-gradient-danger { + background: linear-gradient(135deg, var(--red-core), var(--red-deep)) !important; + color: white !important; +} +.small-box.bg-gradient-danger .icon { + color: rgba(255,255,255,0.3) !important; +} + +/* Navy -> Saddle Brown */ +.small-box.bg-gradient-navy { + background: linear-gradient(135deg, var(--saddleBrown), #5d2f0a) !important; + color: white !important; +} +.small-box.bg-gradient-navy .icon { + color: rgba(255,255,255,0.3) !important; +} + +/* Continue for all other colors... */ +.small-box.bg-gradient-indigo { + background: linear-gradient(135deg, var(--purple-core), var(--purple-deep)) !important; + color: white !important; +} +.small-box.bg-gradient-indigo .icon { + color: rgba(255,255,255,0.3) !important; +} + +/* ── Sidebar Active Menu Item ── */ +/* Colors are driven by --sidebar-* variables set in app_ui.R */ +:root { + --sidebar-core: var(--azure-core); + --sidebar-lite: var(--azure-lite); + --sidebar-deep: var(--azure-deep); +} + +.main-sidebar .nav-sidebar .nav-item.active .nav-link, +.main-sidebar .nav-sidebar .nav-item .nav-link.active { + background-color: var(--sidebar-core) !important; + color: white !important; +} + +/* Hover effect for menu items */ +.main-sidebar .nav-sidebar .nav-item .nav-link:hover { + background-color: var(--sidebar-lite) !important; + color: #333 !important; +} + +/* Active menu item icon */ +.main-sidebar .nav-sidebar .nav-item.active .nav-link .nav-icon, +.main-sidebar .nav-sidebar .nav-item .nav-link.active .nav-icon { + color: white !important; +} + +/* For expanded menu items */ +.main-sidebar .nav-sidebar .nav-treeview .nav-item.active .nav-link, +.main-sidebar .nav-sidebar .nav-treeview .nav-item .nav-link.active { + background-color: var(--sidebar-deep) !important; + color: white !important; +} + /* ── Select Samples tabsetPanel (By Sample / By Family) */ /* inactive tab - grey background */ @@ -49,3 +396,56 @@ .main-footer a { color: grey; } + +#MainMenu .header { + color: grey; + margin-top: 10px; + margin-bottom: 10px; + padding-left: 15px; +} + +/* ── Most specific selector for AdminLTE override ── */ +.nav.nav-pills.nav-sidebar.flex-column.sidebar-menu .header { + color: grey; + margin-top: 10px; + margin-bottom: 10px; + padding-left: 15px; +} + +/* ── Footer styling ── */ +.dashboard-footer-right { + display: flex; + align-items: center; +} + +.dashboard-footer-text { + display: flex; + flex-direction: column; + margin-right: 15px; + text-align: right; +} + +.dashboard-footer-logo { + margin-right: 15px; +} + +.dashboard-footer-left { + display: flex; + align-items: center; + height: 100%; +} + +/* ── Ensure box collapse/expand buttons are always on top ── */ +.card-tools { + position: relative; + z-index: 10; +} + +/* Make collapse/expand icons visible on white box headers */ +.card-tools .btn-tool { + color: #495057 !important; +} + +.card-tools .btn-tool:hover { + color: #212529 !important; +} \ No newline at end of file diff --git a/inst/app/www/custom.js b/inst/app/www/custom.js new file mode 100644 index 0000000..f638789 --- /dev/null +++ b/inst/app/www/custom.js @@ -0,0 +1,45 @@ +$(document).ready(function() { + // Use event delegation for dynamically created elements + $(document).on('click', '.card-header', function(e) { + // Don't trigger if clicking on the actual collapse button + if (!$(e.target).closest('.card-tools').length) { + // Find the collapse button in this header and trigger click + var collapseBtn = $(this).find('[data-card-widget="collapse"]'); + if (collapseBtn.length > 0) { + collapseBtn.trigger('click'); + } + } + }); + + // Function to apply styles to card headers (both existing and new ones) + function styleCardHeaders() { + $('.card-header').css('cursor', 'pointer'); + $('.card-tools').css('cursor', 'default'); + } + + // Apply styles initially + styleCardHeaders(); + + // Watch for new elements and apply styles + var observer = new MutationObserver(function(mutations) { + mutations.forEach(function(mutation) { + if (mutation.addedNodes.length > 0) { + styleCardHeaders(); + } + }); + }); + + // Start observing + observer.observe(document.body, { + childList: true, + subtree: true + }); + + // Your existing tab script + $('#cnv_1-sample_select_tabs li.active > a').addClass('active'); + + $(document).on('shown.bs.tab', '#cnv_1-sample_select_tabs a[data-toggle="tab"]', function(e) { + $('#cnv_1-sample_select_tabs a[data-toggle="tab"]').removeClass('active'); + $(e.target).addClass('active'); + }); +}); \ No newline at end of file