diff --git a/NAMESPACE b/NAMESPACE index d2c62a4..387940a 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -6,6 +6,7 @@ import(bsicons) import(echarts4r) import(shiny) import(tablerDash) +import(waiter) importFrom(dplyr,across) importFrom(dplyr,all_of) importFrom(dplyr,as_tibble) diff --git a/R/app_server.R b/R/app_server.R index 948bb6c..e377c8e 100644 --- a/R/app_server.R +++ b/R/app_server.R @@ -13,52 +13,51 @@ app_server <- function(input, output, session) { class = "btn-success" ) - observeEvent(TRUE, { - showModal( - modalDialog( - title = "WeLCOME to the PoKeMON APP", - "Do you want to start the app!", - size = "s", - footer = tagList( - cancel_btn, - actionButton( - "continue", - "Continue", - class = "btn-danger", - `data-dismiss` = "modal" - ) - ) - ) - ) - }) + # observeEvent(TRUE, { + # showModal( + # modalDialog( + # title = "WeLCOME to the PoKeMON APP", + # "Do you want to start the app!", + # size = "s", + # footer = tagList( + # cancel_btn, + # actionButton( + # "continue", + # "Continue", + # class = "btn-danger", + # `data-dismiss` = "modal" + # ) + # ) + # ) + # ) + # }) - observeEvent(input$continue, { - showModal( - modalDialog( - title = "The N1 poKemON APP IN THE WORLD!!!!", - size = "s", - "Please confirm", - footer = tagList( - modalButton("continue"), - actionButton( - "cancel", - "Cancel", - class = "btn-success", - `data-dismiss` = "modal" - ) - ) - ) - ) - }, priority = 10) + # observeEvent(input$continue, { + # showModal( + # modalDialog( + # title = "The N1 poKemON APP IN THE WORLD!!!!", + # size = "s", + # "Please confirm", + # footer = tagList( + # modalButton("continue"), + # actionButton( + # "cancel", + # "Cancel", + # class = "btn-success", + # `data-dismiss` = "modal" + # ) + # ) + # ) + # ) + # }, priority = 10) observeEvent(input$cancel, { session$reload() }) main <- mod_poke_select_server("poke_select_1") - mod_poke_info_server("poke_info_1", main$selected, main$is_shiny) + mod_poke_info_server("poke_info_1", main$selected) mod_poke_type_server("poke_type_1", main$selected) - mod_poke_evolve_server("poke_evolve_1", main$selected, main$is_shiny) mod_poke_stats_server("poke_stats_1", main$selected) mod_poke_move_server("poke_move_1", main$selected) mod_poke_location_server("poke_location_1", main$selected) diff --git a/R/app_ui.R b/R/app_ui.R index 9147878..3662366 100644 --- a/R/app_ui.R +++ b/R/app_ui.R @@ -3,70 +3,56 @@ #' @param request Internal parameter for `{shiny}`. #' DO NOT REMOVE. #' @noRd +#' @import waiter app_ui <- function(request) { - nav_tag <- tablerDashNav( - id = "mymenu", - src = "https://www.ssbwiki.com/images/9/9c/Master_Ball_Origin.png", - navMenu = tablerNavMenu( - tablerNavMenuItem( - tabName = "PokeInfo", - icon = "home", - h1("PokeInfo", style = "color: green"), - style = "background-color: grey", - ) - ), - # Select input UI module - mod_poke_select_ui("poke_select_1"), - tablerDropdown( - tablerDropdownItem( - title = NULL, - href = "https://pokeapi.co", - url = "https://pokeapi.co/static/logo-6221638601ef7fa7c835eae08ef67a16.png", - status = "success", - date = NULL, - "This app use pokeApi by Paul Hallet and PokeAPI contributors." - ) - ) - ) - - nav_tag[[1]]$attribs$style <- paste0( - nav_tag[[1]]$attribs$style, - "background-color: yellow" - ) + #nav_tag <- tablerDashNav( + # id = "mymenu", + # src = "https://www.ssbwiki.com/images/9/9c/Master_Ball_Origin.png", + # mod_poke_select_ui("poke_select_1") + #) tagList( # Leave this function for adding external resources golem_add_external_resources(), # Your application UI logic - tablerDashPage( - navbar = nav_tag, - footer = tablerDashFooter( - copyrights = "Disclaimer: this app is purely intended for learning purpose. @David Granjon, 2023" - ), - title = "Gotta Catch'Em (Almost) All", - body = tablerDashBody( - style = "background-color: brown", - tablerTabItems( - tablerTabItem( - tabName = "PokeInfo", - fluidRow( - column( - width = 8, - mod_poke_info_ui("poke_info_1"), - mod_poke_type_ui("poke_type_1"), - mod_poke_evolve_ui("poke_evolve_1") - ), - column( - width = 4, - mod_poke_stats_ui("poke_stats_1"), - mod_poke_move_ui("poke_move_1"), - mod_poke_location_ui("poke_location_1") - ) - ) - ) - ) + waiter::waiterPreloader( + html = tagList( + tags$div(class="pokeball", + tags$div(class="pokeball__button") + ), + "Loading ..." ) + ), + bslib::page_fluid( + title = "PokeApp", + br(), + jumbotron("Gotta Catch'Em (Almost) All"), + tags$div(class = "d-flex justify-content-center my-5", mod_poke_select_ui("poke_select_1")), + + bslib::layout_columns( + col_widths = c(4, 8), + bslib::layout_columns( + width = 1, + mod_poke_info_ui("poke_info_1"), + mod_poke_location_ui("poke_location_1") + ), + mod_poke_stats_ui("poke_stats_1") + ), + + #tags$section( + # class = "section-body", + # tags$div( + # class = "left", + # mod_poke_info_ui("poke_info_1"), + # mod_poke_location_ui("poke_location_1") + # ), + # tags$div(class="right", mod_poke_stats_ui("poke_stats_1")) + #), + # style = "background-color: brown", + mod_poke_move_ui("poke_move_1"), + mod_poke_type_ui("poke_type_1"), + footer = "Disclaimer: this app is purely intended for learning purpose. @David Granjon, 2023" ) ) } @@ -90,15 +76,7 @@ golem_add_external_resources <- function() { path = app_sys("app/www"), app_title = "shinyMons2" ), - # custom font setup - tags$link( - href = "https://fonts.googleapis.com/css?family=Press+Start+2P", - rel = "stylesheet" - ), - tags$style( - "html, body, pre, code, kbd, samp { - font-family: 'Press Start 2P'; - }" - ) + # Screen loader + waiter::useWaiter() ) } diff --git a/R/mod_poke_evolve.R b/R/mod_poke_evolve.R index 4841704..56c0c85 100644 --- a/R/mod_poke_evolve.R +++ b/R/mod_poke_evolve.R @@ -1,62 +1,53 @@ -#' poke_evolve UI Function -#' -#' @description A Module. -#' -#' @param id,input,output,session Internal parameters for {}. -#' -#' @noRd -mod_poke_evolve_ui <- function(id) { - ns <- NS(id) - uiOutput(ns("poke_evolve")) -} -#' poke_evolve Server Functions -#' -#' @param selected Selected pokemon data. -#' @param is_shiny Is the pokemon under its shiny form? -#' -#' @noRd -mod_poke_evolve_server <- function(id, selected, is_shiny) { - moduleServer(id, function(input, output, session) { - # treat data and generate the timeline - output$poke_evolve <- renderUI({ - req(!is.null(selected())) - evol <- selected()$evolve_from + #' poke_evolve UI Function + #' + #' @description A Module. + #' + #' @param id,input,output,session Internal parameters for {}. + #' + #' @noRd + mod_poke_evolve_ui <- function(id) { + ns <- NS(id) + uiOutput(ns("poke_evolve")) + } - # If pokemon can't evolve ... - if (length(evol) == 0) { - tablerAlert( - title = "Alert", - "This Pokemon is a base pokemon.", - icon = "thumbs-up", - status = "success" - ) - } else { - # Check that the evolution belongs to the first 151 pkmns ... - if (evol$id <= 151) { - tablerTimelineItem( - title = paste0("Evolves from: ", evol$name), - status = "green", - date = NULL, - img( - src = if (is_shiny()) { - poke_data[[evol$id]]$sprites$front_shiny - } else { - poke_data[[evol$id]]$sprites$front_default - } - ) + #' poke_evolve Server Functions + #' + #' @param selected Selected pokemon data. + #' @param is_shiny Is the pokemon under its shiny form? + #' + #' @noRd + mod_poke_evolve_server <- function(id, selected) { + moduleServer(id, function(input, output, session) { + + # treat data and generate the timeline + output$poke_evolve <- renderUI({ + req(!is.null(selected())) + evol <- selected()$evolve_from + + img <- NULL + # If pokemon can't evolve ... + if (length(evol) == 0) { + tags$div( + tags$p("Base Pokemon", class="evolution-text-inside"), ) } else { - tablerAlert( - title = "Alert", - "This pokemon is an evolution of another pokemon but not - in the first generation.", - icon = "thumbs-up", - status = "success" - ) + # Check that the evolution belongs to the first 151 pkmns ... + if (evol$id <= 151) { + tags$div( + tags$p("Evolves From", class="evolution-text"), + tags$img( + src = poke_data[[evol$id]]$sprites$front_shiny, + ) + ) + } else { + tags$div( + tags$p("Not first generation", class="evolution-text-inside"), + ) + } } - } + + }) }) - }) -} + } diff --git a/R/mod_poke_info.R b/R/mod_poke_info.R index 55b5962..0347174 100644 --- a/R/mod_poke_info.R +++ b/R/mod_poke_info.R @@ -16,8 +16,9 @@ mod_poke_info_ui <- function(id) { #' @param is_shiny Is the pokemon in shiny form? Boolean. #' #' @noRd -mod_poke_info_server <- function(id, selected, is_shiny) { +mod_poke_info_server <- function(id, selected) { moduleServer(id, function(input, output, session) { + ns <- session$ns # generate the profile cards (as many as the number of selected pokemons) output$poke_infos <- renderUI({ @@ -26,42 +27,42 @@ mod_poke_info_server <- function(id, selected, is_shiny) { pokemon <- selected() tagList( - fluidRow( - tablerCard( - tablerProfileCard( - title = selected()$name, - subtitle = tagList( - pokemon$description, - tablerTagList( - align = "center", - tablerTag(name = pokemon$shape, rounded = TRUE, color = "default"), - tablerTag(name = pokemon$habitat, rounded = TRUE, color = pokemon$color) - ) - ), - background = "https://images.pexels.com/photos/355748/pexels-photo-355748.jpeg", - src = if (!is_shiny()) { - pokemon$sprites$front_default - } else { - pokemon$sprites$front_shiny - }, - socials = tablerSocialLinks( - tablerSocialLink( - name = "pokeApi", - href = paste0("https://pokeapi.co/api/v2/pokemon/", tolower(selected())), - icon = "at" + tags$div( + class = "left-content", + tags$div( + style = "position:relative;", + tags$img(class = "avatar-icon", src = pokemon$sprites$front_shiny), + mod_poke_evolve_ui(ns("poke_evolve_1")) + ), + tags$div(class = "name", selected()$name), + tags$div(class = "description", pokemon$description), + tags$div( + class = "social-and-pills", + badge(HTML(sprintf("%s: %s", bs_icon("fingerprint"), pokemon$shape)), "secondary"), + badge(HTML(sprintf("%s: %s", bs_icon("house"), pokemon$habitat)), "secondary") + ), + tags$div( + class = "info-grid", + purrr::map( + other_stats_names(), + ~ tags$div( + class = "info-card", + tags$div( + class = "title", + purrr::map( + unlist(stringr::str_split(.x, "_")), + tags$span + ) ), - tablerSocialLink( - name = "Bulbapedia", - href = paste0("https://bulbapedia.bulbagarden.net/wiki/", selected(), "_(Pok\u00e9mon)"), - icon = "address-card" - ) - ), - width = 4 + tags$p(selected()$other_stats[[.x]]) + ) ) ) - ), - br() + ) ) }) + + mod_poke_stats_server("poke_stats_1", selected) + mod_poke_evolve_server("poke_evolve_1", selected) }) } diff --git a/R/mod_poke_location.R b/R/mod_poke_location.R index 3e5ccfc..b27650d 100644 --- a/R/mod_poke_location.R +++ b/R/mod_poke_location.R @@ -9,7 +9,7 @@ #' @importFrom shiny NS tagList mod_poke_location_ui <- function(id) { ns <- NS(id) - uiOutput(ns("poke_locations"), class = "col-sm-6") + uiOutput(ns("poke_locations")) } #' poke_location Server Functions @@ -26,23 +26,28 @@ mod_poke_location_server <- function(id, selected) { locations <- selected()$locations - tablerCard( - title = paste0("where to find ", selected()$name, " ?"), - collapsible = FALSE, - closable = FALSE, - zoomable = FALSE, - statusSide = "top", - width = 12, - if (!is.null(locations)) { - lapply(seq_along(locations), function(i) { - if (!is.null(locations[[i]])) { - fluidRow(paste(i, ":", locations[[i]])) - } - }) - } else { - "This pokemon cannot be found in the wild." - } + if (is.null(locations)) { + location_content <- "This pokemon cannot be found in the wild." + } else { + location_content <- list_group(locations) + } + tagList( + tags$div( + class = "d-flex justify-content-center mt-4", + HTML( + sprintf( + "Location %s", + bslib::tooltip( + tags$sup(bs_icon("patch-question", size = "1em")), + "The Pokemon map is organized between roads, cities and locations. + This indicates where to find the pokemon." + ) + ) + ) + ), + location_content ) }) + }) } diff --git a/R/mod_poke_move.R b/R/mod_poke_move.R index 45cd10d..9f3e5aa 100644 --- a/R/mod_poke_move.R +++ b/R/mod_poke_move.R @@ -7,7 +7,17 @@ #' @noRd mod_poke_move_ui <- function(id) { ns <- NS(id) - uiOutput(ns("poke_moves"), class = "col-sm-12") + tags$section( + class = "moves", + h3( + "Moves", + bslib::tooltip( + tags$sup(bs_icon("patch-question", size = "0.75em")), + "Moves are attacks the pokemon can learn." + ) + ), + reactable::reactableOutput(ns("poke_moves")) + ) } #' poke_move Server Functions @@ -16,47 +26,60 @@ mod_poke_move_ui <- function(id) { #' @noRd mod_poke_move_server <- function(id, selected) { moduleServer(id, function(input, output, session) { + ns <- session$ns # generate the card - output$poke_moves <- renderPrint({ + output$poke_moves <- reactable::renderReactable({ req(!is.null(selected())) - moves <- selected()$moves - tablerCard( - title = paste0(selected()$name, " Moves"), - statusSide = "top", - collapsible = FALSE, - closable = FALSE, - zoomable = FALSE, - width = 12, - - # card content - lapply(seq_along(moves), FUN = function(i) { - move_name <- moves[[i]]$name - move_pp <- moves[[i]]$pp - move_priority <- moves[[i]]$priority - move_type <- moves[[i]]$type - move_power <- moves[[i]]$power - move_accuracy <- moves[[i]]$accuracy - move_text <- moves[[i]]$text - - fluidRow( - paste("Move: ", move_name), - tagAppendAttributes( - tablerTag( - paste("Power:", move_power), - href = NULL, - rounded = FALSE, - color = NULL - ), - class = "mx-2" - ), - move_text + move_df <- data.frame( + name = purrr::map(moves, `[`, 'name') |> unlist(), + power = purrr::map(moves, `[`, 'power') |> unlist(), + text = purrr::map(moves, `[`, 'text') |> unlist() + ) + + bar_chart <- function(label, width = "100%", height = "100%", fill = "#00bfc4", background = NULL) { + + if (width == "0px") { + return(div( + style = list( + alignItems = "center", + background = background, + padding = "3px", + `font-size`= "0.75rem"), + label) ) - }) + } + + bar <- div(style = list(background = fill, display = 'flex', width = width, height = height, padding = "3px"), label) + chart <- div(style = list( + flexGrow = 1, + `font-size`= "0.75rem", + color="white", + background = background), + bar + ) + div(style = list(alignItems = "center"), chart) + } + + reactable::reactable( + move_df |> dplyr::arrange(-power), + height = 270, + pagination = FALSE, + columns = list( + name = reactable::colDef(name = "Move", width = 150, cell = function(value) { tags$strong(value)} ), + text = reactable::colDef(name = "Details"), + power = reactable::colDef(name = "Power", width = 150, align = "left", cell = function(value) { + width <- ifelse(is.na(value), "0px", paste0(value / max(move_df$power, na.rm = TRUE) * 100, "%")) + bar_chart(value, width = width, fill = "#393D47", background = "#e1e1e1") + }) + ) ) + }) + }) + } diff --git a/R/mod_poke_select.R b/R/mod_poke_select.R index e128bfc..a31369d 100644 --- a/R/mod_poke_select.R +++ b/R/mod_poke_select.R @@ -18,29 +18,15 @@ mod_poke_select_ui <- function(id) { ns <- NS(id) fluidRow( align = "center", - bs_icon("hand-index-fill"), pickerInput( inputId = ns("selected"), - width = "10%", - options = list(style = "btn-primary"), + options = list(style = "btn-secondary form-select-lg"), multiple = FALSE, choices = poke_names, choicesOpt = list( content = sprintf(" %s", poke_sprites, poke_names) ), selected = poke_names[[1]] - ), - # because it's a shiny app ;) - tagAppendAttributes( - prettySwitch( - inputId = ns("is_shiny"), - label = "Shiny?", - value = TRUE, - status = "danger", - slim = TRUE, - width = "100%" - ), - class = "m-2" ) ) } @@ -61,7 +47,7 @@ mod_poke_select_server <- function(id) { return( list( selected = selected_pokemon, - is_shiny = reactive(input$is_shiny) + is_shiny = reactive(TRUE) ) ) }) diff --git a/R/mod_poke_stats.R b/R/mod_poke_stats.R index 8f9b84f..9f550a2 100644 --- a/R/mod_poke_stats.R +++ b/R/mod_poke_stats.R @@ -24,20 +24,6 @@ mod_poke_stats_server <- function(id, selected) { moduleServer(id, function(input, output, session) { ns <- session$ns - # Programmatically generate stat cards - lapply(other_stats_names(), function(stat) { - output[[stat]] <- renderUI({ - req(input$poke_basic_stats) - val <- selected()$other_stats[[stat]] - - tablerStatCard( - value = val, - title = h1(stat, style = "color: pink !important"), - width = 12 - ) - }) - }) - # Generate radar chart for pokemons output$poke_stats <- renderEcharts4r({ req(!is.null(selected())) @@ -47,31 +33,24 @@ mod_poke_stats_server <- function(id, selected) { # card wrapper for the charts output$poke_stats_card <- renderUI({ req(!is.null(selected())) - - tablerCard( - title = h1(paste0(selected()$name, " Stats")), - options = tagList( - prettySwitch( - inputId = ns("poke_basic_stats"), - label = "Display Basic Stats?", - value = TRUE, - status = "warning", - slim = TRUE, - fill = FALSE, - bigger = TRUE, - inline = FALSE + bslib::card( + bslib::card_header( + HTML( + sprintf( + "%s Statistics %s", + bs_icon("graph-up"), + bslib::tooltip( + tags$sup(bs_icon("patch-question", size = "1em")), + "A Pokemon has 5 main stats. Mew has 100 for each stat. The maximum for each stat + is 255. The higher the sum, the better is the Pokemon." + ) + ) ) ), - footer = tags$strong(sprintf("Sum of stats: %s (Mew is 500)", selected()$sum_stats)), - status = "purple", - statusSide = "left", - collapsible = FALSE, - closable = FALSE, - zoomable = FALSE, - width = 12, - overflow = FALSE, - echarts4rOutput(outputId = ns("poke_stats")) + bslib::card_body(echarts4rOutput(outputId = ns("poke_stats"))), + bslib::card_footer(sprintf("Sum of stats: %s (Mew is 500)", selected()$sum_stats)) ) }) + }) } diff --git a/R/mod_poke_type.R b/R/mod_poke_type.R index e910ba7..08840d8 100644 --- a/R/mod_poke_type.R +++ b/R/mod_poke_type.R @@ -7,7 +7,20 @@ #' @noRd mod_poke_type_ui <- function(id) { ns <- NS(id) - uiOutput(ns("poke_types")) + tags$section( + class="moves", + h3( + "Types", + bslib::tooltip( + tags$sup(bs_icon("patch-question", size = "0.75em")), + "A pokemon has at least one type but may have several. + Each type has weakness/strength against another one. + For instance, fire is weak against water." + ) + + ), + uiOutput(ns("poke_types")) + ) } #' poke_type Server Functions @@ -51,41 +64,72 @@ mod_poke_type_server <- function(id, selected) { poke_color <- get_type_colors(type_name) - tagList( - tablerInfoCard( - value = paste(type_slot, type_name), - status = poke_color, - icon = icon("xmark"), - description = "%", - width = 12 + tags$div( + tags$div( + "Damages", + + ), + tags$div( + "Damages From", + + ), + tags$div( + "Damages To", + + ) + ) + damage_table <- tags$table( + tags$tr( + tags$th("Damages"), + tags$th("Damages From"), + tags$th("Damages To") + ), + tags$tr( + tags$td(span(class="n", "2X")), + tags$td( + purrr::map(double_damage_from, + ~tags$span(.x, class="pill bg-secondary tag tag-rounded",) + ) + ), + tags$td( + purrr::map(double_damage_to, ~tags$span(.x, class="pill bg-secondary tag tag-rounded")) + ) ), - fluidRow( - column( - width = 10, - align = "left", - h5("Damages from:"), br(), - HTML(paste0(tablerTag(name = "2X", rounded = FALSE, color = "red"), " ")), - lapply(seq_along(double_damage_from), FUN = function(j) double_damage_from[[j]]), br(), - HTML(paste0(tablerTag(name = "0.5X", rounded = FALSE, color = "green"), " ")), - lapply(seq_along(half_damage_from), FUN = function(j) half_damage_from[[j]]), br(), - HTML(paste0(tablerTag(name = "0", rounded = FALSE, color = "default"), " ")), - lapply(seq_along(no_damage_from), FUN = function(j) no_damage_from[[j]]) + tags$tr( + tags$td(span(class="n", "1/2")), + tags$td( + purrr::map(half_damage_from, ~tags$span(.x, class="pill bg-light tag tag-rounded")) ), - column( - width = 2, - align = "left", - h5("Damages to:"), br(), - HTML(paste0(tablerTag(name = "2X", rounded = FALSE, color = "green"), " ")), - lapply(seq_along(double_damage_to), FUN = function(j) double_damage_to[[j]]), br(), - HTML(paste0(tablerTag(name = "0.5X", rounded = FALSE, color = "red"), " ")), - lapply(seq_along(half_damage_to), FUN = function(j) half_damage_to[[j]]), br(), - HTML(paste0(tablerTag(name = "0", rounded = FALSE, color = "default"), " ")), - lapply(seq_along(no_damage_to), FUN = function(j) no_damage_to[[j]]) + tags$td( + purrr::map(half_damage_to, ~tags$span(.x, class="pill bg-light tag tag-rounded")) ) ), - br() + tags$tr( + tags$td(class="n", "0"), + tags$td( + purrr::map(no_damage_from, + ~tags$span(.x, + class="pill bg-white tag tag-rounded", + ) + ) + ), + tags$td( + purrr::map(no_damage_to, + ~tags$span(.x, + class="pill bg-white tag tag-rounded", + ) + ) + ) + ) ) + + tags$div(class="type-card", + tags$span(class="title", type_name), + damage_table + ) + }) + }) }) } diff --git a/R/utils.R b/R/utils.R index 822f3e1..bff63dd 100644 --- a/R/utils.R +++ b/R/utils.R @@ -6,21 +6,21 @@ #' @keywords internal get_type_colors <- function(type) { switch(type, - "normal" = "gray-lightest", - "fighting" = "red", - "flying" = "indigo", - "poison" = "purple-light", - "ground" = "yellow-lighter", - "rock" = "yellow-darker", - "bug" = "green-lighter", - "ghost" = "purple-dark", + "normal" = "lightgray", + "fighting" = "#F98D80", + "flying" = "#BD9FFC", + "poison" = "#CBC3E3", + "ground" = "#FFFFED", + "rock" = "#FDDA0D", + "bug" = "#AEF1BD", + "ghost" = "#BD9FFC", "fire" = "orange", - "water" = "azure", - "grass" = "green", + "water" = "#B3DFF8", + "grass" = "#1EE80A", "electric" = "yellow", - "psychic" = "pink", - "ice" = "azure-lighter", - "dragon" = "purple-darker" + "psychic" = "#FF9AD0", + "ice" = "#C5FAF8", + "dragon" = "#BD9FFC" ) } @@ -140,12 +140,14 @@ create_radar_stats <- function(pokemon) { max = get_max_of_max() ) |> e_radar(z, name = paste0(pokemon$evolve_from$name, " Stats")) |> + e_color(c('#393D47', '#BBBBBB')) |> e_tooltip(trigger = "item") } else { data |> e_charts(x) |> e_radar(y, name = paste0(pokemon$name, " Stats"), max = get_max_of_max()) |> - e_tooltip(trigger = "item") + e_tooltip(trigger = "item") |> + e_color('#393D47') } } @@ -195,6 +197,32 @@ select_pokemon <- function(selected) { } } +badge <- function(text, color) { + tags$span( + class = sprintf("badge rounded-pill text-bg-%s", color), + text + ) +} + +jumbotron <- function(title, ...) { + tags$div( + class = "p-5 mt-1 mb-4 bg-secondary rounded-3", + tags$div( + class = "container-fluid py-5", + tags$h1(class = "display-5 fw-bold", title), + ... + ) + ) +} + +list_group <- function(...) { + items <- c(...) + tags$ol( + class = "list-group list-group-numbered", + lapply(items, tags$li, class = "list-group-item") + ) +} + # R CMD check ... globalVariables("poke_data") diff --git a/app.R b/app.R index 0ff3a5d..b30ed24 100644 --- a/app.R +++ b/app.R @@ -5,3 +5,4 @@ pkgload::load_all() options( "golem.app.prod" = TRUE) run_app() # add parameters here (if any) + diff --git a/inst/app/www/styles.css b/inst/app/www/styles.css new file mode 100644 index 0000000..9c2383d --- /dev/null +++ b/inst/app/www/styles.css @@ -0,0 +1,418 @@ +:root { + --main-color: #393D47; +} + +.background-banner { + width: 100%; + height: 150px; + background-size: contain; + background-color: var(--main-color); +} + +.container { + margin: 0; + max-width: 100%; + padding: 0; +} + +.right { + margin-top: 50px; + width: 100%; + overflow: hidden; +} + +section.section-body { + display: grid; + grid-template-columns: 300px 1fr; + gap: 50px; +} + + +.left-content { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + text-align: center; +} + +.name { + font-size: 1.2rem; + font-weight: 900; +} + +.avatar-icon { + background: white; + border-radius: 50%; + margin-bottom: 20px; + margin-top: -50px; + margin-left: 90px; + height: 150px; + width: 150px; + border: solid 5px var(--main-color); + /* top: -50px; */ + /* left: 0px; */ + /* position: absolute; */ +} + +section.section-body { + display: grid; + grid-template-columns: 300px 1fr; + padding: 0px 40px; +} + +.header.py-4 { + max-height: 60px; +} + +body { + background: white; +} + +button.btn.dropdown-toggle.btn-primary { + margin-top: -10px; + background: white; + color: black; + border: 0px; + /* width: max-content; */ + /* text-align: right; */ + border-bottom: 2px solid var(--main-color); + border-radius: 0px; + /* margin-bottom: 20px; */ +} + +a.header-brand { + margin-left: 20px; +} + +.description { + margin-bottom: 20px; +} + +.social-and-pills { + display: flex; + justify-content: center; + align-items: center; + gap: 7px; +} + +ul.social-links.list-inline.mb-0.mt-2 { + margin-top: 0px !important; +} + +.info-grid { +display: flex; + flex-wrap: wrap; + /* display: grid; */ + /* grid-template-columns: 1fr 1fr 1fr; */ + /* grid-template-rows: 1fr 1fr; */ + text-align: center; + justify-content: center; + /* align-items: center; */ + margin-top: 20px; + gap: 12px; + justify-content: center; +} + +.info-card { + border: solid 3px var(--main-color); + width: 90px; + height: 70px; + font-size: 10px; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +.info-card .title { + font-weight: 900; + font-size: 12px; + color: var(--main-color); + line-height: 1; + flex-direction: column; + display: flex; + margin-bottom: 4px; +} + +.right-grid .bslib-grid { + grid-template-columns: 1fr 200px; +} + +section.moves { + width: 80%; + margin: 0 auto; +} + +.moves-grid { + max-width: 80vw; + margin: auto; + columns: 4 280px; + column-gap: 1rem; + font-size: 1.2rem; + max-width: 80vw; + margin: auto; + columns: 4 280px; + column-gap: 1rem; + font-size: 1.2rem; +} + +.move-card { + width: 300px; + border: solid 3px var(--main-color); + position: relative; + /* background: red; */ + /* color: white; */ + margin: 0 0 1rem; + display: inline-block; + width: 100%; + /* text-align: center; */ + /* font-weight: bold; */ +} + +.info-card p { + margin: 0 !important; +} + +.move-card p { + padding: 20px; + font-size: 12px; +} + +.move-card hr { + display: none; +} + +.move-card p { + padding: 20px; +} + +.power span { + padding: 0px; + margin: 0px; + line-height: 9px; + font-size: 10px; +} + +.move-card .title { + font-size: 20px; + /* margin-left: 20px; */ + /* width: 100%; */ + background: var(--main-color); + padding-left: 20px; + padding: 40px; + /* margin: 20px; */ + color: white; +} + +.power { + height: 60px; + width: 60px; + border-radius: 50%; + flex-direction: column; + align-items: center; + justify-content: center; + display: flex; + background: lightgray; + /* padding: 20px; */ + position: absolute; + right: 25px; + top: 65px; + color: var(--main-color); + line-height: 1; +} + +.moves h3 { + text-align: center; + font-size: 30px; + margin-top: 100px; +} + +.card { + border-color: var(--main-color); +} + +.card-header { + border-bottom-color: var(--main-color); +} + +.card-footer { + border-top-color: var(--main-color); +} + +#poke_info_1-poke_evolve_1-poke_evolve { + position: absolute; + background: white; + border-radius: 50%; + width: 75px; + height: 75px; + border: solid 3px var(--main-color); + justify-content: center; + align-items: center; + display: flex; + font-size: 12px; + text-align: center; + top: 30px; + left: 50px; +} + +div#poke_info_1-poke_evolve_1-poke_evolve p { + margin: 0; + line-height: 12px; +} + +p.evolution-text { + padding: 7px; + padding-right: 50px; + z-index: -900; +} + +.evolution-text { + position: absolute; + left: -90px; + /* padding: 12px; */ + background: var(--main-color); + padding: 7px; + padding-right: 50px; + color: white; + top: 25px; + /* border-radius: 20px; */ + padding: 7px; + padding-right: 50px; + z-index: -900; +} + +h3.moves { + text-align: center; + margin-top: 100px; +} + +#poke_type_1-poke_types { + display: flex; + flex-direction: row; + width: 100%; + margin: 0 auto; + margin-top: 50px; + gap: 12px; + margin-bottom: 150px; +} + +.type-card { + border: solid 1px var(--main-color); +} + +.type-card .title { + background: var(--main-color); + color: white; + padding: 7px; + width: 100%; + display: block; + text-align: center; +} + +.type-card { + border: solid 2px var(--main-color); + width: 100%; +} + +.type-card table { + border-collapse: collapse; + width: 90%; + margin: 10px auto; +} + +.type-card tr { + display: grid; + grid-template-columns: 70px 1fr 1fr; + gap: 20px; + justify-content: center; + align-items: center; + /* text-align: center; */ + border-bottom: solid 1px; + padding-bottom: 10px; +} + +.type-card td { + display: flex; + flex-direction: column; + gap: 10px; + margin-top: 10px; +} + +span.pill.tag.tag-rounded { + text-align: center; + justify-content: center; + background-color: white; + border: solid 1px var(--main-color); +} + + +/* Spinner: https://github.com/athanstan/css-pokeball */ +.pokeball{ + position: relative; + width: 150px; + height: 150px; + background: #fff; + border: 5px solid #000; + border-radius: 50%; + overflow: hidden; + box-shadow: inset -10px 10px 0 10px #ccc; + animation: shake 1.25s cubic-bezier(.36,.07,.19,.97) infinite; + animation-play-state: paused; +} + +.pokeball{ + animation-play-state: running; +} + +@keyframes shake { + 0 { transform: translate(0, 0) rotate(0); } + 20% { transform: translate(-10px, 0) rotate(-20deg); } + 30% { transform: translate(10px, 0) rotate(20deg); } + 50% { transform: translate(-10px, 0) rotate(-10deg); } + 60% { transform: translate(10px, 0) rotate(10deg); } + 100% { transform: translate(0, 0) rotate(0); } + } + +.pokeball::before, +.pokeball::after { + content:""; + position: absolute; +} + +.pokeball::before { + background: red; + width: 100%; + height: 50%; + } + +.pokeball::after { + top: calc(50% - 5px); + width: 100%; + height: 10px; + background: #000; + } + +.pokeball__button { + position: absolute; + top: calc(50% - 15px); + left: calc(50% - 15px); + width: 30px; + height: 30px; + background: #fff; + border: 4px solid #7f8c8d; + border-radius: 50%; + z-index: 10; + box-shadow: 0 0 0 7px black; + animation: blink .5s alternate infinite; + animation-play-state: paused; +} + +.pokeball .pokeball__button{ + animation-play-state: running; +} + +@keyframes blink { + from { background: #eee;} + to { background: #e74c3c; } +}