Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

R Shiny: how to use fontawesome pro version with the icon function?

I'm using fontawesome icons throughout my Shiny app via the icon function.

I've downloaded the pro version of fontawesome, and used the instructions here: shinydashboard some Font Awesome Icons Not Working to replace the free version that Shiny is using by default (inside shiny\www\shared\font-awesome) with my paying version. This works very well locally, and all the pro icons show in my app.

However, when I deploy to shinyapps.io, it seems like Shiny goes back to using the default version. I did try including my pro directory inside the /www/ folder of my app, but without success. Seems like there is no way to tell the icon() function to look at a local version of fontawesome, something like icon(...,lib=local), or icon(...,lib=path_to_fa)...

Any help would be very welcome.

like image 653
Antoine Avatar asked Mar 14 '19 13:03

Antoine


2 Answers

The simplest and most reliable way to do this is to:

  1. Put the font-awesome files in a subdirectory of the app, www/fontawesome/

  2. Add the following somewhere to the UI code:

     htmlDependency(
       name = "font-awesome", version = "99.0",
       src = "./www/fontawesome",
       stylesheet = "css/all.min.css"
     )
    

Here's an example app that illustrates:

shinyApp(
  ui = fluidPage(
    "This is a Font-Awesome Pro-only icon: ", icon("acorn"),
    htmlDependency(
      name = "font-awesome", version = "99.0",
      src = "./www/fontawesome", stylesheet = "css/all.min.css"
    )
  ),
  function(input, output, session) { }
)

One potential issue with the other method is that if the ui component contains a call to icon(), and then there is some dynamically-rendered UI (via renderUI() and uiOutput()) which contains a call to my_icon() that uses an icon that's only in Font-Awesome Pro, then that Pro icon will not show up. The method I showed here will not have that problem.

Here's why the other method will have that problem: When the static ui for an application is rendered to HTML, it looks for htmlDependency objects in the ui, and the newest version of an htmlDependency for a given name (in this case "font-awesome") "wins". So if there's just a call to icon() in the code (and no call to my_icon(), or the explicit htmlDependency() in my example), then the htmlDependency for Font-Awesome that wins is the one that comes with Shiny, 5.13.0 as of this writing. The browser will request that version of Font-Awesome.

Later, if a renderUI() inserts a my_icon() with a Pro icon, the HTML will be sent to the browser along with an htmlDependency object for Font-Awesome Pro. However, at that point, the browser already has loaded Font-Awesome, and it will not know to load this newer version of it -- Shiny currently is not able to replace the already-loaded version of Font-Awesome with a newer one.

Adding the custom htmlDependency to the static ui object makes it so it can be resolved at initial page render time, and the browser knows to load the newer version from the start. The version I used, 99.0, ensures that this custom version will "win" over any other version of a Font-Awesome htmlDependency.

like image 109
wch Avatar answered Oct 13 '22 17:10

wch


The trick was, inside the htmlDependency call (in icon() function), to remove package = "shiny" and replace "www/shared/fontawesome" by the absolute path to my FA folder (I also updated the version number).

EDIT: to be more precise, below is the (very slightly) modified icon function:

my_icon = function (name, class = NULL, lib = "font-awesome") {

prefixes <- list(`font-awesome` = "fa", glyphicon = "glyphicon")
  prefix <- prefixes[[lib]]
  if (is.null(prefix)) {
    stop("Unknown font library '", lib, "' specified. Must be one of ", 
         paste0("\"", names(prefixes), "\"", collapse = ", "))
  }
  iconClass <- ""
  if (!is.null(name)) {
    prefix_class <- prefix
    #if (prefix_class == "fa" && name %in% font_awesome_brands) {
    #  prefix_class <- "fab"
    #}
    iconClass <- paste0(prefix_class, " ", prefix, "-", name)
  }
  if (!is.null(class)) 
    iconClass <- paste(iconClass, class)
  iconTag <- tags$i(class = iconClass)
  if (lib == "font-awesome") {
    htmlDependencies(iconTag) <- htmlDependency("font-awesome", 
                                                "5.7.2", "./www/shared/fontawesome/", 
                                                stylesheet = c("css/all.min.css"))
  }
  htmltools::browsable(iconTag)
}

Besides commenting some lines out, the only part I changed is the htmlDependency call. In the original function, it was:

htmlDependency("font-awesome", "5.3.1", "www/shared/fontawesome", package = "shiny",
               stylesheet = c("css/all.min.css", "css/v4-shims.min.css")
like image 25
Antoine Avatar answered Oct 13 '22 17:10

Antoine