knitr::opts_chunk$set(echo = TRUE) options(reactable.static = TRUE)
```{css ref.label='styles', echo=FALSE}
```r
```{css ref.label='pkgdown_styles', echo=FALSE}
```r library(reactable) library(htmltools) player_stats <- read.csv("player_stats.csv", stringsAsFactors = FALSE) team_stats <- read.csv("team_stats.csv", stringsAsFactors = FALSE) line_score <- read.csv("line_score.csv", stringsAsFactors = FALSE) line_score <- line_score[, c("TEAM_ID", "TEAM_CITY_NAME", "TEAM_NICKNAME", "TEAM_WINS_LOSSES", "PTS_QTR1", "PTS_QTR2", "PTS_QTR3", "PTS_QTR4", "PTS")] line_score_tbl <- reactable( line_score, sortable = FALSE, defaultColDef = colDef(headerClass = "line-score-header", align = "center", minWidth = 50), columns = list( TEAM_ID = colDef(show = FALSE), TEAM_CITY_NAME = colDef( name = "", align = "left", minWidth = 250, cell = function(value, index) { team_url <- sprintf("https://stats.nba.com/team/%s/traditional", line_score[index, "TEAM_ID"]) team_name <- paste(value, line_score[index, "TEAM_NICKNAME"]) team_record <- line_score[index, "TEAM_WINS_LOSSES"] tagList( tags$a(class = "team-name", href = team_url, target = "_blank", team_name), span(class = "team-record", team_record) ) } ), TEAM_NICKNAME = colDef(show = FALSE), TEAM_WINS_LOSSES = colDef(show = FALSE), PTS_QTR1 = colDef(name = "Q1"), PTS_QTR2 = colDef(name = "Q2"), PTS_QTR3 = colDef(name = "Q3"), PTS_QTR4 = colDef(name = "Q4"), PTS = colDef(name = "Final", class = "line-score-final") ), class = "line-score-tbl" ) box_score_tbl <- function(player_stats, team_stats, team) { # Convert M:SS strings to datetimes for proper sorting player_stats$MIN_STR <- player_stats$MIN player_stats$MIN <- strptime(player_stats$MIN, format = "%M:%S") cols <- c("PLAYER_ID", "PLAYER_NAME", "START_POSITION", "MIN", "MIN_STR", "FGM", "FGA", "FG_PCT", "FG3M", "FG3A", "FG3_PCT", "FTM", "FTA", "FT_PCT", "OREB", "DREB", "REB", "AST", "STL", "BLK", "TO", "PF", "PTS", "PLUS_MINUS") stats <- player_stats[player_stats$TEAM_ABBREVIATION == team, cols] team_stats <- team_stats[team_stats$TEAM_ABBREVIATION == team, ] reactable( stats, pagination = FALSE, defaultSortOrder = "desc", defaultSorted = "PTS", defaultColDef = colDef( sortNALast = TRUE, minWidth = 45, class = JS("function(rowInfo, column, state) { // Highlight sorted columns for (let i = 0; i < state.sorted.length; i++) { if (state.sorted[i].id === column.id) { return 'sorted' } } }"), headerClass = "box-score-header", footer = function(values, name) { value <- team_stats[[name]] # Format shots made-attempted if (name %in% c("FGM", "FG3M", "FTM")) { attempted_name <- c(FGM = "FGA", FG3M = "FG3A", FTM = "FTA")[name] value <- sprintf("%s-%s", value, team_stats[[attempted_name]]) } # Format percentages if (name %in% c("FG_PCT", "FG3_PCT", "FT_PCT")) { value <- paste0(value * 100, "%") } # Format +/- if (name == "PLUS_MINUS") { value <- sprintf("%+d", value) } value } ), columns = list( PLAYER_ID = colDef(show = FALSE), PLAYER_NAME = colDef( name = "Player", defaultSortOrder = "asc", width = 130, cell = function(value, index) { player_id <- stats[index, "PLAYER_ID"] player_url <- sprintf("https://stats.nba.com/player/%s", player_id) start_position <- stats[index, "START_POSITION"] if (start_position != "") { value <- tagList(value, " ", tags$sup(start_position)) } tags$a(href = player_url, target = "_blank", value) }, footer = span(class = "box-score-total", "Totals") ), START_POSITION = colDef(show = FALSE), MIN = colDef(name = "Min", minWidth = 60, align = "right", cell = function(value, index) { if (!is.na(value)) stats[index, "MIN_STR"] else "DNP" }), MIN_STR = colDef(show = FALSE), FGM = colDef(name = "FG", minWidth = 55, cell = function(value, index) { if (!is.na(value)) sprintf("%s-%s", value, stats[index, "FGA"]) }), FGA = colDef(show = FALSE), FG_PCT = colDef(name = "FG%", minWidth = 55, format = colFormat(percent = TRUE)), FG3M = colDef(name = "3P", minWidth = 55, cell = function(value, index) { if (!is.na(value)) sprintf("%s-%s", value, stats[index, "FG3A"]) }), FG3A = colDef(name = "3PA", show = FALSE), FG3_PCT = colDef(name = "3P%", minWidth = 55, format = colFormat(percent = TRUE)), FTM = colDef(name = "FT", minWidth = 55, cell = function(value, index) { if (!is.na(value)) sprintf("%s-%s", value, stats[index, "FTA"]) }), FTA = colDef(show = FALSE), FT_PCT = colDef(name = "FT%", minWidth = 55, format = colFormat(percent = TRUE)), OREB = colDef(name = "ORB"), DREB = colDef(name = "DRB"), PLUS_MINUS = colDef(name = "+/-", cell = function(value) { if (is.na(value)) "" else sprintf("%+d", value) }) ), showSortIcon = FALSE, highlight = TRUE, striped = TRUE, class = "box-score-tbl", theme = reactableTheme(cellPadding = "8px") ) } div(class = "box-score", h2(class = "header", "Raptors vs. Warriors:", tags$a(class = "game-date", href="https://stats.nba.com/game/0041800403", target = "_blank", "Jun 5, 2019")), div(class = "line-score", line_score_tbl), div(class = "box-score-title", "Toronto Raptors"), box_score_tbl(player_stats, team_stats, "TOR"), div(class = "box-score-title", "Golden State Warriors"), box_score_tbl(player_stats, team_stats, "GSW") )
Source: NBA.com
Raw data: line_score.csv
, player_stats.csv
, team_stats.csv
htmltools::tags$link(href = "https://fonts.googleapis.com/css?family=Roboto:400,500&display=fallback", rel = "stylesheet")
```{css styles, eval=FALSE} .box-score { font-family: 'Roboto', Helvetica, Arial, sans-serif; }
.box-score a { color: #337ab7; text-decoration: none; }
.box-score a:hover, .box-score a:focus { text-decoration: underline; text-decoration-thickness: max(1px, 0.0625rem); }
.header { text-align: center; font-size: 1.25rem; }
.game-date { font-size: 1rem; }
.line-score { margin-top: 1.5rem; text-align: center; }
.line-score-tbl { margin: 0 auto; max-width: 32rem; font-size: 0.9375rem; }
.line-score-header { font-size: 0.8125rem; font-weight: 400; }
.line-score-final { font-weight: 500; }
.team-name { font-weight: 500; }
.team-record { margin-left: 0.375rem; color: hsl(0, 0%, 45%); font-size: 0.75rem; }
.box-score-title { margin-top: 1.5rem; padding: 0.5rem; background-color: hsl(205, 100%, 36%); color: hsl(0, 0%, 98%); font-size: 0.9375rem; font-weight: 400; }
.box-score-tbl { font-size: 0.75rem; letter-spacing: 0.2px; }
.box-score-header { border-bottom-width: 1px; background-color: hsl(205, 93%, 16%); color: hsl(0, 0%, 98%); font-weight: 400; font-size: 0.7rem; text-transform: uppercase; transition: box-shadow 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275); }
.box-score-header:hover, .box-score-header:focus, .box-score-header[aria-sort="ascending"], .box-score-header[aria-sort="descending"] { background-color: hsl(205, 100%, 36%); }
.box-score-header[aria-sort="ascending"] { box-shadow: inset 0 10px 0 -6px #efaa10; }
.box-score-header[aria-sort="descending"] { box-shadow: inset 0 -10px 0 -6px #efaa10; }
.sorted { background-color: hsla(0, 0%, 60%, 0.1); }
.box-score-total { font-size: 0.8125rem; font-weight: 500; }
```{css pkgdown_styles, include=FALSE} /* rmarkdown html documents */ .main-container { max-width: 1024px !important; } /* pkgdown articles */ .row > main { max-width: 1024px; }
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.