update
This commit is contained in:
280
samplesize/reg_linear/app.R
Normal file
280
samplesize/reg_linear/app.R
Normal file
@@ -0,0 +1,280 @@
|
||||
# ==============================================================================
|
||||
# ỨNG DỤNG SHINY TÍNH CỠ MẪU CHO HỒI QUY LOGISTIC (PHIÊN BẢN ỔN ĐỊNH)
|
||||
# - Sửa lỗi hiển thị công thức bằng cách escape ký tự `\` trong R string.
|
||||
# Author: Gemini & User Collaboration
|
||||
# Date: 2025-10-17
|
||||
# ==============================================================================
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# THIẾT LẬP: Tải các thư viện cần thiết
|
||||
# ------------------------------------------------------------------------------
|
||||
library(shiny)
|
||||
library(bslib)
|
||||
library(ggplot2)
|
||||
library(shinycssloaders)
|
||||
library(dplyr)
|
||||
library(purrr)
|
||||
library(scales)
|
||||
|
||||
# ==============================================================================
|
||||
# PHẦN 1: CÁC HÀM HỖ TRỢ (HELPER FUNCTIONS)
|
||||
# ==============================================================================
|
||||
|
||||
#' Tính cỡ mẫu cho hồi quy logistic theo công thức xấp xỉ của Hsieh (1989).
|
||||
calc_n_logistic_hsieh <- function(beta, var_x, p_mean, power = 0.8, sig.level = 0.05) {
|
||||
z_a <- qnorm(1 - sig.level / 2)
|
||||
z_b <- qnorm(power)
|
||||
denom <- (beta^2) * var_x * p_mean * (1 - p_mean)
|
||||
if (denom <= 0) return(NA_real_)
|
||||
n <- (z_a + z_b)^2 / denom
|
||||
ceiling(n)
|
||||
}
|
||||
|
||||
#' Ước tính công suất thực nghiệm cho hồi quy logistic bằng mô phỏng Monte Carlo.
|
||||
simulate_logistic_power <- function(n, beta, intercept = 0, x_dist = list(type = "normal", mean = 0, sd = 1),
|
||||
n_sim = 1000, alpha = 0.05) {
|
||||
sim_results <- replicate(n_sim, {
|
||||
x <- switch(x_dist$type,
|
||||
"normal" = rnorm(n, mean = x_dist$mean, sd = x_dist$sd),
|
||||
"binary" = rbinom(n, size = 1, prob = x_dist$prob),
|
||||
"uniform" = runif(n, min = x_dist$min, max = x_dist$max))
|
||||
|
||||
linpred <- intercept + beta * x
|
||||
p <- 1 / (1 + exp(-linpred))
|
||||
y <- rbinom(n, 1, p)
|
||||
|
||||
fit <- try(glm(y ~ x, family = binomial), silent = TRUE)
|
||||
if (inherits(fit, "try-error")) return(NA_real_)
|
||||
|
||||
coefs <- summary(fit)$coefficients
|
||||
if ("x" %in% rownames(coefs)) {
|
||||
as.numeric(coefs["x", "Pr(>|z|)"] < alpha)
|
||||
} else {
|
||||
0
|
||||
}
|
||||
})
|
||||
mean(sim_results, na.rm = TRUE)
|
||||
}
|
||||
|
||||
# ==============================================================================
|
||||
# PHẦN 2: GIAO DIỆN NGƯỜI DÙNG (USER INTERFACE - UI)
|
||||
# ==============================================================================
|
||||
|
||||
ui <- fluidPage(
|
||||
theme = bs_theme(version = 5, bootswatch = "cerulean"),
|
||||
withMathJax(),
|
||||
|
||||
titlePanel("Công Cụ Tính Cỡ Mẫu Cho Hồi Quy Logistic"),
|
||||
|
||||
sidebarLayout(
|
||||
sidebarPanel(
|
||||
width = 4,
|
||||
h4("Nhập Tham Số"),
|
||||
br(),
|
||||
h5("Tham số mô hình"),
|
||||
sliderInput("beta", "Giá trị β (log-odds) kỳ vọng:", value = 0.693, min = -2, max = 2, step = 0.01),
|
||||
sliderInput("p_mean_event", "Tỉ lệ 'event' trung bình trong quần thể:", value = 0.25, min = 0.01, max = 0.99),
|
||||
selectInput("x_type", "Phân phối của biến độc lập X:",
|
||||
choices = c("Nhị phân (0/1)" = "binary", "Chuẩn (Normal)" = "normal", "Đồng nhất (Uniform)" = "uniform")),
|
||||
conditionalPanel("input.x_type == 'normal'", numericInput("x_sd", "Độ lệch chuẩn của X:", value = 1, min = 0.1)),
|
||||
conditionalPanel("input.x_type == 'binary'", sliderInput("x_prob", "Tỉ lệ P(X=1):", value = 0.3, min = 0.01, max = 0.99)),
|
||||
conditionalPanel("input.x_type == 'uniform'", numericInput("x_min", "Min của X:", value = -1), numericInput("x_max", "Max của X:", value = 1)),
|
||||
|
||||
hr(),
|
||||
h5("Tham số kiểm định"),
|
||||
sliderInput("power_log", "Công suất mong muốn (1 - \\(\\beta\\)):", value = 0.8, min = 0.5, max = 0.99),
|
||||
sliderInput("alpha_log", "Mức ý nghĩa (\\(\\alpha\\)):", value = 0.05, min = 0.01, max = 0.1),
|
||||
hr(),
|
||||
actionButton("go_log", "Tính toán & Phân tích", class = "btn-primary w-100", icon = icon("calculator"))
|
||||
),
|
||||
|
||||
mainPanel(
|
||||
width = 8,
|
||||
tabsetPanel(
|
||||
id = "log_results_tabs",
|
||||
type = "pills",
|
||||
tabPanel("Kết quả & Diễn giải",
|
||||
withSpinner(uiOutput("log_result_text"), type = 6, color = "#007bff")),
|
||||
tabPanel("Đồ thị Power & Mô phỏng",
|
||||
withSpinner(plotOutput("log_power_plot"), type = 6, color = "#007bff"),
|
||||
hr(),
|
||||
h4("Phân tích Mô phỏng Chi tiết"),
|
||||
p("Chạy mô phỏng sâu hơn với một cỡ mẫu cụ thể để xem phân phối của p-value và ước tính power chính xác hơn."),
|
||||
fluidRow(
|
||||
column(6, numericInput("n_for_sim_detail", "Nhập cỡ mẫu (n) để mô phỏng:", value = 200, min = 10)),
|
||||
column(6, actionButton("run_sim_detail", "Chạy mô phỏng chi tiết", icon = icon("play-circle"), class="btn-success", style="margin-top: 25px;"))
|
||||
),
|
||||
withSpinner(plotOutput("log_sim_detail_plot"), type = 6, color = "#007bff")),
|
||||
tabPanel("Phân tích Effect Size",
|
||||
withSpinner(uiOutput("log_effect_size_ui"), type = 6, color = "#007bff")),
|
||||
|
||||
# --- TAB CÔNG THỨC ĐƯỢC ĐỊNH NGHĨA TĨNH TRONG UI (SỬA LỖI) ---
|
||||
tabPanel("Giả thuyết, Công thức & Ví dụ",
|
||||
h4("Giả thuyết thống kê"),
|
||||
HTML("Kiểm định trong hồi quy logistic thường tập trung vào việc xem hệ số β có khác 0 một cách có ý nghĩa thống kê hay không.<br>"),
|
||||
withMathJax(HTML("$$H_0: \\beta = 0$$")),
|
||||
em("Diễn giải: Biến độc lập X không có ảnh hưởng đến log-odds của kết quả Y (tương đương Odds Ratio = 1)."),
|
||||
br(),
|
||||
withMathJax(HTML("$$H_a: \\beta \\neq 0$$")),
|
||||
em("Diễn giải: Biến độc lập X có ảnh hưởng đến log-odds của kết quả Y (tương đương Odds Ratio \\neq 1)."),
|
||||
hr(),
|
||||
|
||||
h4("Công thức tính cỡ mẫu (Xấp xỉ Hsieh, 1989)"),
|
||||
withMathJax(HTML("$$n \\approx \\frac{(Z_{1-\\alpha/2} + Z_{\\text{power}})^2}{\\beta^2 \\cdot \\text{Var}(X) \\cdot \\bar{p}(1-\\bar{p})}$$")),
|
||||
p(strong("Trong đó:")),
|
||||
tags$ul(
|
||||
withMathJax(tags$li("\\(Z_{1-\\alpha/2}\\) là giá trị Z-score ứng với mức ý nghĩa \\(\\alpha\\) (ví dụ: 1.96 cho \\(\\alpha=0.05\\).)")),
|
||||
withMathJax(tags$li("\\(Z_{\\text{power}}\\) là giá trị Z-score ứng với công suất mong muốn (ví dụ: 0.84 cho power=0.8).")),
|
||||
withMathJax(tags$li("\\(\\beta\\) là hệ số hồi quy log-odds kỳ vọng (effect size).")),
|
||||
withMathJax(tags$li("\\(\\text{Var}(X)\\) là phương sai của biến độc lập X.")),
|
||||
withMathJax(tags$li("\\(\\bar{p}\\) là tỉ lệ trung bình của kết quả (event) trong quần thể."))
|
||||
),
|
||||
hr(),
|
||||
|
||||
h4("Ví dụ trong Y tế công cộng"),
|
||||
p(strong("Bối cảnh nghiên cứu:")),
|
||||
p("Một nhà nghiên cứu muốn tính cỡ mẫu để xem liệu việc ", strong("hút thuốc lá (biến X)"), " có phải là yếu tố nguy cơ cho ", strong("bệnh tăng huyết áp (biến Y)"), " hay không."),
|
||||
p(strong("Diễn giải các tham số:")),
|
||||
tags$ul(
|
||||
tags$li(strong("Kết quả (Event):"), " Bị tăng huyết áp (Y=1)."),
|
||||
tags$li(strong("Biến độc lập (X):"), " Hút thuốc lá (X=1) vs. không hút (X=0). Đây là biến nhị phân."),
|
||||
tags$li(strong("\\(\\beta\\) (Effect size):"), withMathJax(HTML(" Dựa trên y văn, nhà nghiên cứu kỳ vọng Odds Ratio (OR) của việc hút thuốc gây tăng huyết áp là 2.0. Do đó, effect size mong muốn là \\(\\beta = \\ln(OR) = \\ln(2.0) \\approx 0.693\\)"))),
|
||||
tags$li(strong("\\(\\bar{p}\\) (Tỉ lệ event TB):"), withMathJax(HTML(" Tỉ lệ mắc tăng huyết áp chung trong quần thể ước tính là \\(\\bar{p} = 0.25\\)"))),
|
||||
tags$li(strong("\\(\\text{Var}(X)\\):"), withMathJax(HTML(" Tỉ lệ người hút thuốc trong quần thể là 30% (P(X=1)=0.3). Phương sai của X là \\(\\text{Var}(X) = 0.3 \\times 0.7 = 0.21\\)"))),
|
||||
tags$li(strong("Power và \\(\\alpha\\):"), withMathJax(HTML(" Nghiên cứu được thiết kế để có 80% cơ hội phát hiện mối liên quan (power=0.8) với mức ý nghĩa 5% (\\(\\alpha=0.05\\))")))
|
||||
),
|
||||
p("Nhà nghiên cứu sẽ nhập các giá trị trên vào công cụ để ước tính cỡ mẫu cần thiết. Các giá trị này cũng đã được đặt làm mặc định trong ứng dụng để bạn dễ hình dung.")
|
||||
)
|
||||
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
# ==============================================================================
|
||||
# PHẦN 3: LOGIC MÁY CHỦ (SERVER)
|
||||
# ==============================================================================
|
||||
server <- function(input, output, session) {
|
||||
|
||||
rv <- reactiveValues()
|
||||
|
||||
observeEvent(input$go_log, {
|
||||
varx <- switch(input$x_type,
|
||||
"normal" = input$x_sd^2,
|
||||
"binary" = input$x_prob * (1 - input$x_prob),
|
||||
"uniform" = (input$x_max - input$x_min)^2 / 12)
|
||||
n_hsieh <- calc_n_logistic_hsieh(input$beta, varx, input$p_mean_event, input$power_log, input$alpha_log)
|
||||
rv$log_n_hsieh <- n_hsieh
|
||||
rv$log_varx <- varx
|
||||
|
||||
if (!is.na(n_hsieh)) {
|
||||
n_range_log <- unique(round(seq(max(30, n_hsieh * 0.5), n_hsieh * 1.5, length.out = 15)))
|
||||
xdist_log <- switch(input$x_type,
|
||||
"normal" = list(type = "normal", mean = 0, sd = input$x_sd),
|
||||
"binary" = list(type = "binary", prob = input$x_prob),
|
||||
"uniform" = list(type = "uniform", min = input$x_min, max = input$x_max))
|
||||
|
||||
power_values_log <- map_dbl(n_range_log, ~simulate_logistic_power(n = .x, beta = input$beta, x_dist = xdist_log, n_sim = 500, alpha = input$alpha_log))
|
||||
rv$log_plot_data <- tibble(SampleSize = n_range_log, Power = power_values_log)
|
||||
} else {
|
||||
rv$log_plot_data <- NULL
|
||||
}
|
||||
})
|
||||
|
||||
output$log_result_text <- renderUI({
|
||||
if (input$go_log == 0) {
|
||||
return(tags$div(class="alert alert-info", "Nhập các tham số và nhấn 'Tính toán & Phân tích' để xem kết quả."))
|
||||
}
|
||||
|
||||
req(rv$log_n_hsieh)
|
||||
n <- rv$log_n_hsieh
|
||||
|
||||
if (is.na(n)) {
|
||||
return(tags$div(class = "alert alert-danger", "Không thể tính toán. Kiểm tra lại các tham số (ví dụ: mẫu số của công thức có thể bằng 0)."))
|
||||
}
|
||||
|
||||
tagList(
|
||||
h4("Kết quả tính toán (Xấp xỉ Hsieh)"),
|
||||
p("Với Var(X) ≈", tags$b(round(rv$log_varx, 3)), ", để phát hiện một \\(\\beta\\) là", tags$b(input$beta), "với power", tags$b(input$power_log), "và \\(\\alpha\\) là", tags$b(input$alpha_log), ", cỡ mẫu ước tính là:"),
|
||||
tags$h3(style = "color: #007bff; text-align: center;", n, " quan sát"),
|
||||
hr(),
|
||||
tags$div(class = "alert alert-light",
|
||||
tags$b("Ghi chú:"), " Đây là kết quả xấp xỉ. Biểu đồ trong tab 'Đồ thị Power & Mô phỏng' được tạo ra bằng mô phỏng và có thể cho kết quả chính xác hơn.")
|
||||
)
|
||||
})
|
||||
|
||||
output$log_power_plot <- renderPlot({
|
||||
req(rv$log_plot_data, rv$log_n_hsieh)
|
||||
ggplot(rv$log_plot_data, aes(x = SampleSize, y = Power)) +
|
||||
geom_line(color = "#007bff", size = 1.2) +
|
||||
geom_point(color = "#007bff", size = 3) +
|
||||
geom_hline(yintercept = input$power_log, linetype = "dashed", color = "red") +
|
||||
geom_vline(xintercept = rv$log_n_hsieh, linetype = "dotted", color = "darkorange", size=1.2) +
|
||||
labs(title = "Power thực nghiệm (Mô phỏng) vs. Cỡ mẫu", x = "Cỡ mẫu (n)", y = "Power (1 - \\(\\beta\\))") +
|
||||
annotate("text", x = rv$log_n_hsieh * 1.05, y = 0.1, label = paste("Hsieh Approx.\nn =", rv$log_n_hsieh), color = "darkorange", hjust = 0) +
|
||||
scale_y_continuous(labels = percent, limits = c(0, 1)) +
|
||||
theme_minimal(base_size = 14)
|
||||
})
|
||||
|
||||
output$log_effect_size_ui <- renderUI({
|
||||
or <- exp(input$beta)
|
||||
interpretation <- if (or > 1) {
|
||||
paste0("một sự gia tăng ", round((or - 1) * 100, 1), "% trong \"odds\" của kết quả (event) xảy ra.")
|
||||
} else {
|
||||
paste0("một sự sụt giảm ", round((1-or) * 100, 1), "% trong \"odds\" của kết quả (event) xảy ra.")
|
||||
}
|
||||
|
||||
tagList(
|
||||
h4("Phân tích Effect Size: Tỷ số chênh (Odds Ratio - OR)"),
|
||||
p("Trong hồi quy logistic, effect size thường được biểu diễn bằng Tỷ số chênh (OR), được tính bằng công thức \\(OR = e^\\beta\\)."),
|
||||
p("Nó cho biết odds của một \"event\" (kết quả Y=1) thay đổi như thế nào khi biến độc lập X tăng lên một đơn vị."),
|
||||
hr(),
|
||||
p("Với giá trị \\(\\beta\\) bạn đã chọn là ", tags$b(input$beta), ", Tỷ số chênh tương ứng là:"),
|
||||
tags$h3(style = "color: #007bff; text-align: center;", round(or, 3)),
|
||||
tags$div(class = "alert alert-light",
|
||||
tags$b("Diễn giải:"),
|
||||
p("Khi biến X tăng lên một đơn vị, điều này tương ứng với ", tags$b(interpretation))
|
||||
)
|
||||
)
|
||||
})
|
||||
|
||||
observeEvent(input$run_sim_detail, {
|
||||
xdist_detail <- switch(input$x_type,
|
||||
"normal" = list(type = "normal", mean = 0, sd = input$x_sd),
|
||||
"binary" = list(type = "binary", prob = input$x_prob),
|
||||
"uniform" = list(type = "uniform", min = input$x_min, max = input$x_max))
|
||||
|
||||
pvals <- replicate(1000, {
|
||||
x <- switch(xdist_detail$type, "normal" = rnorm(input$n_for_sim_detail, 0, xdist_detail$sd), "binary" = rbinom(input$n_for_sim_detail, 1, xdist_detail$prob), "uniform" = runif(input$n_for_sim_detail, xdist_detail$min, xdist_detail$max))
|
||||
p <- 1 / (1 + exp(-(0 + input$beta * x)))
|
||||
y <- rbinom(input$n_for_sim_detail, 1, p)
|
||||
fit <- try(glm(y ~ x, family = binomial), silent = TRUE)
|
||||
if (!inherits(fit, "try-error") && "x" %in% rownames(summary(fit)$coefficients)) {
|
||||
summary(fit)$coefficients["x", "Pr(>|z|)"]
|
||||
} else {
|
||||
NA_real_
|
||||
}
|
||||
})
|
||||
rv$log_sim_detail_data <- tibble(p_value = pvals)
|
||||
})
|
||||
|
||||
output$log_sim_detail_plot <- renderPlot({
|
||||
req(rv$log_sim_detail_data)
|
||||
power_est <- mean(rv$log_sim_detail_data$p_value < input$alpha_log, na.rm=TRUE)
|
||||
|
||||
ggplot(rv$log_sim_detail_data, aes(x = p_value)) +
|
||||
geom_histogram(bins = 30, fill = "#28a745", color = "black", boundary=0) +
|
||||
geom_vline(xintercept = input$alpha_log, linetype = "dashed", color = "red", size = 1) +
|
||||
labs(
|
||||
title = paste0("Phân phối P-value từ mô phỏng (n=", input$n_for_sim_detail, ")"),
|
||||
subtitle = paste0("Công suất thực nghiệm ước tính: ", percent(power_est, accuracy = 0.1)),
|
||||
x = "P-value", y = "Tần suất"
|
||||
) +
|
||||
theme_minimal(base_size = 14)
|
||||
})
|
||||
}
|
||||
|
||||
# ==============================================================================
|
||||
# PHẦN 4: CHẠY ỨNG DỤNG
|
||||
# ==============================================================================
|
||||
shinyApp(ui, server)
|
File diff suppressed because one or more lines are too long
@@ -0,0 +1,578 @@
|
||||
@use "sass:math";
|
||||
.selectize-control.plugin-drag_drop.multi > .selectize-input.dragging {
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.selectize-control.plugin-drag_drop.multi > .selectize-input > div.ui-sortable-placeholder {
|
||||
visibility: visible !important;
|
||||
background: #f2f2f2 !important;
|
||||
background: rgba(0, 0, 0, 0.06) !important;
|
||||
border: 0 none !important;
|
||||
box-shadow: inset 0 0 12px 4px #fff;
|
||||
}
|
||||
|
||||
.selectize-control.plugin-drag_drop .ui-sortable-placeholder::after {
|
||||
content: "!";
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.selectize-control.plugin-drag_drop .ui-sortable-helper {
|
||||
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.selectize-dropdown.plugin-dropdown_header .selectize-dropdown-header {
|
||||
position: relative;
|
||||
padding: 6px 0.75rem;
|
||||
border-bottom: 1px solid #d0d0d0;
|
||||
background: RGBA(var(--bs-body-bg), 0.15);
|
||||
border-radius: var(--bs-border-radius) var(--bs-border-radius) 0 0;
|
||||
}
|
||||
|
||||
.selectize-dropdown.plugin-dropdown_header .selectize-dropdown-header-close {
|
||||
position: absolute;
|
||||
right: 0.75rem;
|
||||
top: 50%;
|
||||
color: RGBA(var(--bs-emphasis-color-rgb, 0, 0, 0), 0.83);
|
||||
opacity: 0.4;
|
||||
margin-top: -12px;
|
||||
line-height: 20px;
|
||||
font-size: 20px !important;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.selectize-dropdown.plugin-dropdown_header .selectize-dropdown-header-close:hover {
|
||||
color: RGB(var(--bs-emphasis-color-rgb, 0, 0, 0));
|
||||
}
|
||||
|
||||
.selectize-dropdown.plugin-optgroup_columns .selectize-dropdown-content {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.selectize-dropdown.plugin-optgroup_columns .optgroup {
|
||||
border-right: 1px solid #f2f2f2;
|
||||
border-top: 0 none;
|
||||
flex-grow: 1;
|
||||
flex-basis: 0;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.selectize-dropdown.plugin-optgroup_columns .optgroup:last-child {
|
||||
border-right: 0 none;
|
||||
}
|
||||
|
||||
.selectize-dropdown.plugin-optgroup_columns .optgroup:before {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.selectize-dropdown.plugin-optgroup_columns .optgroup-header {
|
||||
border-top: 0 none;
|
||||
}
|
||||
|
||||
.selectize-control.plugin-remove_button .item {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding-right: 0 !important;
|
||||
}
|
||||
|
||||
.selectize-control.plugin-remove_button .item .remove {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
vertical-align: middle;
|
||||
display: inline-block;
|
||||
padding: 1px 5px;
|
||||
border-left: 1px solid #dee2e6;
|
||||
border-radius: 0 2px 2px 0;
|
||||
box-sizing: border-box;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.selectize-control.plugin-remove_button .item .remove:hover {
|
||||
background: rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.selectize-control.plugin-remove_button .item.active .remove {
|
||||
border-left-color: rgba(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
.selectize-control.plugin-remove_button .disabled .item .remove:hover {
|
||||
background: none;
|
||||
}
|
||||
|
||||
.selectize-control.plugin-remove_button .disabled .item .remove {
|
||||
border-left-color: white;
|
||||
}
|
||||
|
||||
.selectize-control.plugin-clear_button .clear {
|
||||
text-decoration: none;
|
||||
display: flex;
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
width: 25px;
|
||||
top: 0;
|
||||
right: calc(0.75rem - 5px);
|
||||
color: var(--bs-body-color, black);
|
||||
opacity: 0.4;
|
||||
font-weight: bold;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
z-index: 1;
|
||||
font-size: 21px;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.selectize-control.plugin-clear_button .clear:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.selectize-control.plugin-clear_button.single .clear {
|
||||
right: calc(0.75rem - 5px + 1.5rem);
|
||||
}
|
||||
|
||||
.selectize-dropdown.plugin-auto_position.selectize-position-top {
|
||||
border-top: 1px solid #d0d0d0;
|
||||
border-bottom: 0 none;
|
||||
border-radius: 3px 3px 0 0;
|
||||
box-shadow: 0 -6px 12px rgba(var(--bs-body-color-rgb, 0, 0, 0), 0.18);
|
||||
}
|
||||
|
||||
.selectize-control.plugin-auto_position .selectize-input.selectize-position-top.dropdown-active {
|
||||
border-radius: 0 0 3px 3px;
|
||||
border-top: 0 none;
|
||||
}
|
||||
|
||||
.selectize-control.plugin-auto_position .selectize-input.selectize-position-top.dropdown-active::before {
|
||||
top: 0;
|
||||
bottom: unset;
|
||||
}
|
||||
|
||||
.selectize-control {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.selectize-dropdown,
|
||||
.selectize-input,
|
||||
.selectize-input input {
|
||||
color: RGBA(var(--bs-emphasis-color-rgb, 0, 0, 0), 0.83);
|
||||
font-family: inherit;
|
||||
font-size: 1rem;
|
||||
line-height: 1.5;
|
||||
font-smoothing: inherit;
|
||||
}
|
||||
|
||||
.selectize-input,
|
||||
.selectize-control.single .selectize-input.input-active {
|
||||
background: var(--bs-body-bg);
|
||||
cursor: text;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.selectize-input {
|
||||
border: 1px solid var(--bs-border-color);
|
||||
padding: 0.375rem 0.75rem;
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
box-sizing: border-box;
|
||||
box-shadow: none;
|
||||
border-radius: var(--bs-border-radius);
|
||||
}
|
||||
|
||||
.selectize-control.multi .selectize-input.has-items {
|
||||
padding: calc( 0.375rem - 1px - 0px) 0.75rem calc( 0.375rem - 1px - 3px - 0px);
|
||||
}
|
||||
|
||||
.selectize-input.full {
|
||||
background-color: var(--bs-body-bg);
|
||||
}
|
||||
|
||||
.selectize-input.disabled, .selectize-input.disabled * {
|
||||
cursor: default !important;
|
||||
}
|
||||
|
||||
.selectize-input.focus {
|
||||
box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.selectize-input.dropdown-active {
|
||||
border-radius: var(--bs-border-radius) var(--bs-border-radius) 0 0;
|
||||
}
|
||||
|
||||
.selectize-input > * {
|
||||
vertical-align: baseline;
|
||||
display: inline-block;
|
||||
zoom: 1;
|
||||
}
|
||||
|
||||
.selectize-control.multi .selectize-input > div {
|
||||
cursor: pointer;
|
||||
margin: 0 3px 3px 0;
|
||||
padding: 1px 5px;
|
||||
background: RGBA(var(--bs-emphasis-color-rgb, 0, 0, 0), 0.05);
|
||||
color: RGBA(var(--bs-emphasis-color-rgb, 0, 0, 0), 0.83);
|
||||
border: 0px solid #dee2e6;
|
||||
}
|
||||
|
||||
.selectize-control.multi .selectize-input > div.active {
|
||||
background: #2fa4e7;
|
||||
color: #fff;
|
||||
border: 0px solid rgba(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
.selectize-control.multi .selectize-input.disabled > div, .selectize-control.multi .selectize-input.disabled > div.active {
|
||||
color: #cdcdcd;
|
||||
background: #cdcdcd;
|
||||
border: 0px solid #cdcdcd;
|
||||
}
|
||||
|
||||
.selectize-input > input {
|
||||
display: inline-block !important;
|
||||
padding: 0 !important;
|
||||
min-height: 0 !important;
|
||||
max-height: none !important;
|
||||
max-width: 100% !important;
|
||||
margin: 0 !important;
|
||||
text-indent: 0 !important;
|
||||
border: 0 none !important;
|
||||
background: none !important;
|
||||
line-height: inherit !important;
|
||||
user-select: auto !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
.selectize-input > input::-ms-clear {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.selectize-input > input:focus {
|
||||
outline: none !important;
|
||||
}
|
||||
|
||||
.selectize-input > input[placeholder] {
|
||||
box-sizing: initial;
|
||||
}
|
||||
|
||||
.selectize-input.has-items > input {
|
||||
margin: 0 0px !important;
|
||||
}
|
||||
|
||||
.selectize-input::after {
|
||||
content: " ";
|
||||
display: block;
|
||||
clear: left;
|
||||
}
|
||||
|
||||
.selectize-input.dropdown-active::before {
|
||||
content: " ";
|
||||
display: block;
|
||||
position: absolute;
|
||||
background: rgba(var(--bs-border-color), 0.8);
|
||||
height: 1px;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.selectize-dropdown {
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
z-index: 10;
|
||||
border: 1px solid #d0d0d0;
|
||||
background: var(--bs-body-bg);
|
||||
margin: -1px 0 0 0;
|
||||
border-top: 0 none;
|
||||
box-sizing: border-box;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||
border-radius: 0 0 var(--bs-border-radius) var(--bs-border-radius);
|
||||
}
|
||||
|
||||
.selectize-dropdown [data-selectable] {
|
||||
cursor: pointer;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.selectize-dropdown [data-selectable] .highlight {
|
||||
background: rgba(255, 237, 40, 0.4);
|
||||
border-radius: 1px;
|
||||
}
|
||||
|
||||
.selectize-dropdown .option,
|
||||
.selectize-dropdown .optgroup-header,
|
||||
.selectize-dropdown .no-results,
|
||||
.selectize-dropdown .create {
|
||||
padding: 3px 0.75rem;
|
||||
}
|
||||
|
||||
.selectize-dropdown .option,
|
||||
.selectize-dropdown [data-disabled],
|
||||
.selectize-dropdown [data-disabled] [data-selectable].option {
|
||||
cursor: inherit;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.selectize-dropdown [data-selectable].option {
|
||||
opacity: 1;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.selectize-dropdown .optgroup:first-child .optgroup-header {
|
||||
border-top: 0 none;
|
||||
}
|
||||
|
||||
.selectize-dropdown .optgroup-header {
|
||||
color: #868e96;
|
||||
background: var(--bs-body-bg);
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.selectize-dropdown .active {
|
||||
background-color: #2fa4e7;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.selectize-dropdown .active.create {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.selectize-dropdown .selected {
|
||||
background-color: #2fa4e7;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.selectize-dropdown .create {
|
||||
color: RGBA(var(--bs-emphasis-color-rgb, 0, 0, 0), 0.5);
|
||||
}
|
||||
|
||||
.selectize-dropdown .active:not(.selected) {
|
||||
background: #2fa4e7;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.selectize-dropdown-content {
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
max-height: 200px;
|
||||
overflow-scrolling: touch;
|
||||
}
|
||||
|
||||
.selectize-dropdown-emptyoptionlabel {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.selectize-dropdown .spinner {
|
||||
display: inline-block;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
margin: 3px 0.75rem;
|
||||
}
|
||||
|
||||
.selectize-dropdown .spinner:after {
|
||||
content: " ";
|
||||
display: block;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
margin: 3px;
|
||||
border-radius: 50%;
|
||||
border: 5px solid #d0d0d0;
|
||||
border-color: #d0d0d0 transparent #d0d0d0 transparent;
|
||||
animation: lds-dual-ring 1.2s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes lds-dual-ring {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
.selectize-control.single .selectize-input,
|
||||
.selectize-control.single .selectize-input input {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.selectize-control.single .selectize-input.input-active, .selectize-control.single .selectize-input.input-active input:not(:read-only) {
|
||||
cursor: text;
|
||||
}
|
||||
|
||||
.selectize-control.single .selectize-input:not(.no-arrow):after {
|
||||
content: " ";
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: calc(0.75rem + 5px);
|
||||
margin-top: -3px;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-style: solid;
|
||||
border-width: 5px 5px 0 5px;
|
||||
border-color: RGBA(var(--bs-emphasis-color-rgb, 0, 0, 0), 0.83) transparent transparent transparent;
|
||||
}
|
||||
|
||||
.selectize-control.single .selectize-input:not(.no-arrow).dropdown-active:after {
|
||||
margin-top: -4px;
|
||||
border-width: 0 5px 5px 5px;
|
||||
border-color: transparent transparent RGBA(var(--bs-emphasis-color-rgb, 0, 0, 0), 0.83) transparent;
|
||||
}
|
||||
|
||||
.selectize-control.rtl {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.selectize-control.rtl.single .selectize-input:after {
|
||||
left: calc(0.75rem + 5px);
|
||||
right: auto;
|
||||
}
|
||||
|
||||
.selectize-control.rtl .selectize-input > input {
|
||||
margin: 0 4px 0 -2px !important;
|
||||
}
|
||||
|
||||
.selectize-control .selectize-input.disabled {
|
||||
opacity: 0.5;
|
||||
background-color: var(--bs-body-bg);
|
||||
}
|
||||
|
||||
.selectize-dropdown,
|
||||
.selectize-dropdown.form-control {
|
||||
height: auto;
|
||||
padding: 0;
|
||||
margin: 2px 0 0 0;
|
||||
z-index: 1000;
|
||||
background: var(--bs-body-bg);
|
||||
border: 1px solid var(--bs-border-color-translucent);
|
||||
border-radius: 0.375rem;
|
||||
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);
|
||||
}
|
||||
|
||||
.selectize-dropdown .optgroup-header {
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.selectize-dropdown .optgroup:first-child:before {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.selectize-dropdown .optgroup:before {
|
||||
content: " ";
|
||||
display: block;
|
||||
height: 0;
|
||||
margin: 0.5rem 0;
|
||||
overflow: hidden;
|
||||
border-top: 1px solid var(--bs-border-color-translucent);
|
||||
margin-left: -0.75rem;
|
||||
margin-right: -0.75rem;
|
||||
}
|
||||
|
||||
.selectize-dropdown .create {
|
||||
padding-left: 0.75rem;
|
||||
}
|
||||
|
||||
.selectize-dropdown-content {
|
||||
padding: 5px 0;
|
||||
}
|
||||
|
||||
.selectize-dropdown-emptyoptionlabel {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.selectize-input {
|
||||
min-height: calc(1.5em + 0.75rem + calc(var(--bs-border-width) * 2));
|
||||
transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.selectize-input {
|
||||
transition: none;
|
||||
}
|
||||
}
|
||||
|
||||
.selectize-input.dropdown-active {
|
||||
border-radius: var(--bs-border-radius);
|
||||
}
|
||||
|
||||
.selectize-input.dropdown-active::before {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.selectize-input.focus {
|
||||
border-color: #97d2f3;
|
||||
outline: 0;
|
||||
box-shadow: 0 0 0 0.25rem rgba(47, 164, 231, 0.25);
|
||||
}
|
||||
|
||||
.is-invalid .selectize-input {
|
||||
border-color: #c71c22;
|
||||
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
|
||||
}
|
||||
|
||||
.is-invalid .selectize-input:focus {
|
||||
border-color: #9a161a;
|
||||
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #e96065;
|
||||
}
|
||||
|
||||
.selectize-control.form-control-sm .selectize-input {
|
||||
min-height: calc(1.5em + 0.5rem + calc(var(--bs-border-width) * 2)) !important;
|
||||
height: calc(1.5em + 0.5rem + calc(var(--bs-border-width) * 2));
|
||||
padding: 0.25rem 0.5rem !important;
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.25;
|
||||
}
|
||||
|
||||
.selectize-control.multi .selectize-input {
|
||||
height: auto;
|
||||
padding-left: calc(0.75rem - 5px);
|
||||
padding-right: calc(0.75rem - 5px);
|
||||
}
|
||||
|
||||
.selectize-control.multi .selectize-input > div {
|
||||
border-radius: calc(var(--bs-border-radius) - 1px);
|
||||
}
|
||||
|
||||
.form-select.selectize-control,
|
||||
.form-control.selectize-control {
|
||||
padding: 0;
|
||||
height: auto;
|
||||
border: none;
|
||||
background: none;
|
||||
box-shadow: none;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.input-group > .input-group-append > .btn, .input-group > .form-control:not(:first-child) {
|
||||
border-top-left-radius: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
}
|
||||
|
||||
.input-group > .input-group-prepend > .btn {
|
||||
border-top-right-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
|
||||
.input-group .selectize-control:not(:last-child) .selectize-input {
|
||||
overflow: unset;
|
||||
border-top-right-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
|
||||
.input-group .selectize-control:not(:first-child) .selectize-input {
|
||||
overflow: unset;
|
||||
border-top-left-radius: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
}
|
||||
|
||||
.selectize-dropdown.plugin-auto_position.selectize-position-top {
|
||||
border-top: 1px solid var(--bs-border-color) !important;
|
||||
border-bottom: 1px solid var(--bs-border-color) !important;
|
||||
border-radius: var(--bs-border-radius) !important;
|
||||
}
|
||||
|
||||
.selectize-control.plugin-auto_position .selectize-input.selectize-position-top.dropdown-active {
|
||||
border-radius: var(--bs-border-radius) !important;
|
||||
border-top: 1px solid var(--bs-border-color) !important;
|
||||
}
|
@@ -0,0 +1,294 @@
|
||||
@charset "UTF-8";
|
||||
/* 'shiny' skin for Ion.RangeSlider, largely based on the 'big' skin, but with smaller dimensions, grayscale grid text, and without gradients
|
||||
© Posit, PBC, 2023
|
||||
© RStudio, Inc, 2014
|
||||
© Denis Ineshin, 2014 https://github.com/IonDen
|
||||
© guybowden, 2014 https://github.com/guybowden
|
||||
*/
|
||||
.irs {
|
||||
position: relative;
|
||||
display: block;
|
||||
-webkit-touch-callout: none;
|
||||
-webkit-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
/* https://github.com/rstudio/shiny/issues/3443 */
|
||||
/* https://css-tricks.com/inheriting-box-sizing-probably-slightly-better-best-practice/ */
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.irs *, .irs *:before, .irs *:after {
|
||||
box-sizing: inherit;
|
||||
}
|
||||
|
||||
.irs-line {
|
||||
position: relative;
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
outline: none !important;
|
||||
}
|
||||
|
||||
.irs-bar {
|
||||
position: absolute;
|
||||
display: block;
|
||||
left: 0;
|
||||
width: 0;
|
||||
}
|
||||
|
||||
.irs-shadow {
|
||||
position: absolute;
|
||||
display: none;
|
||||
left: 0;
|
||||
width: 0;
|
||||
}
|
||||
|
||||
.irs-handle {
|
||||
position: absolute;
|
||||
display: block;
|
||||
box-sizing: border-box;
|
||||
cursor: pointer;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.irs-handle.type_last {
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.irs-min, .irs-max {
|
||||
position: absolute;
|
||||
display: block;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.irs-min {
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.irs-max {
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.irs-from, .irs-to, .irs-single {
|
||||
position: absolute;
|
||||
display: block;
|
||||
top: 0;
|
||||
left: 0;
|
||||
cursor: default;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.irs-grid {
|
||||
position: absolute;
|
||||
display: none;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.irs-with-grid .irs-grid {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.irs-grid-pol {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 1px;
|
||||
height: 8px;
|
||||
}
|
||||
|
||||
.irs-grid-pol.small {
|
||||
height: 4px;
|
||||
}
|
||||
|
||||
.irs-grid-text {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
white-space: nowrap;
|
||||
text-align: center;
|
||||
font-size: 9px;
|
||||
line-height: 9px;
|
||||
padding: 0 3px;
|
||||
}
|
||||
|
||||
.irs-disable-mask {
|
||||
position: absolute;
|
||||
display: block;
|
||||
top: 0;
|
||||
left: -1%;
|
||||
width: 102%;
|
||||
height: 100%;
|
||||
cursor: default;
|
||||
background: rgba(0, 0, 0, 0);
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.lt-ie9 .irs-disable-mask {
|
||||
background: #000;
|
||||
filter: alpha(opacity=0);
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.irs-disabled {
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
.irs-hidden-input {
|
||||
position: absolute !important;
|
||||
display: block !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
width: 0 !important;
|
||||
height: 0 !important;
|
||||
font-size: 0 !important;
|
||||
line-height: 0 !important;
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
overflow: hidden;
|
||||
outline: none !important;
|
||||
z-index: -9999 !important;
|
||||
background: none !important;
|
||||
border-style: solid !important;
|
||||
border-color: transparent !important;
|
||||
}
|
||||
|
||||
.irs {
|
||||
font-family: var(--bs-font-sans-serif);
|
||||
}
|
||||
|
||||
.irs--shiny {
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.irs--shiny.irs-with-grid {
|
||||
height: 60px;
|
||||
}
|
||||
|
||||
.irs--shiny .irs-line {
|
||||
top: 25px;
|
||||
height: 8px;
|
||||
background: linear-gradient(to bottom, #dedede -50%, #fff 150%);
|
||||
background-color: #ededed;
|
||||
border: 1px solid #cccccc;
|
||||
border-radius: 8px;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.irs--shiny .irs-line::before {
|
||||
content: "";
|
||||
display: block;
|
||||
position: relative;
|
||||
cursor: s-resize;
|
||||
width: 100%;
|
||||
height: 22px;
|
||||
top: -9px;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.irs--shiny .irs-bar {
|
||||
top: 25px;
|
||||
height: 8px;
|
||||
border-top: 1px solid #2fa4e7;
|
||||
border-bottom: 1px solid #2fa4e7;
|
||||
background: #2fa4e7;
|
||||
cursor: s-resize;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.irs--shiny .irs-bar--single {
|
||||
border-radius: 8px 0 0 8px;
|
||||
}
|
||||
|
||||
.irs--shiny .irs-bar::before {
|
||||
content: "";
|
||||
display: block;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 22px;
|
||||
top: -9px;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.irs--shiny .irs-shadow {
|
||||
top: 38px;
|
||||
height: 2px;
|
||||
background: rgba(0, 0, 0, 0.3);
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.irs--shiny .lt-ie9 .irs-shadow {
|
||||
filter: alpha(opacity=30);
|
||||
}
|
||||
|
||||
.irs--shiny .irs-handle {
|
||||
top: 17px;
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
border: 1px solid #ababab;
|
||||
background-color: #dedede;
|
||||
box-shadow: 1px 1px 3px rgba(255, 255, 255, 0.3);
|
||||
border-radius: 22px;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.irs--shiny .irs-handle.type_last {
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
.irs--shiny .irs-handle.state_hover, .irs--shiny .irs-handle:hover {
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.irs--shiny .irs-min,
|
||||
.irs--shiny .irs-max {
|
||||
top: 0;
|
||||
padding: 1px 3px;
|
||||
text-shadow: none;
|
||||
background-color: rgba(0, 0, 0, 0.1);
|
||||
border-radius: 3px;
|
||||
font-size: 10px;
|
||||
line-height: 1.333;
|
||||
}
|
||||
|
||||
.irs--shiny .lt-ie9 .irs-min,
|
||||
.irs--shiny .lt-ie9 .irs-max {
|
||||
background: #cccccc;
|
||||
}
|
||||
|
||||
.irs--shiny .irs-from,
|
||||
.irs--shiny .irs-to,
|
||||
.irs--shiny .irs-single {
|
||||
color: #fff;
|
||||
text-shadow: none;
|
||||
padding: 1px 3px;
|
||||
background-color: #2fa4e7;
|
||||
border-radius: 3px;
|
||||
font-size: 11px;
|
||||
line-height: 1.333;
|
||||
}
|
||||
|
||||
.irs--shiny .lt-ie9 .irs-from,
|
||||
.irs--shiny .lt-ie9 .irs-to,
|
||||
.irs--shiny .lt-ie9 .irs-single {
|
||||
background: #999999;
|
||||
}
|
||||
|
||||
.irs--shiny .irs-grid {
|
||||
height: 27px;
|
||||
}
|
||||
|
||||
.irs--shiny .irs-grid-pol {
|
||||
background-color: #000;
|
||||
}
|
||||
|
||||
.irs--shiny .irs-grid-text {
|
||||
bottom: 5px;
|
||||
}
|
||||
|
||||
.irs--shiny .irs-grid-pol.small {
|
||||
background-color: #999999;
|
||||
}
|
@@ -0,0 +1,448 @@
|
||||
.shiny-panel-conditional,
|
||||
div:where(.shiny-html-output) {
|
||||
/* uiOutput()/ conditionalPanel() are "pass-through" containers when they have children. */
|
||||
}
|
||||
|
||||
.shiny-panel-conditional:has(> *),
|
||||
div:where(.shiny-html-output):has(> *) {
|
||||
display: contents;
|
||||
/* Pass along styles that no longer impact the pass-through container */
|
||||
}
|
||||
|
||||
.shiny-panel-conditional:has(> *).recalculating > *,
|
||||
div:where(.shiny-html-output):has(> *).recalculating > * {
|
||||
opacity: var(--_shiny-fade-opacity);
|
||||
}
|
||||
|
||||
/* This is necessary so that an empty verbatimTextOutput slot
|
||||
is the same height as a non-empty one (only important when
|
||||
* placeholder = TRUE) */
|
||||
pre.shiny-text-output:empty::before {
|
||||
content: " ";
|
||||
}
|
||||
|
||||
pre.shiny-text-output.noplaceholder:empty {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border-width: 0;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
/* Some browsers (like Safari) will wrap text in <pre> tags with Bootstrap's
|
||||
CSS. This changes the behavior to not wrap.
|
||||
*/
|
||||
pre.shiny-text-output {
|
||||
word-wrap: normal;
|
||||
border-radius: 0.375rem;
|
||||
}
|
||||
|
||||
.shiny-image-output img.shiny-scalable, .shiny-plot-output img.shiny-scalable {
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
}
|
||||
|
||||
#shiny-disconnected-overlay {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
background-color: RGBA(var(--bs-emphasis-color-rgb, 0, 0, 0), 0.42);
|
||||
opacity: 0.5;
|
||||
overflow: hidden;
|
||||
z-index: 99998;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
html.autoreload-enabled #shiny-disconnected-overlay.reloading {
|
||||
opacity: 0;
|
||||
animation: fadeIn 250ms forwards;
|
||||
animation-delay: 1s;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
to {
|
||||
opacity: 0.1;
|
||||
}
|
||||
}
|
||||
|
||||
.table.shiny-table > thead > tr > th, .table.shiny-table > thead > tr > td, .table.shiny-table > tbody > tr > th, .table.shiny-table > tbody > tr > td, .table.shiny-table > tfoot > tr > th, .table.shiny-table > tfoot > tr > td {
|
||||
padding-right: 12px;
|
||||
padding-left: 12px;
|
||||
}
|
||||
|
||||
.shiny-table.spacing-xs > thead > tr > th, .shiny-table.spacing-xs > thead > tr > td, .shiny-table.spacing-xs > tbody > tr > th, .shiny-table.spacing-xs > tbody > tr > td, .shiny-table.spacing-xs > tfoot > tr > th, .shiny-table.spacing-xs > tfoot > tr > td {
|
||||
padding-top: 3px;
|
||||
padding-bottom: 3px;
|
||||
}
|
||||
|
||||
.shiny-table.spacing-s > thead > tr > th, .shiny-table.spacing-s > thead > tr > td, .shiny-table.spacing-s > tbody > tr > th, .shiny-table.spacing-s > tbody > tr > td, .shiny-table.spacing-s > tfoot > tr > th, .shiny-table.spacing-s > tfoot > tr > td {
|
||||
padding-top: 5px;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
.shiny-table.spacing-m > thead > tr > th, .shiny-table.spacing-m > thead > tr > td, .shiny-table.spacing-m > tbody > tr > th, .shiny-table.spacing-m > tbody > tr > td, .shiny-table.spacing-m > tfoot > tr > th, .shiny-table.spacing-m > tfoot > tr > td {
|
||||
padding-top: 8px;
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
|
||||
.shiny-table.spacing-l > thead > tr > th, .shiny-table.spacing-l > thead > tr > td, .shiny-table.spacing-l > tbody > tr > th, .shiny-table.spacing-l > tbody > tr > td, .shiny-table.spacing-l > tfoot > tr > th, .shiny-table.spacing-l > tfoot > tr > td {
|
||||
padding-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.shiny-table .NA {
|
||||
color: RGBA(var(--bs-emphasis-color-rgb, 0, 0, 0), 0.46);
|
||||
}
|
||||
|
||||
.shiny-output-error {
|
||||
color: var(--bs-danger);
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.shiny-output-error:before {
|
||||
content: 'Error: ';
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.shiny-output-error-validation {
|
||||
color: RGBA(var(--bs-emphasis-color-rgb, 0, 0, 0), 0.5);
|
||||
}
|
||||
|
||||
.shiny-output-error-validation:before {
|
||||
content: '';
|
||||
font-weight: inherit;
|
||||
}
|
||||
|
||||
/* Work around MS Edge transition bug (issue #1637) */
|
||||
@supports (-ms-ime-align: auto) {
|
||||
.shiny-bound-output {
|
||||
transition: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.recalculating {
|
||||
--_shiny-fade-opacity: var(--shiny-fade-opacity, 0.3);
|
||||
opacity: var(--_shiny-fade-opacity);
|
||||
transition: opacity 250ms ease 500ms;
|
||||
}
|
||||
|
||||
.slider-animate-container {
|
||||
text-align: right;
|
||||
margin-top: -9px;
|
||||
}
|
||||
|
||||
.slider-animate-button {
|
||||
/* Ensure controls above slider line touch target */
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.slider-animate-button .pause {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.slider-animate-button.playing .pause {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.slider-animate-button .play {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.slider-animate-button.playing .play {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.progress.shiny-file-input-progress {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.progress.shiny-file-input-progress .progress-bar.bar-danger {
|
||||
transition: none;
|
||||
}
|
||||
|
||||
.btn-file {
|
||||
border-top-right-radius: 0 !important;
|
||||
border-bottom-right-radius: 0 !important;
|
||||
}
|
||||
|
||||
/* Make sure the filename doesn't extend past the bounds of the container */
|
||||
.shiny-input-container input[type=file] {
|
||||
overflow: hidden;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
/* Old-style progress */
|
||||
.shiny-progress-container {
|
||||
position: fixed;
|
||||
top: 0px;
|
||||
width: 100%;
|
||||
/* Make sure it draws above all Bootstrap components */
|
||||
z-index: 2000;
|
||||
}
|
||||
|
||||
.shiny-progress .progress {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
top: 0px;
|
||||
height: 3px;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
.shiny-progress .bar {
|
||||
opacity: 0.6;
|
||||
transition-duration: 250ms;
|
||||
}
|
||||
|
||||
.shiny-progress .progress-text {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
width: 240px;
|
||||
background-color: RGBA(var(--bs-primary-rgb, 47, 164, 231), 0.05);
|
||||
margin: 0px;
|
||||
padding: 2px 3px;
|
||||
opacity: 0.85;
|
||||
}
|
||||
|
||||
.shiny-progress .progress-text .progress-message {
|
||||
padding: 0px 3px;
|
||||
font-weight: bold;
|
||||
font-size: 90%;
|
||||
}
|
||||
|
||||
.shiny-progress .progress-text .progress-detail {
|
||||
padding: 0px 3px;
|
||||
font-size: 80%;
|
||||
}
|
||||
|
||||
/* New-style progress (uses notifications API) */
|
||||
.shiny-progress-notification .progress {
|
||||
margin-bottom: 5px;
|
||||
height: 10px;
|
||||
}
|
||||
|
||||
.shiny-progress-notification .progress-text .progress-message {
|
||||
font-weight: bold;
|
||||
font-size: 90%;
|
||||
}
|
||||
|
||||
.shiny-progress-notification .progress-text .progress-detail {
|
||||
font-size: 80%;
|
||||
}
|
||||
|
||||
.shiny-label-null {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.crosshair {
|
||||
cursor: crosshair;
|
||||
}
|
||||
|
||||
.grabbable {
|
||||
cursor: grab;
|
||||
cursor: -moz-grab;
|
||||
cursor: -webkit-grab;
|
||||
}
|
||||
|
||||
.grabbing {
|
||||
cursor: grabbing;
|
||||
cursor: -moz-grabbing;
|
||||
cursor: -webkit-grabbing;
|
||||
}
|
||||
|
||||
.ns-resize {
|
||||
cursor: ns-resize;
|
||||
}
|
||||
|
||||
.ew-resize {
|
||||
cursor: ew-resize;
|
||||
}
|
||||
|
||||
.nesw-resize {
|
||||
cursor: nesw-resize;
|
||||
}
|
||||
|
||||
.nwse-resize {
|
||||
cursor: nwse-resize;
|
||||
}
|
||||
|
||||
/* Workaround for Qt, which doesn't use font fallbacks */
|
||||
.qt pre, .qt code {
|
||||
font-family: monospace !important;
|
||||
}
|
||||
|
||||
/* Workaround for Qt 5, which draws its own margins around checks and radios;
|
||||
overrides the top margin on these elements set by Bootstrap */
|
||||
.qt5 .radio input[type="radio"],
|
||||
.qt5 .checkbox input[type="checkbox"] {
|
||||
margin-top: 0px;
|
||||
}
|
||||
|
||||
/* Workaround for radio buttons and checkboxes not showing on Qt on Mac.
|
||||
This occurs in the RStudio IDE on macOS 11.5.
|
||||
https://github.com/rstudio/shiny/issues/3484
|
||||
*/
|
||||
.qtmac input[type="radio"],
|
||||
.qtmac input[type="checkbox"] {
|
||||
zoom: 1.0000001;
|
||||
}
|
||||
|
||||
.shiny-frame {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.shiny-flow-layout > div {
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
padding-right: 12px;
|
||||
width: 220px;
|
||||
}
|
||||
|
||||
.shiny-split-layout {
|
||||
width: 100%;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.shiny-split-layout > div {
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
box-sizing: border-box;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.shiny-input-panel {
|
||||
padding: 6px 8px;
|
||||
margin-top: 6px;
|
||||
margin-bottom: 6px;
|
||||
background-color: RGBA(var(--bs-emphasis-color-rgb, 0, 0, 0), 0.04);
|
||||
border: 1px solid var(--bs-border-color, #dee2e6);
|
||||
border-radius: 0.375rem;
|
||||
}
|
||||
|
||||
/* For checkbox groups and radio buttons, bring the options closer to label,
|
||||
if label is present. */
|
||||
.shiny-input-checkboxgroup label ~ .shiny-options-group,
|
||||
.shiny-input-radiogroup label ~ .shiny-options-group {
|
||||
margin-top: -10px;
|
||||
}
|
||||
|
||||
/* Checkbox groups and radios that are inline need less negative margin to
|
||||
separate from label. */
|
||||
.shiny-input-checkboxgroup.shiny-input-container-inline label ~ .shiny-options-group,
|
||||
.shiny-input-radiogroup.shiny-input-container-inline label ~ .shiny-options-group {
|
||||
margin-top: -1px;
|
||||
}
|
||||
|
||||
/* Limit the width of inputs in the general case. */
|
||||
.shiny-input-container:not(.shiny-input-container-inline) {
|
||||
width: 300px;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
/* Don't limit the width of inputs in a sidebar. */
|
||||
.well .shiny-input-container {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
/* Width of non-selectize select inputs */
|
||||
.shiny-input-container > div > select:not(.selectized) {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* Styling for textAreaInput(autoresize=TRUE) */
|
||||
textarea.textarea-autoresize.form-control {
|
||||
padding: 5px 8px;
|
||||
resize: none;
|
||||
overflow-y: hidden;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
#shiny-notification-panel {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
background-color: rgba(0, 0, 0, 0);
|
||||
padding: 2px;
|
||||
width: 300px;
|
||||
max-width: 100%;
|
||||
z-index: 99999;
|
||||
}
|
||||
|
||||
.shiny-notification {
|
||||
position: relative;
|
||||
background-color: var(--bs-body-bg, #fff);
|
||||
color: var(--bs-emphasis-color, #000);
|
||||
border: 1px solid var(--bs-border-color, #dee2e6);
|
||||
border-radius: 0.375rem;
|
||||
opacity: 0.85;
|
||||
padding: 10px 2rem 10px 10px;
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
.shiny-notification-message {
|
||||
color: var(--bs-info-text-emphasis);
|
||||
background-color: var(--bs-info-bg-subtle);
|
||||
border: 1px solid var(--bs-info-border-subtle);
|
||||
}
|
||||
|
||||
.shiny-notification-warning {
|
||||
color: var(--bs-warning-text-emphasis);
|
||||
background-color: var(--bs-warning-bg-subtle);
|
||||
border: 1px solid var(--bs-warning-border-subtle);
|
||||
}
|
||||
|
||||
.shiny-notification-error {
|
||||
color: var(--bs-danger-text-emphasis);
|
||||
background-color: var(--bs-danger-bg-subtle);
|
||||
border: 1px solid var(--bs-danger-border-subtle);
|
||||
}
|
||||
|
||||
.shiny-notification-close {
|
||||
position: absolute;
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
top: 0;
|
||||
right: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-weight: normal;
|
||||
font-size: 1.125em;
|
||||
padding: 0.25rem;
|
||||
color: RGBA(var(--bs-emphasis-color-rgb, 0, 0, 0), 0.8);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.shiny-notification-close:hover {
|
||||
color: RGB(var(--bs-emphasis-color-rgb, 0, 0, 0));
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.shiny-notification-content-action a {
|
||||
color: RGB(var(--bs-primary-rgb, 47, 164, 231));
|
||||
text-decoration: underline;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.shiny-file-input-active {
|
||||
box-shadow: 0 0 0 0.25rem rgba(47, 164, 231, 0.25);
|
||||
}
|
||||
|
||||
.shiny-file-input-over {
|
||||
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(76, 174, 76, 0.6);
|
||||
}
|
||||
|
||||
/* Overrides bootstrap-datepicker3.css styling for invalid date ranges.
|
||||
See https://github.com/rstudio/shiny/issues/2042 for details. */
|
||||
.datepicker table tbody tr td.disabled,
|
||||
.datepicker table tbody tr td.disabled:hover,
|
||||
.datepicker table tbody tr td span.disabled,
|
||||
.datepicker table tbody tr td span.disabled:hover {
|
||||
color: var(--bs-tertiary-color);
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
/* Hidden tabPanels */
|
||||
.nav-hidden {
|
||||
/* override anything bootstrap sets for `.nav` */
|
||||
display: none !important;
|
||||
}
|
Reference in New Issue
Block a user