# ── Prepare display data ───────────────────────────────────────────────────────
tbl_data <- df_state |>
select(State, Region, NorthSouth, ExamYear, Gender,
CandiNo, TotalCandi, FemaleShare, GenderGap, GPI, YoYGrowth) |>
arrange(State, ExamYear, Gender) |>
mutate(
FemaleShare = round(FemaleShare, 3),
GPI = round(GPI, 3),
YoYGrowth = round(YoYGrowth, 2)
)
# ── Column indices that get dropdown select filters (0-based) ─────────────────
# Categorical columns: Region (1), NorthSouth (2), ExamYear (3), Gender (4)
dropdown_cols <- c(1, 2, 3, 4) # 0-based indices
# ── JavaScript: convert specified column header filters to <select> dropdowns ─
js_dropdown <- JS(
"function(settings, json) {",
" var api = this.api();",
" var dropdownCols = [1, 2, 3, 4];", # match dropdown_cols above
" dropdownCols.forEach(function(colIdx) {",
" var col = api.column(colIdx, {search: 'applied'});",
" var filterCell = $(col.header()).closest('thead').find('tr:eq(1) th').eq(colIdx);",
" var select = $('<select style=\"width:100%;padding:3px 4px;border:1px solid #ccc;border-radius:4px;font-size:12px;\"><option value=\"\">All</option></select>')",
" .appendTo(filterCell.empty())",
" .on('change', function() {",
" var val = $.fn.dataTable.util.escapeRegex($(this).val());",
" col.search(val ? '^'+val+'$' : '', true, false).draw();",
" });",
" col.data().unique().sort().each(function(d) {",
" select.append('<option value=\"'+d+'\">'+d+'</option>');",
" });",
" });",
"}"
)
datatable(
tbl_data,
caption = htmltools::tags$caption(
style = "caption-side: top; text-align: left; font-size: 14px; font-weight: 600; padding-bottom: 8px;",
"Interactive Data Explorer — 10 analytical variables | ",
htmltools::tags$span(
style = "font-weight: normal; color: #666;",
"Use dropdowns to filter by Region, Zone, Year or Gender; search box for State"
)
),
filter = "top",
extensions = "Buttons",
rownames = FALSE,
colnames = c(
"State", "Region", "Zone", "Year", "Gender",
"Candidates", "Total Candi.", "Female Share", "Gender Gap", "GPI", "YoY Growth %"
),
options = list(
pageLength = 15,
lengthMenu = list(c(10, 15, 25, 50, -1), c("10", "15", "25", "50", "All")),
dom = "Blfrtip",
buttons = list(
list(extend = "csv", text = "⬇ CSV", className = "btn btn-sm btn-outline-secondary"),
list(extend = "excel", text = "⬇ Excel", className = "btn btn-sm btn-outline-secondary")
),
scrollX = TRUE,
autoWidth = FALSE,
initComplete = js_dropdown,
columnDefs = list(
# State — wide enough for long names
list(width = "110px", targets = 0),
# Region
list(width = "110px", targets = 1),
# Zone (NorthSouth)
list(width = "75px", targets = 2),
# Year
list(width = "60px", targets = 3, className = "dt-center"),
# Gender
list(width = "75px", targets = 4, className = "dt-center"),
# Numeric columns — right-align
list(width = "90px", targets = 5, className = "dt-right"),
list(width = "90px", targets = 6, className = "dt-right"),
list(width = "90px", targets = 7, className = "dt-right"),
list(width = "90px", targets = 8, className = "dt-right"),
list(width = "65px", targets = 9, className = "dt-right"),
list(width = "90px", targets = 10, className = "dt-right")
),
language = list(
search = "Search state:",
lengthMenu = "Show _MENU_ rows",
info = "Showing _START_ to _END_ of _TOTAL_ records",
zeroRecords = "No records match the current filters"
)
)
) |>
formatRound(c("FemaleShare", "GPI"), digits = 3) |>
formatRound("YoYGrowth", digits = 2) |>
formatRound(c("GenderGap"), digits = 0) |>
formatCurrency(c("CandiNo", "TotalCandi"),
currency = "", interval = 3, mark = ",", digits = 0) |>
formatStyle(
"Gender",
backgroundColor = styleEqual(
c("Female", "Male"),
c("#f9eef4", "#eaf4fb")
)
) |>
formatStyle(
"FemaleShare",
background = styleColorBar(c(0, 1), "#A23B72", angle = -90),
backgroundSize = "98% 40%",
backgroundRepeat = "no-repeat",
backgroundPosition = "center"
) |>
formatStyle(columns = 0:10, fontSize = "13px")