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))
+})