This page is specific to R. RichTreeView() expects its
items as a nested list, as described in the official Rich
Tree View - Items docs. As R users mostly keep their data in data
frames and tibbles, asMuiTree() builds that nested list
from a data frame.
From columns to levels
Each column given in levels becomes a level of the tree,
in the given order. By default, all the columns are used. Here, a
selection of the functions of the muiMaterial,
muiCharts
and reactRouter
packages:
fns <- rbind(
data.frame(package = "muiMaterial", group = "Layout", fn = c("Box", "Stack", "Grid")),
data.frame(package = "muiMaterial", group = "Inputs", fn = c("Button", "TextField", "Slider", "Switch")),
data.frame(package = "muiMaterial", group = "Navigation", fn = c("Link", "Tabs", "Drawer")),
data.frame(package = "muiMaterial", group = "Surfaces", fn = c("AppBar", "Toolbar", "Card")),
data.frame(package = "muiCharts", group = "Charts", fn = c("BarChart", "LineChart", "PieChart", "Gauge")),
data.frame(package = "muiCharts", group = "Building blocks", fn = c("ChartsContainer", "ChartsXAxis", "ChartsLegend", "Toolbar")),
data.frame(package = "reactRouter", group = "Routers", fn = c("RouterProvider", "createHashRouter", "createMemoryRouter")),
data.frame(package = "reactRouter", group = "Routes", fn = c("Route", "Outlet")),
data.frame(package = "reactRouter", group = "Navigation", fn = c("Link", "NavLink", "Navigate")),
data.frame(package = "reactRouter", group = "Hooks", fn = c("useParams", "useLoaderData", "useNavigate"))
)
head(fns)
#> package group fn
#> 1 muiMaterial Layout Box
#> 2 muiMaterial Layout Stack
#> 3 muiMaterial Layout Grid
#> 4 muiMaterial Inputs Button
#> 5 muiMaterial Inputs TextField
#> 6 muiMaterial Inputs Slider
RichTreeView(
items = asMuiTree(fns)
)Change the order or drop a column to look at the data from another angle. Here, the topics come first, so the “Navigation” functions of muiMaterial and reactRouter are grouped together:
RichTreeView(
items = asMuiTree(fns, levels = c("group", "package", "fn"))
)The item structure
Each item is a list with an id, a label
and, except at the last level, children:
items <- asMuiTree(fns, levels = c("package", "fn"))
str(items[[1]], max.level = 2, list.len = 3)
#> List of 3
#> $ label : chr "muiMaterial"
#> $ id : chr "muiMaterial-6972885"
#> $ children:List of 13
#> ..$ :List of 2
#> ..$ :List of 2
#> ..$ :List of 2
#> .. [list output truncated]This is the structure described in the MUI docs: item identifier, item label and item children.
Stable ids with levels_id
By default, asMuiTree() creates ids by joining the label
with a random number (e.g. "muiCharts-4821937"), so they
change on each run. Stable ids are needed to expand or select items by
default, and to read the selection in Shiny. Give one id column per
level with levels_id:
fns$fn_id <- paste0(fns$package, "::", fns$fn)
RichTreeView(
items = asMuiTree(
fns,
levels = c("package", "fn"),
levels_id = c("package", "fn_id")
),
defaultExpandedItems = list("muiCharts"),
defaultSelectedItems = "muiCharts::BarChart"
)Ids must not contain spaces. The package::fn ids read
like R code, and are unique even though Link and
Toolbar exist in two packages.
Repeated labels: use the path as id
Ids must be unique in the whole tree, but a label can appear under several parents. Here, the group “Navigation” exists in muiMaterial and in reactRouter. Build the ids from the path of the item:
# ids must not contain spaces
to_id <- function(x) gsub("[^a-z0-9]+", "-", tolower(x))
fns$group_id <- paste(fns$package, to_id(fns$group), sep = "-")
unique(fns$group_id)
#> [1] "muiMaterial-layout" "muiMaterial-inputs"
#> [3] "muiMaterial-navigation" "muiMaterial-surfaces"
#> [5] "muiCharts-charts" "muiCharts-building-blocks"
#> [7] "reactRouter-routers" "reactRouter-routes"
#> [9] "reactRouter-navigation" "reactRouter-hooks"
RichTreeView(
items = asMuiTree(
fns,
levels = c("package", "group", "fn"),
levels_id = c("package", "group_id", "fn_id")
),
defaultExpandedItems = list("muiMaterial", "muiMaterial-navigation", "reactRouter", "reactRouter-navigation")
)Labels with counts
A label is a string, so summaries can be added to it in R before
calling asMuiTree(). Here the number of functions is added
to each package and group:
count <- function(...) ave(seq_along(fns$fn), ..., FUN = length)
fns$package_label <- paste0(fns$package, " (", count(fns$package), ")")
fns$group_label <- paste0(fns$group, " (", count(fns$package, fns$group), ")")
RichTreeView(
items = asMuiTree(
fns,
levels = c("package_label", "group_label", "fn"),
levels_id = c("package", "group_id", "fn_id")
),
defaultExpandedItems = list("reactRouter")
)To render the count in its own element instead, see the custom item in Rich Tree View - Customization.
Extra fields per level
Extra arguments named after a level column add fields to every item
of that level. The tree view ignores unknown fields, but you can read
them in functions such as isItemDisabled:
RichTreeView(
items = asMuiTree(
fns,
levels = c("package", "group"),
levels_id = c("package", "group_id"),
package = list(disabled = TRUE)
),
isItemDisabled = JS("(item) => !!item.disabled"),
disabledItemsFocusable = TRUE,
defaultExpandedItems = list("muiCharts")
)With disabledItemsFocusable = TRUE, the disabled
packages can still be expanded with the keyboard. See disabled
items in the MUI docs.
Missing values
Rows with a missing value (NA) at a level are dropped
from that level and below. Here, the group of
SparkLineChart is missing, so it is not in the tree:
charts <- data.frame(
group = c("Charts", "Charts", "Charts", NA),
fn = c("BarChart", "LineChart", "PieChart", "SparkLineChart")
)
RichTreeView(
items = asMuiTree(charts, levels_id = c("group", "fn")),
defaultExpandedItems = list("Charts")
)Replace the missing values first to keep them:
charts$group[is.na(charts$group)] <- "Other"
RichTreeView(
items = asMuiTree(charts, levels_id = c("group", "fn")),
defaultExpandedItems = list("Charts", "Other")
)Tibbles
asMuiTree() converts its input with
as.data.frame(), so tibbles work too:
library(dplyr)
hooks_and_charts <- as_tibble(fns) |>
filter(startsWith(fn, "use") | endsWith(fn, "Chart")) |>
select(package, fn) |>
arrange(package, fn)
hooks_and_charts
#> # A tibble: 6 × 2
#> package fn
#> <chr> <chr>
#> 1 muiCharts BarChart
#> 2 muiCharts LineChart
#> 3 muiCharts PieChart
#> 4 reactRouter useLoaderData
#> 5 reactRouter useNavigate
#> 6 reactRouter useParams
RichTreeView(
items = asMuiTree(hooks_and_charts)
)Thanks
asMuiTree() is adapted from shinyWidgets::create_tree().
Thanks to the authors of shinyWidgets for their work!