├── .Rbuildignore ├── .github └── workflows │ └── R-CMD-check.yaml ├── .gitignore ├── DESCRIPTION ├── LICENSE ├── LICENSE.md ├── NAMESPACE ├── NEWS.md ├── R ├── bs-icon.R ├── bsicons-package.R └── icons.R ├── README.md ├── bsicons.Rproj ├── man ├── bs_icon.Rd └── bsicons-package.Rd ├── revdep ├── .gitignore ├── README.md ├── cran.md ├── failures.md └── problems.md ├── tests ├── testthat.R └── testthat │ ├── _snaps │ ├── bsicon.md │ └── bsicon │ │ └── main-icon-test.png │ ├── helper-skip.R │ └── test-bsicon.R └── tools └── update_icons.R /.Rbuildignore: -------------------------------------------------------------------------------- 1 | ^bsicons\.Rproj$ 2 | ^\.Rproj\.user$ 3 | ^LICENSE\.md$ 4 | ^\.gitignore$ 5 | ^\.github$ 6 | -------------------------------------------------------------------------------- /.github/workflows/R-CMD-check.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/rstudio/shiny-workflows 2 | # 3 | # NOTE: This Shiny team GHA workflow is overkill for most R packages. 4 | # For most R packages it is better to use https://github.com/r-lib/actions 5 | on: 6 | push: 7 | branches: [main, rc-**] 8 | pull_request: 9 | branches: [main] 10 | schedule: 11 | - cron: '0 7 * * 1' # every monday 12 | 13 | name: Package checks 14 | 15 | jobs: 16 | website: 17 | uses: rstudio/shiny-workflows/.github/workflows/website.yaml@v1 18 | routine: 19 | uses: rstudio/shiny-workflows/.github/workflows/routine.yaml@v1 20 | R-CMD-check: 21 | uses: rstudio/shiny-workflows/.github/workflows/R-CMD-check.yaml@v1 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .Rproj.user 2 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: bsicons 2 | Title: Easily Work with 'Bootstrap' Icons 3 | Version: 0.1.2.9000 4 | Authors@R: c( 5 | person("Carson", "Sievert", , "carson@posit.co", role = c("cre", "aut"), 6 | comment = c(ORCID = "0000-0002-4958-2844")), 7 | person("Posit Software, PBC", role = c("cph", "fnd")), 8 | person("Mark", "Otto", role = "cph", 9 | comment = "Bootstrap icons maintainer") 10 | ) 11 | Description: Easily use 'Bootstrap' icons inside 'Shiny' apps and 'R 12 | Markdown' documents. More generally, icons can be inserted in any 13 | 'htmltools' document through inline 'SVG'. 14 | License: MIT + file LICENSE 15 | URL: https://github.com/rstudio/bsicons 16 | BugReports: https://github.com/rstudio/bsicons/issues 17 | Depends: 18 | R (>= 2.10) 19 | Imports: 20 | cli, 21 | htmltools, 22 | rlang, 23 | utils 24 | Suggests: 25 | bslib, 26 | processx, 27 | testthat, 28 | webshot2, 29 | withr 30 | Config/testthat/edition: 3 31 | Encoding: UTF-8 32 | Roxygen: list(markdown = TRUE) 33 | RoxygenNote: 7.2.3 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | YEAR: 2022 2 | COPYRIGHT HOLDER: bsicons authors 3 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # MIT License 2 | 3 | Copyright (c) 2022 bsicons authors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /NAMESPACE: -------------------------------------------------------------------------------- 1 | # Generated by roxygen2: do not edit by hand 2 | 3 | export(bs_icon) 4 | import(cli) 5 | import(htmltools) 6 | import(rlang) 7 | import(utils) 8 | -------------------------------------------------------------------------------- /NEWS.md: -------------------------------------------------------------------------------- 1 | # bsicons (development version) 2 | 3 | # bsicons 0.1.2 4 | 5 | * Update icons from v1.10.2 to v1.11.1 (see [release notes](https://github.com/twbs/icons/releases)). (#9) 6 | * `bs_icon()` now defaults to `a11y="sem"` when `title` is provided, meaning that the title is shown on hover and announced by screen readers by default (#8). 7 | 8 | # bsicons 0.1.1 9 | 10 | * Improved default vertical alignment of icons in an inline context (#4). 11 | * Improved error message when an unrecognized icon is provided to `bs_icon()` (#5). 12 | 13 | # bsicons 0.1 14 | 15 | * Initial release of package. 16 | -------------------------------------------------------------------------------- /R/bs-icon.R: -------------------------------------------------------------------------------- 1 | #' Use Bootstrap icons (as inline SVG) 2 | #' 3 | #' @param name The name of the Bootstrap icon. Whitespace is replaced with `-` 4 | #' (that way, `"arrow up"` can be used to refer to the "actual name" of 5 | #' `"arrow-up"`). For a searchable list of names, see 6 | #' @param size Any valid CSS unit defining both the height and width of the 7 | #' icon. 8 | #' @param class Additional CSS classes to add to the `` element. Consider 9 | #' providing Bootstrap 5+ utility classes (e.g., `text-success`) here to 10 | #' stylize the icon (but also note that those utility classes will only work 11 | #' when Bootstrap 5+ is on the page). 12 | #' @param title If provided (highly recommended), `a11y` defaults to `"sem"`, 13 | #' meaning the title is used for on-hover text and screen reader 14 | #' announcements. 15 | #' @param a11y Cases that distinguish the role of the icon and inform which 16 | #' accessibility attributes to be used. Icons can either be `"deco"` 17 | #' (decorative, the default case), `"sem"` (semantic), `"none"` (no 18 | #' accessibility features). The default, `"auto"`, resolves to `"sem"` if a 19 | #' `title` is provided (and `"deco"` otherwise). 20 | #' @param ... additional CSS properties (e.g., `margin`, `position`, etc.) 21 | #' placed on the `` tag. 22 | #' 23 | #' @return An [htmltools::HTML()] string containing the SVG icon. 24 | #' 25 | #' @examples 26 | #' 27 | #' up <- bs_icon("arrow-up-circle", size = "9em", class = "text-success") 28 | #' up_fill <- bs_icon("arrow-up-circle-fill", size = "9em", class = "text-success") 29 | #' 30 | #' # utility class will only apply with a modern version of Bootstrap 31 | #' if (interactive() && requireNamespace('bslib')) { 32 | #' bslib::page_fluid(up, up_fill) 33 | #' } 34 | #' 35 | #' @import htmltools 36 | #' @export 37 | bs_icon <- function( 38 | name, 39 | size = "1em", 40 | class = NULL, 41 | title = NULL, 42 | a11y = c("auto", "deco", "sem", "none"), 43 | ... 44 | ) { 45 | 46 | if (length(name) != 1) { 47 | rlang::abort("The number of icons specified in `name` must be 1.") 48 | } 49 | 50 | name <- sub("\\s+", "-", tolower(name)) 51 | idx <- match(name, tolower(icon_info$name)) 52 | if (is.na(idx)) { 53 | dists <- utils::adist(name, icon_info$name) 54 | suggestions <- icon_info$name[order(dists)][1:5] 55 | cli::cli_abort(c( 56 | "This Bootstrap icon {.val {name}} does not exist.", 57 | "i" = "Did you mean one of the following: {.or {.val {suggestions}}}?", 58 | "i" = "Try searching for the icon: {.url https://icons.getbootstrap.com}." 59 | )) 60 | } 61 | 62 | svg_children <- icon_info$contents[idx] 63 | 64 | size <- validateCssUnit(size) 65 | style_attr <- paste0( 66 | "height:", size, ";", 67 | "width:", size, ";", 68 | "fill:currentColor;", 69 | # Better default vertical positioning of icons in a inline context (inspired by fontawesome::fa()) 70 | "vertical-align:-0.125em;", 71 | htmltools::css(...) 72 | ) 73 | 74 | # Generate accessibility attributes if either of 75 | # the "deco" or "sem" cases are chosen 76 | a11y <- rlang::arg_match(a11y) 77 | a11y_attrs <- "" 78 | 79 | if (a11y == "auto") { 80 | a11y <- if (is.null(title)) "deco" else "sem" 81 | } 82 | 83 | if (a11y == "deco") { 84 | a11y_attrs <- 'aria-hidden="true" role="img" ' 85 | } else if (a11y == "sem") { 86 | title <- title %||% name 87 | a11y_attrs <- sprintf( 88 | 'aria-label="%s" role="img" ', 89 | htmlEscape(title, attribute = TRUE) 90 | ) 91 | } 92 | 93 | res <- sprintf( 94 | '%s%s', 95 | name, 96 | paste(class, collapse = " "), 97 | style_attr, 98 | a11y_attrs, 99 | if (is.null(title)) "" else paste0("", htmlEscape(title), ""), 100 | svg_children 101 | ) 102 | 103 | browsable(HTML(res)) 104 | } 105 | 106 | "%||%" <- function(x, y) { 107 | if (is.null(x)) y else x 108 | } 109 | -------------------------------------------------------------------------------- /R/bsicons-package.R: -------------------------------------------------------------------------------- 1 | #' @keywords internal 2 | "_PACKAGE" 3 | 4 | ## usethis namespace: start 5 | #' @import rlang 6 | #' @import cli 7 | #' @import utils 8 | ## usethis namespace: end 9 | NULL 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # bsicons 3 | 4 | 5 | 6 | [![CRAN 7 | status](https://www.r-pkg.org/badges/version/bsicons)](https://cran.r-project.org/package=bsicons) 8 | [![Lifecycle: 9 | experimental](https://img.shields.io/badge/lifecycle-experimental-orange.svg)](https://lifecycle.r-lib.org/articles/stages.html) 10 | [![R build 11 | status](https://github.com/rstudio/bsicons/actions/workflows/R-CMD-check.yaml/badge.svg)](https://github.com/rstudio/bsicons/actions) 12 | 13 | 14 | 15 | An R package for using [Bootstrap icons](https://icons.getbootstrap.com/) in Shiny and R Markdown 16 | 17 | ## Installation 18 | 19 | Install the stable release of `bsicons` on CRAN: 20 | 21 | ```r 22 | install.packages("bsicons") 23 | ``` 24 | 25 | ## Example 26 | 27 | ``` r 28 | bsicons::bs_icon("geo", size = "3rem") 29 | ``` 30 | 31 | -------------------------------------------------------------------------------- /bsicons.Rproj: -------------------------------------------------------------------------------- 1 | Version: 1.0 2 | 3 | RestoreWorkspace: No 4 | SaveWorkspace: No 5 | AlwaysSaveHistory: Default 6 | 7 | EnableCodeIndexing: Yes 8 | UseSpacesForTab: Yes 9 | NumSpacesForTab: 2 10 | Encoding: UTF-8 11 | 12 | RnwWeave: Sweave 13 | LaTeX: pdfLaTeX 14 | 15 | AutoAppendNewline: Yes 16 | StripTrailingWhitespace: Yes 17 | LineEndingConversion: Posix 18 | 19 | BuildType: Package 20 | PackageUseDevtools: Yes 21 | PackageInstallArgs: --no-multiarch --with-keep.source 22 | PackageRoxygenize: rd,collate,namespace 23 | -------------------------------------------------------------------------------- /man/bs_icon.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/bs-icon.R 3 | \name{bs_icon} 4 | \alias{bs_icon} 5 | \title{Use Bootstrap icons (as inline SVG)} 6 | \usage{ 7 | bs_icon( 8 | name, 9 | size = "1em", 10 | class = NULL, 11 | title = NULL, 12 | a11y = c("auto", "deco", "sem", "none"), 13 | ... 14 | ) 15 | } 16 | \arguments{ 17 | \item{name}{The name of the Bootstrap icon. Whitespace is replaced with \code{-} 18 | (that way, \code{"arrow up"} can be used to refer to the "actual name" of 19 | \code{"arrow-up"}). For a searchable list of names, see \url{https://icons.getbootstrap.com/}} 20 | 21 | \item{size}{Any valid CSS unit defining both the height and width of the 22 | icon.} 23 | 24 | \item{class}{Additional CSS classes to add to the \verb{} element. Consider 25 | providing Bootstrap 5+ utility classes (e.g., \code{text-success}) here to 26 | stylize the icon (but also note that those utility classes will only work 27 | when Bootstrap 5+ is on the page).} 28 | 29 | \item{title}{If provided (highly recommended), \code{a11y} defaults to \code{"sem"}, 30 | meaning the title is used for on-hover text and screen reader 31 | announcements.} 32 | 33 | \item{a11y}{Cases that distinguish the role of the icon and inform which 34 | accessibility attributes to be used. Icons can either be \code{"deco"} 35 | (decorative, the default case), \code{"sem"} (semantic), \code{"none"} (no 36 | accessibility features). The default, \code{"auto"}, resolves to \code{"sem"} if a 37 | \code{title} is provided (and \code{"deco"} otherwise).} 38 | 39 | \item{...}{additional CSS properties (e.g., \code{margin}, \code{position}, etc.) 40 | placed on the \verb{} tag.} 41 | } 42 | \value{ 43 | An \code{\link[htmltools:HTML]{htmltools::HTML()}} string containing the SVG icon. 44 | } 45 | \description{ 46 | Use Bootstrap icons (as inline SVG) 47 | } 48 | \examples{ 49 | 50 | up <- bs_icon("arrow-up-circle", size = "9em", class = "text-success") 51 | up_fill <- bs_icon("arrow-up-circle-fill", size = "9em", class = "text-success") 52 | 53 | # utility class will only apply with a modern version of Bootstrap 54 | if (interactive() && requireNamespace('bslib')) { 55 | bslib::page_fluid(up, up_fill) 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /man/bsicons-package.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/bsicons-package.R 3 | \docType{package} 4 | \name{bsicons-package} 5 | \alias{bsicons} 6 | \alias{bsicons-package} 7 | \title{bsicons: Easily Work with 'Bootstrap' Icons} 8 | \description{ 9 | Easily use 'Bootstrap' icons inside 'Shiny' apps and 'R Markdown' documents. More generally, icons can be inserted in any 'htmltools' document through inline 'SVG'. 10 | } 11 | \seealso{ 12 | Useful links: 13 | \itemize{ 14 | \item \url{https://github.com/rstudio/bsicons} 15 | \item Report bugs at \url{https://github.com/rstudio/bsicons/issues} 16 | } 17 | 18 | } 19 | \author{ 20 | \strong{Maintainer}: Carson Sievert \email{carson@posit.co} (\href{https://orcid.org/0000-0002-4958-2844}{ORCID}) 21 | 22 | Other contributors: 23 | \itemize{ 24 | \item Posit Software, PBC [copyright holder, funder] 25 | \item Mark Otto (Bootstrap icons maintainer) [copyright holder] 26 | } 27 | 28 | } 29 | \keyword{internal} 30 | -------------------------------------------------------------------------------- /revdep/.gitignore: -------------------------------------------------------------------------------- 1 | checks 2 | library 3 | checks.noindex* 4 | library.noindex* 5 | cloud.noindex* 6 | data.sqlite 7 | *.html 8 | -------------------------------------------------------------------------------- /revdep/README.md: -------------------------------------------------------------------------------- 1 | # Revdeps 2 | 3 | -------------------------------------------------------------------------------- /revdep/cran.md: -------------------------------------------------------------------------------- 1 | ## revdepcheck results 2 | 3 | We checked 2 reverse dependencies, comparing R CMD check results across CRAN and dev versions of this package. 4 | 5 | * We saw 0 new problems 6 | * We failed to check 0 packages 7 | 8 | -------------------------------------------------------------------------------- /revdep/failures.md: -------------------------------------------------------------------------------- 1 | *Wow, no problems at all. :)* -------------------------------------------------------------------------------- /revdep/problems.md: -------------------------------------------------------------------------------- 1 | *Wow, no problems at all. :)* -------------------------------------------------------------------------------- /tests/testthat.R: -------------------------------------------------------------------------------- 1 | library(testthat) 2 | library(bsicons) 3 | 4 | test_check("bsicons") 5 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/bsicon.md: -------------------------------------------------------------------------------- 1 | # bs_icon() returns a SVG string and renders as HTML 2 | 3 | Code 4 | as.character(globe) 5 | Output 6 | [1] "" 7 | 8 | --- 9 | 10 | Code 11 | as.character(rocket) 12 | Output 13 | [1] "A rocket ship\n\n" 14 | 15 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/bsicon/main-icon-test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/bsicons/f7dca31f5715a3c66a323aa6897f80846dacebc1/tests/testthat/_snaps/bsicon/main-icon-test.png -------------------------------------------------------------------------------- /tests/testthat/helper-skip.R: -------------------------------------------------------------------------------- 1 | skip_if_not_on_gha_mac <- function() { 2 | skip_on_cran() 3 | skip_if_not(on_ci(), "Not on a CI service (i.e., GHA)") 4 | skip_on_os("windows") 5 | skip_on_os("linux") 6 | } 7 | 8 | 9 | # a la testthat:::on_ci 10 | on_ci <- function() { 11 | isTRUE(as.logical(Sys.getenv("CI"))) 12 | } 13 | -------------------------------------------------------------------------------- /tests/testthat/test-bsicon.R: -------------------------------------------------------------------------------- 1 | library(bsicons) 2 | library(bslib) 3 | library(htmltools) 4 | 5 | bs_deps <- bs_theme_dependencies(bs_theme()) 6 | 7 | # @param name a name for the snapshot 8 | # @param ... a collection of UI elements 9 | expect_snapshot_html <- function(name, ...) { 10 | # Only do browser based snapshotting on GHA mac 11 | skip_if_not_on_gha_mac() 12 | 13 | # I would love to use servr::httd() to serve up the file, but for some reason 14 | # that isn't working with webshot, so use python+processx instead 15 | skip_if( 16 | Sys.which("python3") == "", 17 | "python3 is required to do browser based testing" 18 | ) 19 | 20 | withr::with_tempdir({ 21 | py <- processx::process$new("python3", c("-m", "http.server", "4000")) 22 | 23 | html <- div(id = "main_content", bs_deps, ...) 24 | save_html(html, "index.html") 25 | 26 | png <- webshot2::webshot("http://localhost:4000", selector = "#main_content") 27 | expect_snapshot_file(png, name = name) 28 | }) 29 | } 30 | 31 | test_that("bs_icon() returns a SVG string and renders as HTML", { 32 | 33 | globe <- bs_icon("globe", size = "5rem") 34 | expect_snapshot(as.character(globe), cran = TRUE) 35 | 36 | rocket <- bs_icon( 37 | "rocket", size = "7rem", 38 | class = "text-success mt-1", 39 | title = "A rocket ship", 40 | border = "2px solid red" 41 | ) 42 | expect_snapshot(as.character(rocket), cran = TRUE) 43 | 44 | 45 | expect_snapshot_html( 46 | name = "main-icon-test.png", 47 | globe, rocket, 48 | bs_icon("0-circle-fill", size = "5rem", class = "text-primary") 49 | ) 50 | }) 51 | -------------------------------------------------------------------------------- /tools/update_icons.R: -------------------------------------------------------------------------------- 1 | library(rvest) 2 | 3 | # https://github.com/twbs/icons/releases 4 | version <- "1.11.1" 5 | 6 | pkg_home <- rprojroot::find_package_root_file() 7 | pkgload::load_all(pkg_home) 8 | 9 | if (!exists("icon_info")) { 10 | stop("Loading bsicons should introduce a icon_info object") 11 | } 12 | 13 | new_icon_info <- NULL 14 | 15 | withr::with_tempdir({ 16 | download.file( 17 | sprintf("https://github.com/twbs/icons/archive/refs/tags/v%s.zip", version), 18 | "icons.zip" 19 | ) 20 | unzip("icons.zip") 21 | setwd(paste0("icons-", version)) 22 | 23 | icon_files <- dir("icons", full.names = TRUE) 24 | icon_html <- lapply(icon_files, function(x) { 25 | read_html(paste(readLines(x), collapse = "\n")) 26 | }) 27 | 28 | # Make sure there is one svg tag per file 29 | svgs <- lapply(icon_html, html_elements, "svg") 30 | stopifnot(unique(lengths(svgs)) == 1) 31 | 32 | # ------------------------------------------------------- 33 | # Appears as though all icons are designed to be 1:1 34 | # aspect ratio and used with fill=currentColor, so we don't 35 | # need that information. 36 | # ------------------------------------------------------- 37 | widths <- vapply(svgs, html_attr, character(1), "width") 38 | heights <- vapply(svgs, html_attr, character(1), "height") 39 | fills <- vapply(svgs, html_attr, character(1), "fill") 40 | vbs <- vapply(svgs, html_attr, character(1), "viewbox") 41 | stopifnot( 42 | unique(widths) == "16", 43 | unique(heights) == "16", 44 | unique(fills) == "currentColor", 45 | unique(vbs) == "0 0 16 16" 46 | ) 47 | 48 | # ------------------------------------------------------- 49 | # All the classes are just "bi bi-name" 50 | # ------------------------------------------------------- 51 | classes <- vapply(svgs, html_attr, character(1), "class") 52 | classes <- strsplit(classes, "\\s+") 53 | icon_names <- sub("^bi-", "", vapply(classes, "[[", character(1), 2)) 54 | 55 | stopifnot( 56 | unique(lengths(classes)) == 2, 57 | unique(vapply(classes, "[[", character(1), 1)) == "bi", 58 | all(icon_names == tools::file_path_sans_ext(basename(icon_files))) 59 | ) 60 | 61 | # -------------------------------------------------------- 62 | # Make sure there's no other svg attributes that we're missing 63 | # -------------------------------------------------------- 64 | missing_attrs <- lapply(svgs, function(x) { 65 | setdiff( 66 | names(html_attrs(x)[[1]]), 67 | c("width", "height", "fill", "viewbox", "class", "xmlns") 68 | ) 69 | }) 70 | stopifnot(sum(lengths(missing_attrs)) == 0) 71 | 72 | # N.B. some icons (e.g., align-bottom) have elements other 73 | # than just s (e.g., ) 74 | contents <- lapply(svgs, html_elements, "*") 75 | 76 | new_icon_info <<- list( 77 | name = icon_names, 78 | contents = vapply(contents, function(x) paste(as.character(x), collapse = "\n"), character(1)) 79 | ) 80 | }) 81 | 82 | 83 | if (is.null(new_icon_info)) { 84 | stop("Failed to get new icons") 85 | } 86 | 87 | missing_icons <- setdiff(icon_info$name, new_icon_info$name) 88 | if (length(missing_icons)) { 89 | stop( 90 | "New version of Bootstrap icons dropped the following icons: '", 91 | paste(missing_icons, collapse = "', '"), 92 | "'. Consider keeping them for backwards-compatibility.") 93 | } 94 | 95 | 96 | cat( 97 | "# Generated by tools/update_icons.R: do not edit by hand\n\n", 98 | "icon_info <- ", 99 | paste(capture.output(dput(new_icon_info)), collapse = ""), 100 | sep = "", 101 | file = file.path(pkg_home, "R", "icons.R") 102 | ) 103 | --------------------------------------------------------------------------------