diff --git a/DESCRIPTION b/DESCRIPTION index 7d661433..8d4b48b5 100755 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -15,6 +15,7 @@ Authors@R: c(person("Filip", "Stachura", email = "filip@appsilon.com", role = "a person("Pedro", "Manuel Coutinho da Silva", email = "pedro@appsilon.com", role = "ctb"), person("Paweł", "Przytuła", email = "pawel@appsilon.com", role = "ctb"), person("Kamil", "Żyła", email = "kamil@appsilon.com", role = "ctb"), + person("Oriol", "Senan", email = "oriol@appsilon.com", role = "ctb"), person(family = "Appsilon Sp. z o.o.", role = c("cph"))) Description: Creating a great user interface for your Shiny apps can be a hassle, especially if you want to work purely in R @@ -46,5 +47,6 @@ Suggests: DT, covr, leaflet, - plotly + plotly, + xml2 RoxygenNote: 7.1.1 diff --git a/NAMESPACE b/NAMESPACE index 4e66d2ff..cc461954 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -7,6 +7,7 @@ export(SUPPORTED_THEMES) export(accordion) export(actionButton) export(action_button) +export(add_svg_sprite) export(button) export(calendar) export(card) @@ -58,6 +59,7 @@ export(removeModal) export(removeNotification) export(remove_all_modals) export(remove_modal) +export(remove_svg_sprite) export(render_menu_link) export(search_field) export(search_selection_api) diff --git a/R/sprite.R b/R/sprite.R new file mode 100644 index 00000000..37f409e4 --- /dev/null +++ b/R/sprite.R @@ -0,0 +1,93 @@ +id_exists <- function(id, sprite) { + sprite_symbols <- xml2::xml_text(xml2::xml_find_all( + sprite, "/*[name()='svg']/*[name()='symbol']/@id")) + id %in% sprite_symbols +} + +#' Add svg to a sprite map +#' +#' @param newsvg \code{xml_document} object with svg that will be added +#' @param sprite \code{xml_document} object with the sprite map +#' @param id sets the id of the new svg inside the sprite map, if is +#' NULL it looks for the id inside the \code{newsvg} object. +#' +#' @return invisible copy of new sprite map +#' +#' @details the sprite map object is modified in place +#' +#' @examples +#' library(xml2) +#' sprite <- xml_new_root("svg", "version" = "1.1", "xmlns" = "http://www.w3.org/2000/svg") +#' newsvg <- read_xml(" +#' +#' ") +#' add_svg_sprite(newsvg, sprite, "new-icon") +#' +#' sprite <- xml_new_root("svg", "version" = "1.1", "xmlns" = "http://www.w3.org/2000/svg") +#' appsilon_logo <- read_xml(system.file("assets/icons/appsilon-logo.svg", package = "shiny.semantic")) +#' shiny_logo <- read_xml(system.file("assets/icons/shiny-logo.svg", package = "shiny.semantic")) +#' add_svg_sprite(appsilon_logo, sprite, "appsilon_logo") +#' add_svg_sprite(shiny_logo, sprite, "shiny_logo") +#' sprite_file <- tempfile() +#' write_xml(sprite, sprite_file, options = "no_declaration") +#' @export +add_svg_sprite <- function(newsvg, sprite, id = NULL) { + if (!requireNamespace("xml2", quietly = TRUE)) { + stop("Package \"xml2\" needed for this function to work. + Please install it", call. = FALSE) + } + if (is.null(id)) { + id <- xml2::xml_attr(newsvg, "id") + if (is.na(id)) stop("id is NULL and newsvg does not have id") + } + stopifnot(!id_exists(id, sprite)) + viewBox <- xml2::xml_attr(newsvg, "viewBox") + root_newsvg <- xml2::xml_new_root( + "symbol", "id" = id, "viewBox" = viewBox) + xml2::xml_add_child(root_newsvg, xml2::xml_child(newsvg)) + xml2::xml_add_child( + sprite, + xml2::xml_find_first(root_newsvg, "/*[name()='symbol']")) + invisible(sprite) +} + + +#' Remove svg from a sprite map +#' +#' @param id symbol id of the svg to be removed +#' @param sprite \code{xml_document} object with the sprite map +#' +#' @return invisible copy of new sprite map +#' +#' @details sprite map is modifyed on place +#' +#' @examples +#' library(xml2) +#' sprite <- xml_new_root("svg", "version" = "1.1", "xmlns" = "http://www.w3.org/2000/svg") +#' newsvg <- read_xml(" +#' +#' ") +#' add_svg_sprite(newsvg, sprite, "new-icon") +#' remove_svg_sprite("new-icon", sprite) +#' +#' #' sprite <- xml_new_root("svg", "version" = "1.1", "xmlns" = "http://www.w3.org/2000/svg") +#' appsilon_logo <- read_xml(system.file("assets/icons/appsilon-logo.svg", package = "shiny.semantic")) +#' shiny_logo <- read_xml(system.file("assets/icons/shiny-logo.svg", package = "shiny.semantic")) +#' add_svg_sprite(appsilon_logo, sprite, "appsilon_logo") +#' add_svg_sprite(shiny_logo, sprite, "shiny_logo") +#' remove_svg_sprite("shiny_logo", sprite) +#' sprite_file <- tempfile() +#' write_xml(sprite, sprite_file, options = "no_declaration") +#' @export + remove_svg_sprite <- function(id, sprite) { + if (!requireNamespace("xml2", quietly = TRUE)) { + stop("Package \"xml2\" needed for this function to work. + Please install it", call. = FALSE) + } + if (!id_exists(id, sprite)) stop(glue::glue("id {id} not found in sprite")) + symbol2remove <- xml2::xml_find_first( + sprite, + paste0("/*[name()='svg']/*[name()='symbol'][@id='",id,"']")) + xml2::xml_remove(symbol2remove) + invisible(sprite) +} diff --git a/inst/assets/icons/appsilon-logo.svg b/inst/assets/icons/appsilon-logo.svg new file mode 100644 index 00000000..22e7cb60 --- /dev/null +++ b/inst/assets/icons/appsilon-logo.svg @@ -0,0 +1,7 @@ + diff --git a/inst/assets/icons/shiny-logo.svg b/inst/assets/icons/shiny-logo.svg new file mode 100644 index 00000000..79df6fa6 --- /dev/null +++ b/inst/assets/icons/shiny-logo.svg @@ -0,0 +1,3 @@ + diff --git a/man/add_svg_sprite.Rd b/man/add_svg_sprite.Rd new file mode 100644 index 00000000..686db0ea --- /dev/null +++ b/man/add_svg_sprite.Rd @@ -0,0 +1,41 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/sprite.R +\name{add_svg_sprite} +\alias{add_svg_sprite} +\title{Add svg to a sprite map} +\usage{ +add_svg_sprite(newsvg, sprite, id = NULL) +} +\arguments{ +\item{newsvg}{\code{xml_document} object with svg that will be added} + +\item{sprite}{\code{xml_document} object with the sprite map} + +\item{id}{sets the id of the new svg inside the sprite map, if is +NULL it looks for the id inside the \code{newsvg} object.} +} +\value{ +invisible copy of new sprite map +} +\description{ +Add svg to a sprite map +} +\details{ +the sprite map object is modified in place +} +\examples{ +library(xml2) +sprite <- xml_new_root("svg", "version" = "1.1", "xmlns" = "http://www.w3.org/2000/svg") +newsvg <- read_xml(" + + ") +add_svg_sprite(newsvg, sprite, "new-icon") + +sprite <- xml_new_root("svg", "version" = "1.1", "xmlns" = "http://www.w3.org/2000/svg") +appsilon_logo <- read_xml(system.file("assets/icons/appsilon-logo.svg", package = "shiny.semantic")) +shiny_logo <- read_xml(system.file("assets/icons/shiny-logo.svg", package = "shiny.semantic")) +add_svg_sprite(appsilon_logo, sprite, "appsilon_logo") +add_svg_sprite(shiny_logo, sprite, "shiny_logo") +sprite_file <- tempfile() +write_xml(sprite, sprite_file, options = "no_declaration") +} diff --git a/man/remove_svg_sprite.Rd b/man/remove_svg_sprite.Rd new file mode 100644 index 00000000..f49f8075 --- /dev/null +++ b/man/remove_svg_sprite.Rd @@ -0,0 +1,40 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/sprite.R +\name{remove_svg_sprite} +\alias{remove_svg_sprite} +\title{Remove svg from a sprite map} +\usage{ +remove_svg_sprite(id, sprite) +} +\arguments{ +\item{id}{symbol id of the svg to be removed} + +\item{sprite}{\code{xml_document} object with the sprite map} +} +\value{ +invisible copy of new sprite map +} +\description{ +Remove svg from a sprite map +} +\details{ +sprite map is modifyed on place +} +\examples{ +library(xml2) +sprite <- xml_new_root("svg", "version" = "1.1", "xmlns" = "http://www.w3.org/2000/svg") +newsvg <- read_xml(" + +") +add_svg_sprite(newsvg, sprite, "new-icon") +remove_svg_sprite("new-icon", sprite) + +#' sprite <- xml_new_root("svg", "version" = "1.1", "xmlns" = "http://www.w3.org/2000/svg") +appsilon_logo <- read_xml(system.file("assets/icons/appsilon-logo.svg", package = "shiny.semantic")) +shiny_logo <- read_xml(system.file("assets/icons/shiny-logo.svg", package = "shiny.semantic")) +add_svg_sprite(appsilon_logo, sprite, "appsilon_logo") +add_svg_sprite(shiny_logo, sprite, "shiny_logo") +remove_svg_sprite("shiny_logo", sprite) +sprite_file <- tempfile() +write_xml(sprite, sprite_file, options = "no_declaration") +} diff --git a/tests/testthat/test_sprite.R b/tests/testthat/test_sprite.R new file mode 100644 index 00000000..01c12d9e --- /dev/null +++ b/tests/testthat/test_sprite.R @@ -0,0 +1,26 @@ +context("sprite") + +test_that("Function addsvg_sprite add an svg to current sprite",{ + require(xml2) + sprite <- xml_new_root("svg", "version" = "1.1", "xmlns" = "http://www.w3.org/2000/svg") + newsvg <- read_xml(" + + ") + + add_svg_sprite(newsvg, sprite, "shiny-logo") + expect_equal(xml_attr(xml_child(sprite), "id"), "shiny-logo") + expect_error(add_svg_sprite(newsvg, sprite, "shiny-logo")) +}) + +test_that("Function rmsvg_sprite removes the \"id\" svg",{ + require(xml2) + sprite <- xml_new_root("svg", "version" = "1.1", "xmlns" = "http://www.w3.org/2000/svg") + newsvg <- read_xml(" + + ") + + add_svg_sprite(newsvg, sprite, "shiny-logo") + remove_svg_sprite("shiny-logo", sprite) + expect_equal(length(xml_children(sprite)), 0) + expect_error(remove_svg_sprite("shiny-logo", sprite)) +})