Nothing
# ui.R
## the UI side
#' some menu items of the dashboard sidebar
#' @description
#' to generate some menu items for the dashboard, which can be integrated to other
#' packages, such as 'fentouxungui/SeuratExplorerServer' from github.
#'
#' @import shiny markdown
#' @import shinyBS
#' @importFrom shinydashboard menuItem menuSubItem
#' @importFrom shinyjs useShinyjs
#' @export
#' @return return some menu items for the dashboard
#' @examples
#' explorer_sidebar_ui()
#'
explorer_sidebar_ui <- function(){
# to make shinyBS::updateCollapse() runs correctly, refer to: https://github.com/ebailey78/shinyBS/issues/92
shiny::addResourcePath("sbs", system.file("www", package="shinyBS"))
# Tooltip texts for each menu item (hover to show)
tooltips <- c(
dimplot = "Visualize cells in reduced dimensions (UMAP / t-SNE / PCA), colored by cluster or split by metadata.",
featureplot = "Overlay gene expression values on a dimensionality reduction plot.",
vlnplot = "Compare gene expression distributions across clusters.",
dotplot = "Show expression percentage and average level across clusters in a dot matrix.",
heatmap = "Gene expression heatmap at single-cell level.",
averagedheatmap = "Gene expression heatmap averaged per cluster.",
ridgeplot = "Gene expression density ridges across clusters.",
cellratioplot = "Cell-type proportion across samples or groups.",
degs = "Find marker genes per cluster or differential genes between two groups.",
topgenes = "Find highly expressed genes per cluster.",
featuresummary = "Summarize gene expression (mean / median / percentage) per cluster.",
featurecorrelation = "Calculate gene-gene expression correlations.",
renameclusters = "Interactively rename cluster labels.",
geneclusters = "Group cells by a gene's expression level with custom cutoffs.",
combinedclusters = "Combine two cluster annotations into a new grouping.",
addmodulescore = "Score cells with a gene set using AddModuleScore.",
featuresdf = "Search and browse feature / gene names across assays.",
cellmetadata = "Browse and download cell-level metadata.",
objectstructure = "Inspect the structure of the Seurat object."
)
conditionalPanel(
condition = "output.file_loaded",
sidebarMenu(menuItem(text = "Explorer", tabName = "explorer", icon = shiny::icon("dashboard"), startExpanded = TRUE,
menuSubItem(text = span(title = tooltips[["dimplot"]], "Dim Reduction Plot", tags$small(class = "label label-danger", style = "margin-left: 4px; font-size: 8px; padding: 1px 3px;", "HOT")), tabName = "dimplot", icon = shiny::icon("angle-right")),
menuSubItem(text = span(title = tooltips[["featureplot"]], "Feature Plot", tags$small(class = "label label-danger", style = "margin-left: 4px; font-size: 8px; padding: 1px 3px;", "HOT")), tabName = "featureplot", icon = shiny::icon("angle-right")),
menuSubItem(text = span(title = tooltips[["vlnplot"]], "Violin Plot", tags$small(class = "label label-danger", style = "margin-left: 4px; font-size: 8px; padding: 1px 3px;", "HOT")), tabName = "vlnplot", icon = shiny::icon("angle-right")),
menuSubItem(text = span(title = tooltips[["dotplot"]], "Dot Plot", tags$small(class = "label label-success", style = "margin-left: 4px; font-size: 8px; padding: 1px 3px;", "UPDATED")), tabName = "dotplot", icon = shiny::icon("angle-right")),
menuSubItem(text = span(title = tooltips[["heatmap"]], "Heatmap Cell Level"), tabName = "heatmap", icon = shiny::icon("angle-right")),
menuSubItem(text = span(title = tooltips[["averagedheatmap"]], "Heatmap Group Averaged"), tabName = "averagedheatmap", icon = shiny::icon("angle-right")),
menuSubItem(text = span(title = tooltips[["ridgeplot"]], "Ridge Plot"), tabName = "ridgeplot", icon = shiny::icon("angle-right")),
menuSubItem(text = span(title = tooltips[["cellratioplot"]], "Cell Percentage Plot"), tabName = "cellratioplot", icon = shiny::icon("angle-right")),
menuSubItem(text = span(title = tooltips[["degs"]], "DEGs Analysis", tags$small(class = "label label-danger", style = "margin-left: 4px; font-size: 8px; padding: 1px 3px;", "HOT")), tabName = "degs", icon = shiny::icon("angle-right")),
menuSubItem(text = span(title = tooltips[["topgenes"]], "Top Expressed Features"), tabName = "topgenes", icon = shiny::icon("angle-right")),
menuSubItem(text = span(title = tooltips[["featuresummary"]], "Feature Summary"), tabName = "featuresummary", icon = shiny::icon("angle-right")),
menuSubItem(text = span(title = tooltips[["featurecorrelation"]], "Feature Correlation"), tabName = "featurecorrelation", icon = shiny::icon("angle-right")),
menuSubItem(text = span(title = tooltips[["renameclusters"]], "Rename Clusters"), tabName = "renameclusters", icon = shiny::icon("angle-right")),
menuSubItem(text = span(title = tooltips[["geneclusters"]], "Gene Based Cluster", tags$small(class = "label label-info", style = "margin-left: 4px; font-size: 8px; padding: 1px 3px;", "BETA")), tabName = "geneclusters", icon = shiny::icon("angle-right")),
menuSubItem(text = span(title = tooltips[["combinedclusters"]], "Combination Based Cluster", tags$small(class = "label label-info", style = "margin-left: 4px; font-size: 8px; padding: 1px 3px;", "BETA")), tabName = "combinedclusters", icon = shiny::icon("angle-right")),
menuSubItem(text = span(title = tooltips[["addmodulescore"]], "Module Score", tags$small(class = "label label-info", style = "margin-left: 4px; font-size: 8px; padding: 1px 3px;", "BETA")), tabName = "addmodulescore", icon = shiny::icon("angle-right")),
menuSubItem(text = span(title = tooltips[["featuresdf"]], "Search Features"), tabName = "featuresdf", icon = shiny::icon("angle-right")),
menuSubItem(text = span(title = tooltips[["cellmetadata"]], "Cells Metadata"), tabName = "cellmetadata", icon = shiny::icon("angle-right")),
menuSubItem(text = span(title = tooltips[["objectstructure"]], "Object Structure"), tabName = "objectstructure", icon = shiny::icon("angle-right"))
# menuSubItem(text = "About", tabName = "about", icon = shiny::icon("angle-right"))
)
)
)
}
#' generate the body UI for each menu item specified in `explorer_sidebar_ui`
#' @param tab_list a tag list for the body UI of shiny dashboard
#' @import shinydashboard
#' @import shiny shinyWidgets shinyBS knitr
#' @importFrom shinydashboard tabItem
#' @importFrom shinycssloaders withSpinner
#' @importFrom shinyBS bsCollapse bsCollapsePanel
#' @export
#' @return a filled tag list for body UI
#' @examples
#' tab_list <- list()
#' tab_list <- explorer_body_ui(tab_list = tab_list)
#'
explorer_body_ui <- function(tab_list){
tab_list[["dimplot"]] = tabItem(tabName = "dimplot",
fluidRow(
box(title = "Dimensional Reduction Plot",
uiOutput("dimplot_resizable_ui"),
# show the button on right end, refer to: https://stackoverflow.com/questions/28749693/shiny-r-aligning-buttons
div(style = "display:inline-block; float:right",downloadBttn(outputId = "downloaddimplot",style = "bordered",color = "primary")),
width = 9, status = "primary", collapsible = TRUE, solidHeader = TRUE),
box(title = div(
icon("sliders-h"),
strong(" Plot Settings"),
style = "display: flex; align-items: center; gap: 8px; color: white;"
),
solidHeader = TRUE, status = "primary", width = 3,
# Data Selection Group
param_group_collapse(
id = "dimplot_data", title = "Data Selection",
icon_name = "database", color = "#3b82f6",
withSpinner(uiOutput("DimDimensionReduction.UI"), proxy.height = "10px"),
withSpinner(uiOutput("DimClusterResolution.UI"), proxy.height = "10px")
),
# Display Options Group
param_group_collapse(
id = "dimplot_display", title = "Display Options",
icon_name = "eye", color = "#10b981",
checkboxInput("DimShowLabel",label = "Show cluster label", TRUE),
checkboxInput("DimShowLegend",label = "Show legend", TRUE)
),
# Size Settings Group
param_group_collapse(
id = "dimplot_size", title = "Size Settings",
icon_name = "adjust", color = "#f59e0b",
sliderInput("DimLabelSize", label = "Label Size:", min = 0, max = 10, value = 7),
sliderInput("DimPointSize", label = "Point Size:", min = 0.001, max = 2, value = 0.8)
),
# Advanced Options
param_group_collapse(
id = "dimplot_advanced", title = "Advanced Options",
icon_name = "cogs", color = "#6b7280",
withSpinner(uiOutput("DimSplit.UI"), proxy.height = "10px"),
withSpinner(uiOutput("DimHighlightedClusters.UI"), proxy.height = "10px"),
bsCollapse(id = "collapseDimplot", open = "Change Cluster Order",
bsCollapsePanel(title = "Change Cluster Order",
withSpinner(uiOutput("DimClusterOrder.UI"), proxy.height = "10px"),
style = "info")),
checkboxInput("DimPlotMode",label = "Automatically adjust plotting area", TRUE),
uiOutput("dimplot_size_ui")
)
)
)
)
tab_list[["featureplot"]] = tabItem(tabName = "featureplot",
fluidRow(
box(title = "Features on Dimensional Reduction Plot",
uiOutput("featureplot_resizable_ui"),
div(style = "display:inline-block; float:right", downloadBttn(outputId = "downloadfeatureplot",style = "bordered",color = "primary")),
width = 9, status = "primary", collapsible = TRUE, solidHeader = TRUE),
box(title = div(
icon("sliders-h"),
strong(" Plot Settings"),
style = "display: flex; align-items: center; gap: 8px; color: white;"
),
solidHeader = TRUE, status = "primary", width = 3,
# Gene Selection Group
param_group_collapse(
id = "featureplot_gene", title = "Gene Selection",
icon_name = "dna", color = "#3b82f6",
textAreaInput("FeatureGeneSymbol", "Gene Symbol:", value = "", height = '80px', resize = "vertical"),
div(
style = "background-color: #e9ecef; border: 1px solid #3b82f6; padding: 5px; border-radius: 4px; margin-top: 5px; margin-bottom: 10px;",
uiOutput("Featurehints.UI")
)
),
# Data Selection Group
param_group_collapse(
id = "featureplot_data", title = "Data Selection",
icon_name = "database", color = "#10b981",
withSpinner(uiOutput("FeatureDimensionReduction.UI"), proxy.height = "10px"),
withSpinner(uiOutput("FeatureAssays.UI"), proxy.height = "10px"),
withSpinner(uiOutput("FeatureAssaySlots.UI"), proxy.height = "10px"),
withSpinner(uiOutput("FeatureClusterResolution.UI"), proxy.height = "10px")
),
# Display Options Group
param_group_collapse(
id = "featureplot_display", title = "Display Options",
icon_name = "eye", color = "#f59e0b",
checkboxInput("FeatureShowLabel",label = "Show cluster label", FALSE),
withSpinner(uiOutput("FeatureSplit.UI"), proxy.height = "10px"),
sliderInput("FeatureLabelSize", label = "Label Size:", min = 1, max = 12, value = 4)
),
# Color Settings Group
param_group_collapse(
id = "featureplot_color", title = "Color Settings",
icon_name = "palette", color = "#8b5cf6",
colourpicker::colourInput("FeaturePlotLowestExprColor", "Lowest expression color:", "#E5E5E5", palette = "limited"),
colourpicker::colourInput("FeaturePlotHighestExprColor", "Highest expression color:", "#FF0000", palette = "limited")
),
# Advanced Settings Group
param_group_collapse(
id = "featureplot_advanced", title = "Advanced Settings",
icon_name = "cogs", color = "#6b7280",
sliderInput("FeaturePointAlpha", label = "Point Alpha:", min = 0.1, max = 1, value = 1),
sliderInput("FeatureMinCutoff", label = "Min expression cutoff (%):", min = 0, max = 100, value = 0),
sliderInput("FeatureMaxCutoff", label = "Max expression cutoff (%):", min = 0, max = 100, value = 100),
sliderInput("FeaturePointSize", label = "Point Size:", min = 0.001, max = 5, value = 0.8),
checkboxInput("FeaturePlotMode",label = "Auto-adjust plotting area", TRUE),
uiOutput("featureplot_size_ui")
)
)
)
)
tab_list[["vlnplot"]] = tabItem(tabName = "vlnplot",
fluidRow(
box(title = "Features Violin Plot",
uiOutput("vlnplot_resizable_ui"),
div(style = "display:inline-block; float:right", downloadBttn(outputId = "downloadvlnplot",style = "bordered",color = "primary")),
width = 9, status = "primary", collapsible = TRUE, solidHeader = TRUE),
box(title = div(
icon("sliders-h"),
strong(" Plot Settings"),
style = "display: flex; align-items: center; gap: 8px; color: white;"
),
solidHeader = TRUE, status = "primary", width = 3,
# Data Selection
param_group_collapse(
id = "vlnplot_data", title = "Data Selection",
icon_name = "database", color = "#3b82f6",
textAreaInput("VlnGeneSymbol", "Gene Symbols:", value = "", height = '80px', resize = "vertical"),
div(
style = "background-color: #e9ecef; border: 1px solid #3b82f6; padding: 5px; border-radius: 4px; margin-top: 5px; margin-bottom: 10px;",
uiOutput("Vlnhints.UI")
),
withSpinner(uiOutput("VlnAssays.UI"), proxy.height = "10px"),
withSpinner(uiOutput("VlnAssaySlots.UI"), proxy.height = "10px"),
withSpinner(uiOutput("VlnSplitBy.UI"), proxy.height = "10px")
),
# Cluster Settings
param_group_collapse(
id = "vlnplot_cluster", title = "Cluster Settings",
icon_name = "layer-group", color = "#10b981",
withSpinner(uiOutput("VlnClusterResolution.UI"), proxy.height = "10px"),
withSpinner(uiOutput("VlnIdentsSelected.UI"), proxy.height = "10px"),
bsCollapse(id = "collapseVlnplot", open = "0",
bsCollapsePanel(title = "Change Cluster Order",
withSpinner(uiOutput("VlnClusterOrder.UI"), proxy.height = "10px"),
style = "info", value = "0"))
),
# Plot Options
param_group_collapse(
id = "vlnplot_plot", title = "Plot Options",
icon_name = "sliders-h", color = "#f59e0b",
conditionalPanel(
condition = "output.Vlnplot_splitoption_twolevels",
checkboxInput("VlnSplitPlot",label = "Split Plot", FALSE)
),
conditionalPanel(
condition = "output.Vlnplot_multiple_genes",
checkboxInput("VlnStackPlot",label = "Stack Plot", FALSE)
),
conditionalPanel(
condition = "output.Vlnplot_StackPlot",
checkboxInput("VlnFlipPlot",label = "Flip Plot", FALSE)
),
conditionalPanel(
condition = "output.Vlnplot_StackPlot && input.VlnSplitBy == 'None'",
selectInput("VlnFillBy","Color By:", choices = c(Feature = "feature", Cluster = "ident")),
),
conditionalPanel(
condition = "input.VlnSplitBy == 'None'",
selectInput("Vlnfillcolorplatte","select color plate:", choices = color_choice_vector, selected = "Default")
),
sliderInput("VlnPointSize", label = "Point Size:", min = 0, max = 4, value = 0),
sliderInput("VlnPointAlpha", label = "Point Alpha:", min = 0, max = 1, value = 1),
sliderInput("VlnXlabelSize", label = "X Axis Label Size:", min = 0, max = 20, value = 14),
sliderInput("VlnYlabelSize", label = "Y Axis Label Size:", min = 0, max = 20, value = 10),
checkboxInput("VlnPlotMode",label = "Automatically adjust plotting area", TRUE),
uiOutput("vlnplot_size_ui")
)
)
)
)
tab_list[["dotplot"]] = tabItem(tabName = "dotplot",
fluidRow(
box(title = "Features Dot Plot",
uiOutput("dotplot_resizable_ui"),
div(style = "display:inline-block; float:right", downloadBttn(outputId = "downloaddotplot",style = "bordered",color = "primary")),
width = 9, status = "primary", collapsible = TRUE, solidHeader = TRUE),
box(title = div(
icon("sliders-h"),
strong(" Plot Settings"),
style = "display: flex; align-items: center; gap: 8px; color: white;"
),
solidHeader = TRUE, status = "primary", width = 3,
# Data Selection
param_group_collapse(
id = "dotplot_data", title = "Data Selection",
icon_name = "database", color = "#3b82f6",
textAreaInput("DotGeneSymbol", "Gene Symbols:", value = "", height = '80px', resize = "vertical"),
div(
style = "background-color: #e9ecef; border: 1px solid #3b82f6; padding: 5px; border-radius: 4px; margin-top: 5px; margin-bottom: 10px;",
uiOutput("Dothints.UI")
),
withSpinner(uiOutput("DotAssays.UI"), proxy.height = "10px"),
withSpinner(uiOutput("DotSplitBy.UI"), proxy.height = "10px")
),
# Cluster Settings
param_group_collapse(
id = "dotplot_cluster", title = "Cluster Settings",
icon_name = "layer-group", color = "#10b981",
withSpinner(uiOutput("DotClusterResolution.UI"), proxy.height = "10px"),
withSpinner(uiOutput("DotIdentsSelected.UI"), proxy.height = "10px"),
bsCollapse(id = "collapseDotplot", open = "0",
bsCollapsePanel(title = "Change Cluster Order",
withSpinner(uiOutput("DotClusterOrder.UI"), proxy.height = "10px"),
style = "info", value = "0")),
checkboxInput("DotClusterIdents",label = "Cluster the Clusters", FALSE)
),
# Filter Cells
param_group_collapse(
id = "dotplot_filter", title = "Filter Cells",
icon_name = "filter", color = "#8b5cf6",
withSpinner(uiOutput("IntraClusterDotplotSubsetCells.UI"), proxy.height = "10px"),
withSpinner(uiOutput("IntraClusterDotplotSubsetCellsSelectedClusters.UI"), proxy.height = "10px")
),
# Plot Options
param_group_collapse(
id = "dotplot_plot", title = "Plot Options",
icon_name = "sliders-h", color = "#f59e0b",
checkboxInput("DotRotateAxis",label = "Rotate x Axis Labels", FALSE),
checkboxInput("DotFlipCoordinate",label = "Flip XY Coordinate", FALSE),
conditionalPanel(
condition = "output.DotPlot_Split_isNone",
colourpicker::colourInput("DotPlotLowestExprColor", "Pick Color for lowest expression:", "#E5E5E5", palette = "limited"),
colourpicker::colourInput("DotPlotHighestExprColor", "Pick Color for highest expression:", "#0000FF",palette = "limited"),
),
sliderInput("DotDotScale", label = "Dot Scale:", min = 1, max = 12, value = 6),
sliderInput("DotXlabelSize", label = "X Axis Label Size:", min = 0, max = 20, value = 14),
sliderInput("DotYlabelSize", label = "Y Axis Label Size:", min = 0, max = 20, value = 10),
checkboxInput("DotPlotMode",label = "Automatically adjust plotting area", TRUE),
uiOutput("dotplot_size_ui")
)
)
)
)
tab_list[["heatmap"]] = tabItem(tabName = "heatmap",
fluidRow(
box(title = "Features Heatmap Plot",
uiOutput("heatmap_resizable_ui"),
div(style = "display:inline-block; float:right", downloadBttn(outputId = "downloadheatmap",style = "bordered",color = "primary")),
width = 9, status = "primary", collapsible = TRUE, solidHeader = TRUE),
box(title = div(
icon("sliders-h"),
strong(" Plot Settings"),
style = "display: flex; align-items: center; gap: 8px; color: white;"
),
solidHeader = TRUE, status = "primary", width = 3,
# Data Selection
param_group_collapse(
id = "heatmap_data", title = "Data Selection",
icon_name = "database", color = "#3b82f6",
textAreaInput("HeatmapGeneSymbol", "Gene Symbols:", value = "", height = '80px', resize = "vertical"),
div(
style = "background-color: #e9ecef; border: 1px solid #3b82f6; padding: 5px; border-radius: 4px; margin-top: 5px; margin-bottom: 10px;",
uiOutput("Heatmaphints.UI")
),
withSpinner(uiOutput("HeatmapAssays.UI"), proxy.height = "10px"),
withSpinner(uiOutput("HeatmapAssaySlots.UI"), proxy.height = "10px")
),
# Cluster Settings
param_group_collapse(
id = "heatmap_cluster", title = "Cluster Settings",
icon_name = "layer-group", color = "#10b981",
withSpinner(uiOutput("HeatmapClusterResolution.UI"), proxy.height = "10px"),
withSpinner(uiOutput("HeatmapIdentsSelected.UI"), proxy.height = "10px"),
bsCollapse(id = "collapseHeatmap", open = "0",
bsCollapsePanel(title = "Change Cluster Order",
withSpinner(uiOutput("HeatmapClusterOrder.UI"), proxy.height = "10px"),
style = "info", value = "0"))
),
# Plot Options
param_group_collapse(
id = "heatmap_plot", title = "Plot Options",
icon_name = "sliders-h", color = "#f59e0b",
sliderInput("HeatmapTextSize", label = "Cluster Text Size:", min = 1, max = 12, value = 6, step = 0.5),
sliderInput("HeatmapTextHjust", label = "Cluster Text Hjust:", min = -10, max = 20, value = 0, step = 0.5),
sliderInput("HeatmapTextVjust", label = "Cluster Text Vjust:", min = -5, max = 5, value = 0, step = 0.1),
sliderInput("HeatmapTextRatateAngle", label = "Cluster Text Rotate Angle:", min = -90, max = 90, value = 0, step = 1),
sliderInput("HeatmapGroupBarHeight", label = "Cluster Group Bar Height:", min = 0, max = 0.1, value = 0.04, step = 0.01),
sliderInput("HeatmapLineWidth", label = "Line Width:", min = 1, max = 10, value = 1),
sliderInput("HeatmapFeatureTextSize", label = "Feature Text Size:", min = 0, max = 20, value = 10),
checkboxInput("HeatmapPlotMode",label = "Automatically adjust plotting area", TRUE),
uiOutput("heatmap_size_ui")
)
)
)
)
tab_list[["averagedheatmap"]] = tabItem(tabName = "averagedheatmap",
fluidRow(
box(title = "Features Heatmap by Averaged Expression",
uiOutput("averagedheatmap_resizable_ui"),
div(style = "display:inline-block; float:right", downloadBttn(outputId = "downloadaveragedheatmap",style = "bordered",color = "primary")),
width = 9, status = "primary", collapsible = TRUE, solidHeader = TRUE),
box(title = div(
icon("sliders-h"),
strong(" Plot Settings"),
style = "display: flex; align-items: center; gap: 8px; color: white;"
),
solidHeader = TRUE, status = "primary", width = 3,
# Data Selection
param_group_collapse(
id = "avgheatmap_data", title = "Data Selection",
icon_name = "database", color = "#3b82f6",
textAreaInput("AveragedHeatmapGeneSymbol", "Gene Symbols:", value = "", height = '80px', resize = "vertical"),
div(
style = "background-color: #e9ecef; border: 1px solid #3b82f6; padding: 5px; border-radius: 4px; margin-top: 5px; margin-bottom: 10px;",
uiOutput("AveragedHeatmaphints.UI")
),
withSpinner(uiOutput("AveragedHeatmapAssays.UI"), proxy.height = "10px")
),
# Cluster Settings
param_group_collapse(
id = "avgheatmap_cluster", title = "Cluster Settings",
icon_name = "layer-group", color = "#10b981",
withSpinner(uiOutput("AveragedHeatmapClusterResolution.UI"), proxy.height = "10px"),
withSpinner(uiOutput("AveragedHeatmapIdentsSelected.UI"), proxy.height = "10px"),
bsCollapse(id = "collapseHeatmap", open = "0",
bsCollapsePanel(title = "Change Cluster Order",
withSpinner(uiOutput("AveragedHeatmapClusterOrder.UI"), proxy.height = "10px"),
style = "info", value = "0")),
checkboxInput("AveragedHeatmapClusterClusters",label = "Cluster Clusters", FALSE),
checkboxInput("AveragedHeatmapClusterFeatures",label = "Cluster Features", FALSE)
),
# Plot Options
param_group_collapse(
id = "avgheatmap_plot", title = "Plot Options",
icon_name = "sliders-h", color = "#f59e0b",
sliderInput("AveragedHeatmapClusterTextSize", label = "Cluster Text Size:", min = 1, max = 30, value = 12),
sliderInput("AveragedHeatmapClusterTextRatateAngle", label = "Cluster Text Rotate Angle:", min = -90, max = 90, value = 45),
sliderInput("AveragedHeatmapFeatureTextSize", label = "Feature Text Size:", min = 1, max = 20, value = 10),
checkboxInput("AveragedHeatmapPlotMode",label = "Automatically adjust plotting area", TRUE),
uiOutput("averagedheatmap_size_ui")
)
)
)
)
tab_list[["ridgeplot"]] = tabItem(tabName = "ridgeplot",
fluidRow(
box(title = "Features Ridge Plot",
uiOutput("ridgeplot_resizable_ui"),
div(style = "display:inline-block; float:right", downloadBttn(outputId = "downloadridgeplot",style = "bordered",color = "primary")),
width = 9, status = "primary", collapsible = TRUE, solidHeader = TRUE),
box(title = "Settings", solidHeader = TRUE, status = "primary", width = 3,
# Data Selection
param_group_collapse(
id = "ridgeplot_data", title = "Data Selection",
icon_name = "database", color = "#3b82f6",
textAreaInput("RidgeplotGeneSymbol", "Gene Symbols:", value = "", height = '80px', resize = "vertical"),
div(
style = "background-color: #e9ecef; border: 1px solid #3b82f6; padding: 5px; border-radius: 4px; margin-top: 5px; margin-bottom: 10px;",
uiOutput("Ridgeplothints.UI")
),
withSpinner(uiOutput("RidgeplotAssays.UI"), proxy.height = "10px"),
withSpinner(uiOutput("RidgeplotAssaySlots.UI"), proxy.height = "10px")
),
# Cluster Settings
param_group_collapse(
id = "ridgeplot_cluster", title = "Cluster Settings",
icon_name = "layer-group", color = "#10b981",
withSpinner(uiOutput("RidgeplotClusterResolution.UI"), proxy.height = "10px"),
withSpinner(uiOutput("RidgeplotIdentsSelected.UI"), proxy.height = "10px"),
bsCollapse(id = "collapseRidgeplot", open = "0",
bsCollapsePanel(title = "Change Cluster Order",
withSpinner(uiOutput("RidgeplotClusterOrder.UI"), proxy.height = "10px"),
style = "info", value = "0"))
),
# Plot Options
param_group_collapse(
id = "ridgeplot_plot", title = "Plot Options",
icon_name = "sliders-h", color = "#f59e0b",
conditionalPanel(
condition = "output.Ridgeplot_stack_NotSelected",
sliderInput("RidgeplotNumberOfColumns", label = "Number of columns:", min = 1, max = 10, value = 1),
),
conditionalPanel(
condition = "output.Ridgeplot_stack_show",
checkboxInput("RidgeplotStackPlot",label = "Stack Plot", FALSE),
),
conditionalPanel(
condition = "input.RidgeplotStackPlot",
selectInput("RidgeplotFillBy","Color By:", choices = c(Feature = "feature", Cluster = "ident"))
),
sliderInput("RidgeplotXlabelSize", label = "X Axis Label Size:", min = 0, max = 20, value = 14),
sliderInput("RidgeplotYlabelSize", label = "Y Axis Label Size:", min = 0, max = 20, value = 10),
checkboxInput("RidgeplotPlotMode",label = "Automatically adjust plotting area", TRUE),
uiOutput("ridgeplot_size_ui")
)
)
)
)
tab_list[["cellratioplot"]] = tabItem(tabName = "cellratioplot",
fluidRow(
box(title = "Cell Percentage Plot",
uiOutput("cellratioplot_resizable_ui"),
div(style = "display:inline-block; float:right", downloadBttn(outputId = "downloadcellratioplot",style = "bordered",color = "primary")),
div(style = "margin-top: 50px;",
hr(),
DT::dataTableOutput('cellratiodata'),
align="center"),
width = 9, status = "primary", collapsible = TRUE, solidHeader = TRUE),
box(title = "Settings", solidHeader = TRUE, status = "primary", width = 3,
# Data Selection (Fill, X, Facet)
param_group_collapse(
id = "cellratio_data", title = "Data Selection",
icon_name = "database", color = "#3b82f6",
withSpinner(uiOutput("CellratioFillChoice.UI"), proxy.height = "10px"),
withSpinner(uiOutput("CellratioXChoice.UI"), proxy.height = "10px"),
withSpinner(uiOutput("CellratioFacetChoice.UI"), proxy.height = "10px")
),
# Cluster Settings
param_group_collapse(
id = "cellratio_cluster", title = "Cluster Settings",
icon_name = "layer-group", color = "#10b981",
withSpinner(uiOutput("CellratioIdentsSelected.UI"), proxy.height = "10px"),
bsCollapse(id = "collapseCellratioFillplot", open = "0",
bsCollapsePanel(title = "Change Order (Fill)",
withSpinner(uiOutput("CellratioplotFillOrder.UI"), proxy.height = "10px"),
style = "info", value = "0")),
bsCollapse(id = "collapseCellratioXplot", open = "0",
bsCollapsePanel(title = "Change Order (X)",
withSpinner(uiOutput("CellratioplotXOrder.UI"), proxy.height = "10px"),
style = "info", value = "0")),
bsCollapse(id = "collapseCellratioFacetplot", open = "0",
bsCollapsePanel(title = "Change Order (Facet)",
withSpinner(uiOutput("CellratioplotFacetOrder.UI"), proxy.height = "10px"),
style = "info", value = "0"))
),
# Plot Options
param_group_collapse(
id = "cellratio_plot", title = "Plot Options",
icon_name = "sliders-h", color = "#f59e0b",
selectInput("Cellratiofillcolorplatte","select color plate:", choices = color_choice_vector, selected = "Default"),
checkboxInput("CellratioRotateAxis",label = "Rotate X Axis", FALSE),
sliderInput("CellratioColumnWidth", label = "Column width:", min = 0, max = 1, value = 0.7),
sliderInput("CellratioFlowAlpha", label = "Flow alpha:", min = 0, max = 1, value = 0.3),
sliderInput("CellratioFlowCurve", label = "Flow curve:", min = 0, max = 1, value = 0.3),
checkboxInput("CellratioMode",label = "Automatically adjust plotting area", TRUE),
uiOutput("cellratioplot_size_ui")
)
)
)
)
tab_list[["degs"]] = tabItem(tabName = "degs",
# CSS styles for close button hover effect
tags$style(type="text/css", "#close_degs_info:hover { color: #059669 !important; }"),
# CSS styles for info text
tags$style(type="text/css", "#degs_info {white-space: pre-wrap; color: #495057; line-height: 1.8;}"),
# Modernized tab styling - ONLY for DEGs page
tags$style("
/* Make sure the two main containers of the DEGs page are perfectly aligned */
#degstabbox-container {
padding-left: 0 !important;
padding-right: 0 !important;
margin-left: 0 !important;
margin-right: 0 !important;
}
#degstabbox-container > .nav-tabs-custom {
margin-left: -15px !important;
margin-right: -15px !important;
width: calc(100% + 30px) !important;
box-sizing: border-box !important;
}
#degstabbox-container .nav-tabs-custom .nav-tabs {
background: linear-gradient(to bottom, #f8f9fa 0%, #ffffff 100%);
border-bottom: 2px solid #10b981;
border-radius: 8px 8px 0 0;
padding: 10px 15px 0 15px;
margin: 0 !important;
}
#degstabbox-container .nav-tabs-custom .nav-tabs li.active:hover a,
#degstabbox-container .nav-tabs-custom .nav-tabs li.active a {
background-color: white;
border-color: #10b981 #10b981 white #10b981;
color: #10b981;
font-weight: 600;
}
#degstabbox-container .nav-tabs-custom .nav-tabs li.active {
border-top-color: #10b981;
border-top-width: 3px;
}
#degstabbox-container .nav-tabs-custom .nav-tabs li a {
color: #6c757d;
border-radius: 6px 6px 0 0;
transition: all 0.2s ease;
margin-right: 5px;
}
#degstabbox-container .nav-tabs-custom .nav-tabs li a:hover {
background-color: #f8f9fa;
color: #10b981;
}
#degstabbox-container .tab-content {
background: white;
border: 2px solid #10b981;
border-top: none;
border-radius: 0 0 8px 8px;
padding: 25px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
margin-left: 0 !important;
margin-right: 0 !important;
}
"),
# Single fluidRow containing both Information Box and tabBox
fluidRow(id = "degs-main-row",
# Information Box - White background + green border (top, full width)
div(
id = "degs-info-box",
class = "col-xs-12",
style = "margin-bottom: 10px;",
div(
class = "box",
style = "background: white; border: 2px solid #10b981; border-radius: 8px; box-shadow: 0 2px 6px rgba(0,0,0,0.08); position: relative;",
div(
class = "box-header",
style = "padding: 15px 20px; border-bottom: 2px solid #10b981; position: relative;",
div(
style = "display: flex; align-items: center; gap: 10px;",
icon("info-circle", style = "color: #10b981; font-size: 20px;"),
h4(style = "margin: 0; color: #10b981; font-weight: 600;", "Information")
),
# Close button in top-right corner
actionButton(
"close_degs_info",
label = NULL,
icon = icon("times"),
style = "position: absolute; right: 15px; top: 50%; transform: translateY(-50%); background: transparent; border: none; color: #10b981; font-size: 24px; cursor: pointer; padding: 0; line-height: 1; transition: color 0.2s ease;",
class = "btn btn-link"
)
),
div(
class = "box-body",
style = "padding: 15px 20px;",
textOutput("degs_info")
)
)
),
# tabBox (bottom, full width)
div(
class = "col-xs-12",
style = "margin-bottom: 10px;",
div(id = "degstabbox-container",
tabBox(
title = div(
style = "display: flex; align-items: center; gap: 10px;",
icon("chart-line", style = "color: #10b981; font-size: 18px;"),
strong("Find Markers or DEGs", style = "color: #495057; font-size: 16px;")
),
id = "tabset_degs", width = 12,
tabPanel("Find Markers for All Clusters",
div(
style = "background: #f0fdf4; border: 1px solid #10b981; border-left: 4px solid #10b981; padding: 20px; border-radius: 8px; margin-bottom: 25px;",
withSpinner(uiOutput("ClusterMarkersClusterResolution.UI"), proxy.height = "10px")
),
div(style = "text-align: center; margin-top: 25px;",
actionButton("DEGsClusterMarkersAnalysis",
icon = shiny::icon("magnifying-glass-chart"),
label = "Analyze",
class = "btn-primary btn-lg",
style = "padding: 12px 35px; border-radius: 8px; font-weight: 600; background: linear-gradient(135deg, #10b981 0%, #059669 100%); border: none; box-shadow: 0 4px 12px rgba(16, 185, 129, 0.3);")
)),
tabPanel("Find DEGs for two groups",
# Step 1 - Filter Cells
div(
style = "background: #eff6ff; border: 1px solid #3b82f6; border-left: 4px solid #3b82f6; padding: 25px; border-radius: 8px; margin-bottom: 25px; box-shadow: 0 2px 6px rgba(59, 130, 246, 0.1);",
h4(icon("filter"), "Step 1: Filter Cells (Optional)", style = "color: #3b82f6; margin-bottom: 12px; font-weight: 600; display: flex; align-items: center; gap: 8px;"),
p("Click the + button to add multiple filter conditions. Cells matching ALL conditions (intersection) will be kept.", style = "color: #6c757d; margin-bottom: 8px;"),
p("If no filters are modified, all cells will be kept by default.", style = "color: #6c757d; margin-bottom: 15px;"),
withSpinner(uiOutput("IntraClusterDEGsFilterRows.UI"), proxy.height = "10px"),
uiOutput("IntraClusterDEGsAddFilter.UI")
),
# Step 2 - Set Comparison
div(
style = "background: #fdf2f8; border: 1px solid #ec4899; border-left: 4px solid #ec4899; padding: 25px; border-radius: 8px; margin-bottom: 25px; box-shadow: 0 2px 6px rgba(236, 72, 153, 0.1);",
h4(icon("balance-scale"), "Step 2: Set the comparison", style = "color: #ec4899; margin-bottom: 15px; font-weight: 600; display: flex; align-items: center; gap: 8px;"),
withSpinner(uiOutput("IntraClusterDEGsCustomizedGroups.UI"), proxy.height = "10px"),
withSpinner(uiOutput("IntraClusterDEGsCustomizedGroupsCase.UI"), proxy.height = "10px"),
withSpinner(uiOutput("IntraClusterDEGsCustomizedGroupsControl.UI"), proxy.height = "10px")
),
div(style = "text-align: center; margin-top: 30px;",
actionButton("IntraClusterDEGssAnalysis",
icon = shiny::icon("magnifying-glass-chart"),
label = "Analyze",
class = "btn-primary btn-lg",
style = "padding: 12px 35px; border-radius: 8px; font-weight: 600; background: linear-gradient(135deg, #ec4899 0%, #be185d 100%); border: none; box-shadow: 0 4px 12px rgba(236, 72, 153, 0.3);")
)),
tabPanel("Custom Parameters",
div(
style = "background: #fef3c7; border: 1px solid #f59e0b; border-left: 4px solid #f59e0b; padding: 25px; border-radius: 8px; margin-bottom: 20px; box-shadow: 0 2px 6px rgba(245, 158, 11, 0.1);",
h4(icon("sliders-h"), "Analysis Parameters", style = "color: #f59e0b; margin-bottom: 20px; font-weight: 600; display: flex; align-items: center; gap: 8px;"),
withSpinner(uiOutput("DEGsAssays.UI"), proxy.height = "10px"),
div(style = "margin-top: 20px;",
sliderInput("logfcthreshold", label = "Logfc Threshold:", min = 0, max = 1, value = 0.1),
selectInput("testuse","Test use:", choices = c(wilcox = "wilcox", wilcox_limma = "wilcox_limma",
T_test = "t", negbinom = "negbinom", poisson = "poisson",
LR = "LR", MAST = "MAST", DESeq2 = "DESeq2")),
sliderInput("minpct", label = "Minimum Expression Percentage:", min = 0, max = 1, value = 0.01),
sliderInput("mindiffpct", label = "Minimum Expression Percentage Difference:", min = 0, max = 1, value = 0)
)
),
div(style = "text-align: center;",
actionButton("SetDefault",
icon = shiny::icon("save"),
label = "Set to Default",
class = "btn-warning",
style = "padding: 10px 30px; border-radius: 8px; font-weight: 600;")
))
) # end of tabBox
) # end of degstabbox-container div
), # end of col-xs-12 div (tabBox)
conditionalPanel(
condition = "output.DEGs_ready",
div(style = "margin-top: 15px; margin-left: 15px; margin-right: 15px;",
fluidRow(
# Analysis Results - Custom style (left column, accounting for 8/12)
div(
class = "col-md-8",
div(
class = "box",
style = "background: white; border: 2px solid #3b82f6; border-radius: 8px; box-shadow: 0 2px 6px rgba(0,0,0,0.08);",
div(
class = "box-header",
style = "padding: 15px 20px; border-bottom: 2px solid #3b82f6;",
div(
style = "display: flex; align-items: center; gap: 10px;",
icon("table", style = "color: #3b82f6; font-size: 18px;"),
h4(style = "margin: 0; color: #3b82f6; font-weight: 600;", "Analysis Results")
)
),
div(
class = "box-body",
style = "padding: 20px;",
withSpinner(DT::dataTableOutput('dataset_degs'))
)
)
),
# External Links - Custom styles (right column, accounting for 4/12)
div(
class = "col-md-4",
conditionalPanel(
condition = "output.DEGs_row_selected",
div(
class = "box",
style = "background: white; border: 2px solid #10b981; border-radius: 8px; box-shadow: 0 2px 6px rgba(0,0,0,0.08);",
div(
class = "box-header",
style = "padding: 15px 20px; border-bottom: 2px solid #10b981;",
div(
style = "display: flex; align-items: center; gap: 10px;",
icon("external-link-alt", style = "color: #10b981; font-size: 18px;"),
h4(style = "margin: 0; color: #10b981; font-weight: 600;", "External Links")
)
),
div(
class = "box-body",
style = "padding: 20px;",
div(
style = "margin: 10px 0;",
selectInput("selectspecies", "Choose Species:", choices = c("Human" = "human","Mouse" = "mouse","Fly" = "fly"), width = '100%'),
selectInput("selectsgenetype", "Choose Feature Types:", choices = c("Symbol" = "Symbol","Ensembl ID" = "Ensembl","Entrez ID" = "EntrezID"), width = '100%'),
withSpinner(uiOutput('ExternalLinks.UI'))
)
)
)
)
) # end of col-md-4 div
) # end of fluidRow containing Analysis Results and External Links
) # end of margin-top div
) # end of conditionalPanel
) # end of fluidRow containing both Information Box and tabBox
) # end of degs tabItem
tab_list[["topgenes"]] = tabItem(tabName = "topgenes",
# CSS styles for close button hover effect
tags$style(type="text/css", "#close_topgenes_info:hover { color: #059669 !important; }"),
# CSS styles for info text
tags$style(type="text/css", "#topgenes_info {white-space: pre-wrap; color: #495057; line-height: 1.8;}"),
# Modernized tab styling - ONLY for topgenes page
tags$style("
#topgenestabbox-container {
padding-left: 0 !important;
padding-right: 0 !important;
margin-left: 0 !important;
margin-right: 0 !important;
}
#topgenestabbox-container > .nav-tabs-custom {
margin-left: -15px !important;
margin-right: -15px !important;
width: calc(100% + 30px) !important;
box-sizing: border-box !important;
}
#topgenestabbox-container .nav-tabs-custom .nav-tabs {
background: linear-gradient(to bottom, #f8f9fa 0%, #ffffff 100%);
border-bottom: 2px solid #3b82f6;
border-radius: 8px 8px 0 0;
padding: 10px 15px 0 15px;
margin: 0 !important;
}
#topgenestabbox-container .nav-tabs-custom .nav-tabs li.active:hover a,
#topgenestabbox-container .nav-tabs-custom .nav-tabs li.active a {
background-color: white;
border-color: #3b82f6 #3b82f6 white #3b82f6;
color: #3b82f6;
font-weight: 600;
}
#topgenestabbox-container .nav-tabs-custom .nav-tabs li.active {
border-top-color: #3b82f6;
border-top-width: 3px;
}
#topgenestabbox-container .nav-tabs-custom .nav-tabs li a {
color: #6c757d;
border-radius: 6px 6px 0 0;
transition: all 0.2s ease;
margin-right: 5px;
}
#topgenestabbox-container .nav-tabs-custom .nav-tabs li a:hover {
background-color: #f8f9fa;
color: #3b82f6;
}
#topgenestabbox-container .tab-content {
background: white;
border: 2px solid #3b82f6;
border-top: none;
border-radius: 0 0 8px 8px;
padding: 25px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
margin-left: 0 !important;
margin-right: 0 !important;
}
"),
# Single fluidRow containing both Information Box and Settings/Analysis
fluidRow(id = "topgenes-main-row",
# Information Box - White background + green border (top, full width)
div(
id = "topgenes-info-box",
class = "col-xs-12",
style = "margin-bottom: 10px;",
div(
class = "box",
style = "background: white; border: 2px solid #10b981; border-radius: 8px; box-shadow: 0 2px 6px rgba(0,0,0,0.08); position: relative;",
div(
class = "box-header",
style = "padding: 15px 20px; border-bottom: 2px solid #10b981; position: relative;",
div(
style = "display: flex; align-items: center; gap: 10px;",
icon("info-circle", style = "color: #10b981; font-size: 20px;"),
h4(style = "margin: 0; color: #10b981; font-weight: 600;", "Information")
),
# Close button in top-right corner
actionButton(
"close_topgenes_info",
label = NULL,
icon = icon("times"),
style = "position: absolute; right: 15px; top: 50%; transform: translateY(-50%); background: transparent; border: none; color: #10b981; font-size: 24px; cursor: pointer; padding: 0; line-height: 1; transition: color 0.2s ease;",
class = "btn btn-link"
)
),
div(
class = "box-body",
style = "padding: 15px 20px;",
textOutput("topgenes_info")
)
)
),
# Step1: Common Settings (Left column, accounting for 3/12)
div(
class = "col-md-3",
style = "margin-bottom: 10px;",
div(
style = "background: #f0fdf4; border: 1px solid #10b981; border-left: 4px solid #10b981; padding: 20px; border-radius: 8px; height: 100%;",
h4(icon("sliders-h"), "Step 1: Settings", style = "color: #10b981; margin-bottom: 15px; font-weight: 600; display: flex; align-items: center; gap: 8px;"),
withSpinner(uiOutput("TopGenesClusterResolution.UI"), proxy.height = "10px"),
withSpinner(uiOutput("TopGenesSelectedClusters.UI"), proxy.height = "10px"),
withSpinner(uiOutput("TopGenesAssays.UI"), proxy.height = "10px"),
div(style = "margin-top: 15px;",
checkboxInput("TopGenesClusterLevel", label = "By each cluster", TRUE)
)
)
),
# Step2: Calculate Top Genes (Right column, accounting for 9/12)
div(
class = "col-md-9",
style = "margin-bottom: 10px;",
div(id = "topgenestabbox-container",
tabBox(
title = div(
style = "display: flex; align-items: center; gap: 10px;",
icon("chart-line", style = "color: #3b82f6; font-size: 18px;"),
strong("Step 2: Calculate Top Genes", style = "color: #495057; font-size: 16px;")
),
id = "tabset_topgenes", width = 12,
tabPanel("Find Top Genes by Cell",
div(
style = "background: #eff6ff; border: 1px solid #3b82f6; border-left: 4px solid #3b82f6; padding: 20px; border-radius: 8px; margin-bottom: 25px;",
sliderInput("TopGenesTopPercent", "UMI percentage cutoff(%):", min = 1, max = 50, value = 1, step = 1, width = "100%")
),
div(style = "text-align: center; margin-top: 25px;",
actionButton("TopGenesAnalysis",
icon = shiny::icon("magnifying-glass-chart"),
label = "Analyze",
class = "btn-primary btn-lg",
style = "padding: 12px 35px; border-radius: 8px; font-weight: 600; background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%); border: none; box-shadow: 0 4px 12px rgba(59, 130, 246, 0.3);")
)),
tabPanel("Find Top Genes by Accumulated UMI Counts",
div(
style = "background: #eff6ff; border: 1px solid #3b82f6; border-left: 4px solid #3b82f6; padding: 20px; border-radius: 8px; margin-bottom: 25px;",
sliderInput("TopGenesTopN", "Top n:", min = 10, max = 1000, value = 100, step = 10, width = "100%")
),
div(style = "text-align: center; margin-top: 25px;",
actionButton("TopAccumulatedGenesAnalysis",
icon = shiny::icon("magnifying-glass-chart"),
label = "Analyze",
class = "btn-primary btn-lg",
style = "padding: 12px 35px; border-radius: 8px; font-weight: 600; background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%); border: none; box-shadow: 0 4px 12px rgba(59, 130, 246, 0.3);")
))
)
)
),
# Analysis Results
conditionalPanel(
condition = "output.TopGenes_ready",
div(style = "margin-top: 15px; margin-left: 15px; margin-right: 15px;",
fluidRow(
div(
class = "col-xs-12",
div(
class = "box",
style = "background: white; border: 2px solid #3b82f6; border-radius: 8px; box-shadow: 0 2px 6px rgba(0,0,0,0.08);",
div(
class = "box-header",
style = "padding: 15px 20px; border-bottom: 2px solid #3b82f6;",
div(
style = "display: flex; align-items: center; gap: 10px;",
icon("table", style = "color: #3b82f6; font-size: 18px;"),
h4(style = "margin: 0; color: #3b82f6; font-weight: 600;", "Analysis Results")
)
),
div(
class = "box-body",
style = "padding: 20px;",
withSpinner(DT::dataTableOutput('dataset_topgenes'))
)
)
)
)
)
)
)
)
tab_list[["featuresummary"]] = tabItem(tabName = "featuresummary",
# CSS styles for close button hover effect
tags$style(type="text/css", "#close_featuresummary_info:hover { color: #059669 !important; }"),
# CSS styles for info text
tags$style(type="text/css", "#featuresummary_info {white-space: pre-wrap; color: #495057; line-height: 1.8;}"),
# Single fluidRow containing both Information Box and Settings/Analysis
fluidRow(id = "featuresummary-main-row",
# Information Box - White background + green border
div(
id = "featuresummary-info-box",
class = "col-xs-12",
style = "margin-bottom: 10px;",
div(
class = "box",
style = "background: white; border: 2px solid #10b981; border-radius: 8px; box-shadow: 0 2px 6px rgba(0,0,0,0.08); position: relative;",
div(
class = "box-header",
style = "padding: 15px 20px; border-bottom: 2px solid #10b981; position: relative;",
div(
style = "display: flex; align-items: center; gap: 10px;",
icon("info-circle", style = "color: #10b981; font-size: 20px;"),
h4(style = "margin: 0; color: #10b981; font-weight: 600;", "Information")
),
# Close button in top-right corner
actionButton(
"close_featuresummary_info",
label = NULL,
icon = icon("times"),
style = "position: absolute; right: 15px; top: 50%; transform: translateY(-50%); background: transparent; border: none; color: #10b981; font-size: 24px; cursor: pointer; padding: 0; line-height: 1; transition: color 0.2s ease;",
class = "btn btn-link"
)
),
div(
class = "box-body",
style = "padding: 15px 20px;",
textOutput("featuresummary_info")
)
)
),
# Settings (Left column, accounting for 3/12)
div(
class = "col-md-3",
style = "margin-bottom: 10px;",
div(
style = "background: #f0fdf4; border: 1px solid #10b981; border-left: 4px solid #10b981; padding: 20px; border-radius: 8px; height: 100%;",
h4(icon("sliders-h"), "Settings", style = "color: #10b981; margin-bottom: 15px; font-weight: 600; display: flex; align-items: center; gap: 8px;"),
textAreaInput("FeatureSummarySymbol", "Input Gene Symbols:", value = "", height = '100px', resize = "vertical"),
withSpinner(uiOutput("FeatureSummaryClusterResolution.UI"), proxy.height = "10px"),
withSpinner(uiOutput("FeatureSummarySelectedClusters.UI"), proxy.height = "10px"),
withSpinner(uiOutput("FeatureSummaryAssays.UI"), proxy.height = "10px"),
div(style = "margin-top: 15px;",
checkboxInput("FeatureSummaryClusterLevel", label = "By each cluster", TRUE)
),
div(style = "margin-top: 20px; text-align: center;",
actionButton("FeatureSummaryAnalysis",
icon = shiny::icon("magnifying-glass-chart"),
label = "Analyze",
class = "btn-primary btn-lg",
style = "padding: 12px 35px; border-radius: 8px; font-weight: 600; background: linear-gradient(135deg, #10b981 0%, #059669 100%); border: none; box-shadow: 0 4px 12px rgba(16, 185, 129, 0.3);")
)
)
),
# Gene Short Summary (Right column, accounting for 9/12)
div(
class = "col-md-9",
style = "margin-bottom: 10px;",
conditionalPanel(
condition = "output.FeatureSummary_ready",
div(
class = "box",
style = "background: white; border: 2px solid #3b82f6; border-radius: 8px; box-shadow: 0 2px 6px rgba(0,0,0,0.08);",
div(
class = "box-header",
style = "padding: 15px 20px; border-bottom: 2px solid #3b82f6;",
div(
style = "display: flex; align-items: center; gap: 10px;",
icon("table", style = "color: #3b82f6; font-size: 18px;"),
h4(style = "margin: 0; color: #3b82f6; font-weight: 600;", "Gene Short Summary")
)
),
div(
class = "box-body",
style = "padding: 20px;",
withSpinner(DT::dataTableOutput('dataset_featuresummary'))
)
)
)
)
)
)
tab_list[["featurecorrelation"]] = tabItem(tabName = "featurecorrelation",
# CSS styles for close button hover effect
tags$style(type="text/css", "#close_featurecorrelation_info:hover { color: #059669 !important; }"),
# CSS styles for info text
tags$style(type="text/css", "#featurecorrelation_info {white-space: pre-wrap; color: #495057; line-height: 1.8;}"),
# Modernized tab styling - ONLY for featurecorrelation page
tags$style("
#featurecorrelationtabbox-container {
padding-left: 0 !important;
padding-right: 0 !important;
margin-left: 0 !important;
margin-right: 0 !important;
}
#featurecorrelationtabbox-container > .nav-tabs-custom {
margin-left: -15px !important;
margin-right: -15px !important;
width: calc(100% + 30px) !important;
box-sizing: border-box !important;
}
#featurecorrelationtabbox-container .nav-tabs-custom .nav-tabs {
background: linear-gradient(to bottom, #f8f9fa 0%, #ffffff 100%);
border-bottom: 2px solid #3b82f6;
border-radius: 8px 8px 0 0;
padding: 10px 15px 0 15px;
margin: 0 !important;
}
#featurecorrelationtabbox-container .nav-tabs-custom .nav-tabs li.active:hover a,
#featurecorrelationtabbox-container .nav-tabs-custom .nav-tabs li.active a {
background-color: white;
border-color: #3b82f6 #3b82f6 white #3b82f6;
color: #3b82f6;
font-weight: 600;
}
#featurecorrelationtabbox-container .nav-tabs-custom .nav-tabs li.active {
border-top-color: #3b82f6;
border-top-width: 3px;
}
#featurecorrelationtabbox-container .nav-tabs-custom .nav-tabs li a {
color: #6c757d;
border-radius: 6px 6px 0 0;
transition: all 0.2s ease;
margin-right: 5px;
}
#featurecorrelationtabbox-container .nav-tabs-custom .nav-tabs li a:hover {
background-color: #f8f9fa;
color: #3b82f6;
}
#featurecorrelationtabbox-container .tab-content {
background: white;
border: 2px solid #3b82f6;
border-top: none;
border-radius: 0 0 8px 8px;
padding: 25px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
margin-left: 0 !important;
margin-right: 0 !important;
}
"),
# Single fluidRow containing both Information Box and Settings/Analysis
fluidRow(id = "featurecorrelation-main-row",
# Information Box - White background + green border
div(
id = "featurecorrelation-info-box",
class = "col-xs-12",
style = "margin-bottom: 10px;",
div(
class = "box",
style = "background: white; border: 2px solid #10b981; border-radius: 8px; box-shadow: 0 2px 6px rgba(0,0,0,0.08); position: relative;",
div(
class = "box-header",
style = "padding: 15px 20px; border-bottom: 2px solid #10b981; position: relative;",
div(
style = "display: flex; align-items: center; gap: 10px;",
icon("info-circle", style = "color: #10b981; font-size: 20px;"),
h4(style = "margin: 0; color: #10b981; font-weight: 600;", "Information")
),
# Close button
actionButton(
"close_featurecorrelation_info",
label = NULL,
icon = icon("times"),
style = "position: absolute; right: 15px; top: 50%; transform: translateY(-50%); background: transparent; border: none; color: #10b981; font-size: 24px; cursor: pointer; padding: 0; line-height: 1; transition: color 0.2s ease;",
class = "btn btn-link"
)
),
div(
class = "box-body",
style = "padding: 15px 20px;",
textOutput("featurecorrelation_info")
)
)
),
# Step 1: Common Settings(Left column, accounting for 3/12)
div(
class = "col-md-3",
style = "margin-bottom: 10px;",
div(
style = "background: #f0fdf4; border: 1px solid #10b981; border-left: 4px solid #10b981; padding: 20px; border-radius: 8px; height: 100%;",
h4(icon("sliders-h"), "Step 1: Settings", style = "color: #10b981; margin-bottom: 15px; font-weight: 600; display: flex; align-items: center; gap: 8px;"),
withSpinner(uiOutput("FeatureCorrelationClusterResolution.UI"), proxy.height = "10px"),
withSpinner(uiOutput("FeatureCorrelationIdentsSelected.UI"), proxy.height = "10px"),
withSpinner(uiOutput("FeatureCorrelationAssays.UI"), proxy.height = "10px"),
selectInput("correlationmethod", "Correlation Method:", choices = c(pearson = "pearson", spearman = "spearman"))
)
),
# Step 2: Calculate Correlation (Right column, accounting for 9/12)
div(
class = "col-md-9",
style = "margin-bottom: 10px;",
div(id = "featurecorrelationtabbox-container",
tabBox(
title = div(
style = "display: flex; align-items: center; gap: 10px;",
icon("chart-line", style = "color: #3b82f6; font-size: 18px;"),
strong("Step 2: Calculate Correlation", style = "color: #495057; font-size: 16px;")
),
id = "tabset_featurecorrelation", width = 12,
tabPanel("Find Top Correlated Gene Pairs",
div(style = "text-align: center; margin-top: 25px;",
actionButton("TopCorrelationAnalysis",
icon = shiny::icon("magnifying-glass-chart"),
label = "Analyze",
class = "btn-primary btn-lg",
style = "padding: 12px 35px; border-radius: 8px; font-weight: 600; background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%); border: none; box-shadow: 0 4px 12px rgba(59, 130, 246, 0.3);")
)),
tabPanel("Find Top Correlated Genes for A Gene",
div(
style = "background: #eff6ff; border: 1px solid #3b82f6; border-left: 4px solid #3b82f6; padding: 20px; border-radius: 8px; margin-bottom: 25px;",
textInput(inputId = "MostCorrelatedAGene", label = "Input a gene:", width = '100%')
),
div(style = "text-align: center; margin-top: 25px;",
actionButton("MostCorrelatedAnalysis",
icon = shiny::icon("magnifying-glass-chart"),
label = "Analyze",
class = "btn-primary btn-lg",
style = "padding: 12px 35px; border-radius: 8px; font-weight: 600; background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%); border: none; box-shadow: 0 4px 12px rgba(59, 130, 246, 0.3);")
)),
tabPanel("Calculate Correlation of All Pairs in A Gene List",
div(
style = "background: #eff6ff; border: 1px solid #3b82f6; border-left: 4px solid #3b82f6; padding: 20px; border-radius: 8px; margin-bottom: 25px;",
textAreaInput(inputId = "CorrelationGeneList", label = "Input a group of genes:", width = '100%', height = '100px', resize = "vertical")
),
div(style = "text-align: center; margin-top: 25px;",
actionButton("calculatecorrelation",
icon = shiny::icon("save"),
label = "Analyze",
class = "btn-primary btn-lg",
style = "padding: 12px 35px; border-radius: 8px; font-weight: 600; background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%); border: none; box-shadow: 0 4px 12px rgba(59, 130, 246, 0.3);")
))
)
)
),
# Analysis Results
div(
class = "col-xs-12",
style = "margin-bottom: 10px;",
conditionalPanel(
condition = "output.FeatureCorrelation_ready",
div(
class = "box",
style = "background: white; border: 2px solid #3b82f6; border-radius: 8px; box-shadow: 0 2px 6px rgba(0,0,0,0.08);",
div(
class = "box-header",
style = "padding: 15px 20px; border-bottom: 2px solid #3b82f6;",
div(
style = "display: flex; align-items: center; gap: 10px;",
icon("table", style = "color: #3b82f6; font-size: 18px;"),
h4(style = "margin: 0; color: #3b82f6; font-weight: 600;", "Analysis Results")
)
),
div(
class = "box-body",
style = "padding: 20px;",
withSpinner(DT::dataTableOutput('dataset_correlation'))
)
)
)
)
)
)
tab_list[["renameclusters"]] = tabItem(tabName = "renameclusters",
fluidRow(id = "renameclusters-main-row",
# Rename Clusters Table (Left column, accounting for 9/12)
div(
class = "col-md-9",
div(
class = "box",
style = "background: white; border: 2px solid #3b82f6; border-radius: 8px; box-shadow: 0 2px 6px rgba(0,0,0,0.08); height: 100%;",
div(
class = "box-header",
style = "padding: 15px 20px; border-bottom: 2px solid #3b82f6;",
div(
style = "display: flex; align-items: center; gap: 10px;",
icon("table", style = "color: #3b82f6; font-size: 18px;"),
h4(style = "margin: 0; color: #3b82f6; font-weight: 600;", "Rename Clusters")
)
),
div(
class = "box-body",
style = "padding: 20px;",
withSpinner(DT::dataTableOutput('cell_annotation')),
conditionalPanel(
condition = "output.renameclusterscheck_OK",
div(style = "margin-top: 20px;",
plotOutput("renameclusterdimplot")
)
)
)
)
),
# Settings (Right column, accounting for 3/12)
div(
class = "col-md-3",
div(
style = "background: #f0fdf4; border: 1px solid #10b981; border-left: 4px solid #10b981; padding: 20px; border-radius: 8px; height: 100%;",
h4(icon("sliders-h"), "Settings", style = "color: #10b981; margin-bottom: 15px; font-weight: 600; display: flex; align-items: center; gap: 8px;"),
withSpinner(uiOutput("renameclustersClusterResolution.UI"), proxy.height = "10px"),
withSpinner(uiOutput("renameclustersDimensionReduction.UI"), proxy.height = "10px"),
textInput('renameclustersNewClusterName', 'Input Cluster name:', value = "group"),
div(
style = "background: #eff6ff; padding: 10px; border-radius: 4px; margin-top: 15px;",
uiOutput("renameclustersNewClusterNamehints.UI")
),
div(style = "margin-top: 20px; text-align: center;",
actionButton("renameclustersCheck",
icon = shiny::icon("check"),
label = "Check",
class = "btn-primary btn-lg",
style = "padding: 10px 30px; border-radius: 8px; font-weight: 600; background: linear-gradient(135deg, #10b981 0%, #059669 100%); border: none; box-shadow: 0 4px 12px rgba(16, 185, 129, 0.3);")
),
conditionalPanel(
condition = "output.renameclusterscheck_OK",
div(style = "margin-top: 15px;",
div(style = "display: flex; gap: 10px; justify-content: center;",
actionButton("renameclustersSubmit",
icon = shiny::icon("arrows-rotate"),
label = "Update",
class = "btn-primary",
style = "padding: 10px 20px; border-radius: 8px; font-weight: 600; background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%); border: none;"),
downloadButton("renameclustersDownload",
"Download",
icon = shiny::icon("file-arrow-down"),
class = "btn-primary",
style = "padding: 10px 20px; border-radius: 8px; font-weight: 600; background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%); border: none;")
),
div(
style = "background: #fef3c7; border-left: 3px solid #f59e0b; padding: 10px; border-radius: 4px; margin-top: 15px;",
p("Tips: Download the cluster name mapping file to save new cluster names permanently.", style = "font-size: 12px; margin: 0; color: #92400e;")
)
)
)
)
)
)
)
tab_list[["geneclusters"]] = tabItem(tabName = "geneclusters",
fluidRow(
box(title = "Gene Expression Histogram",
plotOutput("geneclusters_histogram"),
width = 9, status = "primary", collapsible = TRUE, solidHeader = TRUE),
box(title = "Settings", solidHeader = TRUE, status = "primary", width = 3,
textInput("geneclustersGeneSymbol", "Gene Symbol:", value = "", width = '100%'),
withSpinner(uiOutput("GeneclustersAssays.UI"), proxy.height = "10px"),
withSpinner(uiOutput("geneclustersAssaySlots.UI"), proxy.height = "10px"),
div(style = "margin-top: 15px;",
actionButton("geneclustersPlotHistogram", label = "Generate Histogram",
icon = icon("chart-bar"), class = "btn-primary",
style = "width: 100%; padding: 10px; border-radius: 6px; font-weight: 600;")
)
)
),
conditionalPanel(
condition = "output.geneclusters_histogram_ready",
fluidRow(
div(class = "col-xs-12",
style = "margin-top: 15px;",
div(class = "box", style = "background: white; border: 2px solid #f59e0b; border-radius: 8px;",
div(class = "box-body", style = "padding: 20px;",
textInput("geneclustersCutoffs", "Cutoff values (comma-separated, e.g. 0.5, 1.0, 2.0):",
value = "", width = "100%"),
div(style = "margin-top: 10px;",
actionButton("geneclustersApplyCutoffs", label = "Apply Cutoffs",
icon = icon("cut"), class = "btn-warning",
style = "padding: 10px 30px; border-radius: 6px; font-weight: 600;")
)
)
)
)
)
),
conditionalPanel(
condition = "output.geneclusters_table_ready",
fluidRow(style = "margin-top: 15px;",
div(class = "col-md-9",
div(class = "box", style = "background: white; border: 2px solid #3b82f6; border-radius: 8px;",
div(class = "box-header", style = "padding: 15px 20px; border-bottom: 2px solid #3b82f6;",
h4(icon("table"), "Classification Table", style = "color: #3b82f6; font-weight: 600; margin: 0;")
),
div(class = "box-body", style = "padding: 20px;",
withSpinner(DT::dataTableOutput('geneclusters_table'))
)
)
),
div(class = "col-md-3",
div(style = "background: #f0fdf4; border: 1px solid #10b981; border-left: 4px solid #10b981; padding: 20px; border-radius: 8px;",
h4(icon("check"), "Actions", style = "color: #10b981; margin-bottom: 15px; font-weight: 600;"),
div(style = "margin-bottom: 15px;",
actionButton("geneclustersCheck", label = "Check", icon = icon("check"),
class = "btn-primary", style = "width: 100%; padding: 10px; border-radius: 6px; font-weight: 600;")
),
actionButton("geneclustersSubmit", label = "Submit", icon = icon("upload"),
class = "btn-success", style = "width: 100%; padding: 10px; border-radius: 6px; font-weight: 600;"),
div(style = "margin-top: 15px;",
downloadButton("geneclustersDownload", "Download Mapping", icon = icon("file-arrow-down"),
class = "btn-warning", style = "width: 100%; padding: 10px; border-radius: 6px; font-weight: 600;")
),
div(style = "background: #fef3c7; border-left: 3px solid #f59e0b; padding: 10px; border-radius: 4px; margin-top: 15px;",
p("Tips: Click 'Check' to preview, then 'Submit' to add annotation.", style = "font-size: 12px; margin: 0; color: #92400e;")
)
)
)
)
)
)
tab_list[["addmodulescore"]] = tabItem(tabName = "addmodulescore",
fluidRow(
box(title = "Add Module Score", solidHeader = TRUE, status = "primary", width = 12,
textAreaInput("ModuleScoreFeatures", "Gene Symbols:",
value = "", height = '100px', resize = "vertical",
placeholder = "Paste gene symbols, one per line or comma-separated"),
withSpinner(uiOutput("ModuleScoreAssays.UI"), proxy.height = "10px"),
div(style = "margin-top: 15px;",
actionButton("ModuleScoreCheckGenes", label = "Check Genes",
icon = icon("check"), class = "btn-primary",
style = "padding: 10px 30px; border-radius: 6px; font-weight: 600;")
),
hr(),
uiOutput("ModuleScoreName.UI"),
uiOutput("ModuleScoreSubmit.UI")
)
),
uiOutput("ModuleScoreResult.UI")
)
tab_list[["combinedclusters"]] = tabItem(tabName = "combinedclusters",
fluidRow(
box(title = "Combination Table",
DT::dataTableOutput('combinedclusters_table'),
width = 9, status = "primary", collapsible = TRUE, solidHeader = TRUE),
box(title = "Settings", solidHeader = TRUE, status = "primary", width = 3,
withSpinner(uiOutput("CombinedClusterResolution.UI"), proxy.height = "10px"),
withSpinner(uiOutput("CombinedClusterResolutionSecond.UI"), proxy.height = "10px"),
selectInput("CombinedClusterConnector", "Connector:",
choices = c("-" = "-", "+" = "+", "_" = "_"), selected = "_"),
div(style = "margin-top: 15px;",
actionButton("combinedclustersGenerate", label = "Generate",
icon = icon("table"), class = "btn-primary",
style = "width: 100%; padding: 10px; border-radius: 6px; font-weight: 600;")
),
hr(),
uiOutput("combinedclustersName.UI"),
uiOutput("combinedclustersSubmit.UI")
)
)
)
tab_list[["featuresdf"]] = tabItem(tabName = "featuresdf",
fluidRow(id = "featuresdf-main-row",
div(
class = "col-xs-12",
div(
class = "box",
style = "background: white; border: 2px solid #10b981; border-radius: 8px; box-shadow: 0 2px 6px rgba(0,0,0,0.08);",
div(
class = "box-header",
style = "padding: 15px 20px; border-bottom: 2px solid #10b981;",
div(
style = "display: flex; align-items: center; gap: 10px;",
icon("search", style = "color: #10b981; font-size: 18px;"),
h4(style = "margin: 0; color: #10b981; font-weight: 600;", "Search Features")
)
),
div(
class = "box-body",
style = "padding: 20px;",
withSpinner(uiOutput("FeaturesDataframeAssays.UI"), proxy.height = "10px"),
withSpinner(DT::dataTableOutput('dataset_features'))
)
)
)
)
)
tab_list[["cellmetadata"]] = tabItem(tabName = "cellmetadata",
fluidRow(id = "cellmetadata-main-row",
div(
class = "col-xs-12",
div(
class = "box",
style = "background: white; border: 2px solid #3b82f6; border-radius: 8px; box-shadow: 0 2px 6px rgba(0,0,0,0.08);",
div(
class = "box-header",
style = "padding: 15px 20px; border-bottom: 2px solid #3b82f6;",
div(
style = "display: flex; align-items: center; gap: 10px;",
icon("table", style = "color: #3b82f6; font-size: 18px;"),
h4(style = "margin: 0; color: #3b82f6; font-weight: 600;", "Metadata of Cells")
)
),
div(
class = "box-body",
style = "padding: 20px;",
withSpinner(tagList(
div(style = "margin-bottom: 15px; text-align: right;",
downloadButton("download_meta_data", "Download", icon = shiny::icon("file-arrow-down"),
class = "btn-primary",
style = "padding: 10px 25px; border-radius: 8px; font-weight: 600; background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%); border: none;")
),
DT::dataTableOutput('dataset_meta')
))
)
)
)
)
)
tab_list[["objectstructure"]] = tabItem(tabName = "objectstructure",
fluidRow(id = "objectstructure-main-row",
div(
class = "col-xs-12",
div(
class = "box",
style = "background: white; border: 2px solid #8b5cf6; border-radius: 8px; box-shadow: 0 2px 6px rgba(0,0,0,0.08);",
div(
class = "box-header",
style = "padding: 15px 20px; border-bottom: 2px solid #8b5cf6;",
div(
style = "display: flex; align-items: center; gap: 10px;",
icon("code", style = "color: #8b5cf6; font-size: 18px;"),
h4(style = "margin: 0; color: #8b5cf6; font-weight: 600;", "Structure of Seurat Object")
)
),
div(
class = "box-body",
style = "padding: 20px;",
div(
style = "background: #f5f3ff; border: 1px solid #8b5cf6; border-left: 4px solid #8b5cf6; padding: 20px; border-radius: 8px; margin-bottom: 20px;",
h4(icon("sliders-h"), "Display Settings", style = "color: #8b5cf6; margin-bottom: 15px; font-weight: 600; display: flex; align-items: center; gap: 8px;"),
sliderInput("ObjectStrutureLevel", label = "Structure Depth:", min = 1, max = 10, value = 3)
),
withSpinner(verbatimTextOutput("object_structure"))
)
)
)
)
)
# tab_list[["about"]] = tabItem(tabName = "about",
# fluidRow(
# box(title = "About Seurat Explorer", solidHeader = TRUE, status = "primary", width = 12,
# HTML(markdown::markdownToHTML(knitr::knit(system.file("extdata", "README.Rmd", package ="SeuratExplorer"), quiet=T),fragment.only = T)))
# )
# )
return(tab_list)
}
#' UI
#' @import shiny shinydashboard shinyWidgets
#' @export
#' @return the UI part of the shiny app
#' @examples
#' ui()
#'
ui <- function(){
# shinydashboard::notificationItem: the default function can not open link
# to make a new function: refer to: https://forum.posit.co/t/shinydashboard-notification-item-with-link-in-new-tab/37580/2
notificationItemWithAttr <- function(text, icon = shiny::icon("warning"), status = "success", href = NULL, ...) {
if (is.null(href)){href <- "#"}
icon <- shiny::tagAppendAttributes(icon, class = paste0("text-",status))
shiny::tags$li(a(href = href, icon, text, ...))
}
# Header ----
header <- shinydashboard::dashboardHeader(title = p(strong(em("Seurat Explorer"))),
shinydashboard::dropdownMenu(type = "notifications", icon = shiny::icon("github"), headerText = "R packages on Github:",
notificationItemWithAttr(icon = shiny::icon("github"), status = "info", text = "Seurat Explorer", href = "https://github.com/fentouxungui/SeuratExplorer", target = "_blank"),
notificationItemWithAttr(icon = shiny::icon("github"), status = "info", text = "Seurat Explorer Server", href = "https://github.com/fentouxungui/SeuratExplorerServer", target = "_blank")))
# Sidebar ----
sidebar <- shinydashboard::dashboardSidebar(
# tags$head(tags$style(HTML('* {font-family: "Times New Roman"};'))),
shinydashboard::sidebarMenu(
shinydashboard::menuItem("Dataset", tabName = "dataset", icon = shiny::icon("database")),
explorer_sidebar_ui()
)
)
# BODY ----
tab_list <- list()
tab_list[["dataset"]] <- shinydashboard::tabItem(tabName = "dataset",
fluidRow(
# upload a file
box(status = "primary", title = "Upload Data", width = 12, collapsible = TRUE, solidHeader = TRUE,
fileInput("dataset_file", "Choose A rds or qs2 file of Seurat Object:", accept = c('.rds', ".qs2")),
uiOutput("dataOverview"))
)
)
tab_list <- explorer_body_ui(tab_list = tab_list)
body <- shinydashboard::dashboardBody(
shinyjs::useShinyjs(),
tags$head(
tags$style(HTML("
/* Global font optimization */
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
}
/* Sidebar menu: compact spacing */
.sidebar-menu .treeview-menu li a {
white-space: nowrap;
padding: 4px 5px 4px 12px !important;
}
/* Optimize box style - keep default background */
.box {
border-radius: 6px;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
transition: box-shadow 0.2s ease;
}
.box:hover {
box-shadow: 0 2px 6px rgba(0,0,0,0.15);
}
.box.box-solid .box-title {
font-weight: 600;
}
/* Optimize button style */
.btn {
border-radius: 6px;
font-weight: 500;
transition: all 0.2s ease;
}
.btn:hover {
box-shadow: 0 2px 6px rgba(0,0,0,0.15);
}
/* Modal Dialog Styles */
.modal-content {
border-radius: 8px;
box-shadow: 0 10px 25px rgba(0,0,0,0.15);
}
.modal-header {
border-radius: 8px 8px 0 0;
padding: 16px 20px;
}
.modal-body {
padding: 20px;
}
.modal-footer {
border-top: 1px solid #dee2e6;
padding: 16px 20px;
}
.modal-footer .btn {
border-radius: 6px;
padding: 8px 16px;
font-weight: 500;
}
/* Collapsible parameter group styles */
summary::-webkit-details-marker,
summary::marker {
display: none;
}
.param-group-chevron {
transition: transform 0.3s ease;
display: inline-block;
}
details:not([open]) .param-group-chevron {
transform: rotate(-90deg);
}
/* Responsive optimization */
@media (max-width: 768px) {
.content-wrapper {
padding: 10px;
}
.box {
margin-bottom: 10px;
}
}
"))
),
div(class= "tab-content", tab_list),
tags$script(HTML(
"document.querySelector('body > div.wrapper > header > nav > div > ul > li > a > span').style.visibility = 'hidden';"
)) # to hide how many notifications in shinydashboard::dropdownMenu(), refer to:https://stackoverflow.com/questions/65915414/alter-dropdown-menu-in-shiny
)
# combine
ui_out <- shinydashboard::dashboardPage(header, sidebar, body,
title = "Seurat Explorer")
return(ui_out)
}
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.