├── .Rhistory ├── .gitignore ├── README.md ├── gio ├── .Rbuildignore ├── .Rhistory ├── .gitignore ├── DESCRIPTION ├── NAMESPACE ├── R │ ├── data.R │ ├── gio.R │ └── utils-pipe.R ├── README.md ├── gio.Rproj ├── inst │ └── htmlwidgets │ │ ├── gio.js │ │ ├── gio.yaml │ │ └── lib │ │ ├── gio │ │ └── gio.min.js │ │ └── three │ │ └── three.min.js ├── man │ ├── gio-shiny.Rd │ ├── gio.Rd │ ├── pipe.Rd │ └── random_data.Rd └── shiny.R ├── playground ├── .Rbuildignore ├── .Rhistory ├── .gitignore ├── DESCRIPTION ├── NAMESPACE ├── R │ └── play.R ├── inst │ └── htmlwidgets │ │ ├── play.js │ │ └── play.yaml ├── man │ ├── play-shiny.Rd │ └── play.Rd └── playground.Rproj ├── typed ├── .Rbuildignore ├── .gitignore ├── DESCRIPTION ├── NAMESPACE ├── R │ └── typed.R ├── README.md ├── inst │ └── htmlwidgets │ │ ├── lib │ │ └── typed │ │ │ └── typed.min.js │ │ ├── typed.js │ │ └── typed.yaml └── typed.Rproj └── website ├── _navbar.html ├── _site.yml ├── _site ├── assets │ ├── css │ │ ├── materialize.min.css │ │ └── styles.css │ ├── img │ │ ├── laptop.png │ │ ├── server.jpg │ │ └── social.png │ └── js │ │ └── materialize.min.js ├── faq.html ├── favicon.ico ├── includes │ ├── footer.html │ └── header.html ├── index.html ├── instructor.html ├── material.html ├── presentation │ ├── dt.png │ ├── echarts4r.png │ ├── globe4r.png │ ├── grapher.png │ ├── index.Rmd │ ├── index.html │ ├── leaflet.png │ ├── libs │ │ ├── header-attrs-2.2 │ │ │ └── header-attrs.js │ │ └── remark-css-0.0.1 │ │ │ ├── default.css │ │ │ └── fc.css │ ├── plotly.png │ ├── sigmajs.png │ ├── style.css │ └── wordcloud2.png ├── program.html └── site_libs │ ├── bootstrap-3.3.5 │ ├── css │ │ ├── bootstrap-theme.css │ │ ├── bootstrap-theme.css.map │ │ ├── bootstrap-theme.min.css │ │ ├── bootstrap.css │ │ ├── bootstrap.css.map │ │ ├── bootstrap.min.css │ │ ├── cerulean.min.css │ │ ├── cosmo.min.css │ │ ├── darkly.min.css │ │ ├── flatly.min.css │ │ ├── fonts │ │ │ ├── Lato.ttf │ │ │ ├── LatoBold.ttf │ │ │ ├── LatoItalic.ttf │ │ │ ├── NewsCycle.ttf │ │ │ ├── NewsCycleBold.ttf │ │ │ ├── OpenSans.ttf │ │ │ ├── OpenSansBold.ttf │ │ │ ├── OpenSansBoldItalic.ttf │ │ │ ├── OpenSansItalic.ttf │ │ │ ├── OpenSansLight.ttf │ │ │ ├── OpenSansLightItalic.ttf │ │ │ ├── Raleway.ttf │ │ │ ├── RalewayBold.ttf │ │ │ ├── Roboto.ttf │ │ │ ├── RobotoBold.ttf │ │ │ ├── RobotoLight.ttf │ │ │ ├── RobotoMedium.ttf │ │ │ ├── SourceSansPro.ttf │ │ │ ├── SourceSansProBold.ttf │ │ │ ├── SourceSansProItalic.ttf │ │ │ ├── SourceSansProLight.ttf │ │ │ └── Ubuntu.ttf │ │ ├── journal.min.css │ │ ├── lumen.min.css │ │ ├── paper.min.css │ │ ├── readable.min.css │ │ ├── sandstone.min.css │ │ ├── simplex.min.css │ │ ├── spacelab.min.css │ │ ├── united.min.css │ │ └── yeti.min.css │ ├── fonts │ │ ├── glyphicons-halflings-regular.eot │ │ ├── glyphicons-halflings-regular.svg │ │ ├── glyphicons-halflings-regular.ttf │ │ ├── glyphicons-halflings-regular.woff │ │ └── glyphicons-halflings-regular.woff2 │ ├── js │ │ ├── bootstrap.js │ │ ├── bootstrap.min.js │ │ └── npm.js │ └── shim │ │ ├── html5shiv.min.js │ │ └── respond.min.js │ ├── header-attrs-2.2 │ └── header-attrs.js │ ├── highlightjs-9.12.0 │ ├── default.css │ ├── highlight.js │ └── textmate.css │ ├── jquery-1.11.3 │ └── jquery.min.js │ └── navigation-1.1 │ ├── codefolding.js │ ├── sourceembed.js │ └── tabsets.js ├── assets ├── css │ ├── materialize.min.css │ └── styles.css ├── img │ ├── laptop.png │ ├── server.jpg │ └── social.png └── js │ └── materialize.min.js ├── faq.Rmd ├── favicon.ico ├── includes ├── footer.html └── header.html ├── index.Rmd ├── instructor.Rmd ├── material.Rmd ├── presentation ├── dt.png ├── echarts4r.png ├── globe4r.png ├── grapher.png ├── index.Rmd ├── index.html ├── leaflet.png ├── libs │ ├── header-attrs-2.2 │ │ └── header-attrs.js │ └── remark-css-0.0.1 │ │ ├── default.css │ │ └── fc.css ├── plotly.png ├── sigmajs.png ├── style.css └── wordcloud2.png └── program.Rmd /.Rhistory: -------------------------------------------------------------------------------- 1 | usethis::create_package("playground") 2 | getwd() 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .Rproj.user 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 5 |

6 | 7 | ### [Website](http://htmlwidgets.john-coene.com/) | [FAQ](https://htmlwidgets.john-coene.com/faq.html) | [Material](https://htmlwidgets.john-coene.com/content.html) | [Prerequisites](https://htmlwidgets.john-coene.com/req.html) 8 | 9 | Repository for the "How to build htmlwidgets" e-Rum 2020 workshop to be held on June 20. 10 | 11 | ### [Book](https://javascript-for-r.com/) 12 | 13 | The material presented during the workshop (and more) is now available in book form: [JavaScript for R](https://javascript-for-r.com/) 14 | 15 |
16 | -------------------------------------------------------------------------------- /gio/.Rbuildignore: -------------------------------------------------------------------------------- 1 | ^gio\.Rproj$ 2 | ^\.Rproj\.user$ 3 | -------------------------------------------------------------------------------- /gio/.Rhistory: -------------------------------------------------------------------------------- 1 | system.file("htmlwidgets", package = "gio") 2 | devtools::document() 3 | devtools::load_all() 4 | gio("hello") 5 | devtools::load_all() 6 | devtools::load_all() 7 | gio("useless") 8 | gio("useless", elementId = "globalArea") 9 | devtools::load_all() 10 | gio("useless") 11 | jsonlite::fromJSON('[ 12 | { 13 | "e": "CN", 14 | "i": "US", 15 | "v": 3300000 16 | }, 17 | { 18 | "e": "CN", 19 | "i": "RU", 20 | "v": 10000 21 | } 22 | ]') 23 | jsonlite::fromJSON('[ 24 | { 25 | "e": "CN", 26 | "i": "US", 27 | "v": 3300000 28 | }, 29 | { 30 | "e": "CN", 31 | "i": "RU", 32 | "v": 10000 33 | } 34 | ]') -> x 35 | class(x) 36 | jsonlite::toJSON(x) 37 | jsonlite::toJSON(x, pretty = TRUE) 38 | apply(x, 1, as.list) 39 | x 40 | devtools::load_all() 41 | x 42 | df <- random_data() 43 | head(df) 44 | gio(x) 45 | jsonlite::toJSON(x) 46 | jsonlite::toJSON(x) 47 | ?jsonlite::toJSON(x) 48 | jsonlite::toJSON(x, dataframe = "rows") 49 | jsonlite::toJSON(x, dataframe = "columns") 50 | head(cars) 51 | attr(cars, "hello") <- "world" 52 | head(cars) 53 | attributes(cars) 54 | det 55 | devtools::load_all() 56 | x 57 | x 58 | gio(x) 59 | dd <- random_data(15) 60 | gio(dd) 61 | dd <- random_data() 62 | gio(dd) 63 | dd <- random_data(1000) 64 | gio(dd) 65 | dd <- random_data(1000) 66 | gio(dd) 67 | globe <- gio(dd) 68 | class()globe 69 | class(globe) 70 | globe 71 | str(globe) 72 | globe$x 73 | globe$x$style <- "magic" 74 | globe <- gio(dd) 75 | devtools::document() 76 | devtools::load_all() 77 | globe <- gio(dd) 78 | globe 79 | set_style(globe, "magic") 80 | devtools::load_all() 81 | devtools::load_all() 82 | globe <- gio(dd) 83 | globe 84 | set_style(globe, "magic") 85 | usethis::use_pipe() 86 | devtools::document() 87 | devtools::load_all() 88 | gio(dd) %>% 89 | set_style("gorgeousDream") 90 | ?shiny::onStop() 91 | globe <- gio(dd) 92 | head(globe$x$data) 93 | library(shiny); runApp('shiny.R') 94 | input$selected_country 95 | devtools::load_all() 96 | runApp('shiny.R') 97 | devtools::load_all() 98 | runApp('shiny.R') 99 | htmlwidgets::shinyRenderWidget 100 | globe <- gio(dd) 101 | globe$x$style 102 | ?htmlwidgets::createWidget 103 | ?htmlwidgets::sizingPolicy() 104 | gio(dd) 105 | devtools::load_all() 106 | gio(dd) 107 | gio(dd) %>% gio_style("error") 108 | gio(dd) %>% set_style("error") 109 | gio(dd) %>% set_style("default") 110 | -------------------------------------------------------------------------------- /gio/.gitignore: -------------------------------------------------------------------------------- 1 | .Rproj.user 2 | -------------------------------------------------------------------------------- /gio/DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: gio 2 | Title: What the Package Does (One Line, Title Case) 3 | Version: 0.0.0.9000 4 | Authors@R: 5 | person(given = "First", 6 | family = "Last", 7 | role = c("aut", "cre"), 8 | email = "first.last@example.com", 9 | comment = c(ORCID = "YOUR-ORCID-ID")) 10 | Description: What the package does (one paragraph). 11 | License: `use_mit_license()`, `use_gpl3_license()` or friends to 12 | pick a license 13 | Encoding: UTF-8 14 | LazyData: true 15 | Roxygen: list(markdown = TRUE) 16 | RoxygenNote: 7.1.0 17 | Imports: 18 | magrittr 19 | -------------------------------------------------------------------------------- /gio/NAMESPACE: -------------------------------------------------------------------------------- 1 | # Generated by roxygen2: do not edit by hand 2 | 3 | export("%>%") 4 | export(gio) 5 | export(gioOutput) 6 | export(random_data) 7 | export(renderGio) 8 | export(set_style) 9 | import(htmlwidgets) 10 | importFrom(magrittr,"%>%") 11 | -------------------------------------------------------------------------------- /gio/R/data.R: -------------------------------------------------------------------------------- 1 | #' Random data generator 2 | #' 3 | #' @param n Number of rows. 4 | #' 5 | #' @export 6 | random_data <- function(n = 100){ 7 | # values 8 | values <- runif(n, 10^5, 10^7) 9 | values <- round(values) 10 | 11 | # edges 12 | import <- sample(iso2, n, replace = TRUE) 13 | export <- sample(iso2, n, replace = TRUE) 14 | 15 | df <- data.frame( 16 | e = export, 17 | i = import, 18 | v = values, 19 | stringsAsFactors = FALSE 20 | ) 21 | 22 | subset(df, df$e != df$i) 23 | } 24 | 25 | iso2 <- c("PE", "BF", "FR", "LY", "BY", "PK", "ID", "YE", "MG", "BO", 26 | "CI", "DZ", "CH", "CM", "MK", "BW", "UA", "KE", "TW", "JO", "MX", 27 | "AE", "BZ", "BR", "SL", "ML", "CD", "IT", "SO", "AF", "BD", "DO", 28 | "GW", "GH", "AT", "SE", "TR", "UG", "MZ", "JP", "NZ", "CU", "VE", 29 | "PT", "CO", "MR", "AO", "DE", "SD", "TH", "AU", "PG", "IQ", "HR", 30 | "GL", "NE", "DK", "LV", "RO", "ZM", "IR", "MM", "ET", "GT", "SR", 31 | "EH", "CZ", "TD", "AL", "FI", "SY", "KG", "SB", "OM", "PA", "AR", 32 | "GB", "CR", "PY", "GN", "IE", "NG", "TN", "PL", "NA", "ZA", "EG", 33 | "TZ", "GE", "SA", "VN", "RU", "HT", "BA", "IN", "CN", "CA", "SV", 34 | "GY", "BE", "GQ", "LS", "BG", "BI", "DJ", "AZ", "MY", "PH", "UY", 35 | "CG", "RS", "ME", "EE", "RW", "AM", "SN", "TG", "ES", "GA", "HU", 36 | "MW", "TJ", "KH", "KR", "HN", "IS", "NI", "CL", "MA", "LR", "NL", 37 | "CF", "SK", "LT", "ZW", "LK", "IL", "LA", "KP", "GR", "TM", "EC", 38 | "BJ", "SI", "NO", "MD", "LB", "NP", "ER", "US", "KZ", "AQ", "SZ", 39 | "UZ", "MN", "BT", "NC", "FJ", "KW", "TL", "BS", "VU", "FK", "GM", 40 | "QA", "JM", "CY", "PR", "PS", "BN", "TT", "CV", "PF", "WS", "LU", 41 | "KM", "MU", "FO", "ST", "DM", "TO", "KI", "FM", "BH", "AD", "MP", 42 | "PW", "SC", "AG", "BB", "TC", "VC", "LC", "YT", "VI", "GD", "MT", 43 | "MV", "KY", "KN", "MS", "BL", "NU", "PM", "CK", "WF", "AS", "MH", 44 | "AW", "LI", "VG", "SH", "JE", "AI", "GG", "SM", "BM", "TV", "NR", 45 | "GI", "PN", "MC", "VA", "IM", "GU", "SG") 46 | -------------------------------------------------------------------------------- /gio/R/gio.R: -------------------------------------------------------------------------------- 1 | #' 2 | #' 3 | #' 4 | #' 5 | #' @import htmlwidgets 6 | #' 7 | #' @export 8 | gio <- function(data, width = NULL, height = NULL, elementId = NULL) { 9 | 10 | # forward options using x 11 | x = list( 12 | data = data 13 | ) 14 | 15 | attr(x, 'TOJSON_ARGS') <- list(dataframe = "rows") 16 | 17 | # create widget 18 | htmlwidgets::createWidget( 19 | name = 'gio', 20 | x, 21 | width = width, 22 | height = height, 23 | package = 'gio', 24 | elementId = elementId, 25 | sizingPolicy = htmlwidgets::sizingPolicy( 26 | defaultWidth = "100%", 27 | browser.fill = TRUE, 28 | padding = 0 29 | ) 30 | ) 31 | } 32 | 33 | #' @export 34 | set_style <- function(globe, style = "magic"){ 35 | globe$x$style <- style 36 | return(globe) 37 | } 38 | 39 | #' Shiny bindings for gio 40 | #' 41 | #' Output and render functions for using gio within Shiny 42 | #' applications and interactive Rmd documents. 43 | #' 44 | #' @param outputId output variable to read from 45 | #' @param width,height Must be a valid CSS unit (like \code{'100\%'}, 46 | #' \code{'400px'}, \code{'auto'}) or a number, which will be coerced to a 47 | #' string and have \code{'px'} appended. 48 | #' @param expr An expression that generates a gio 49 | #' @param env The environment in which to evaluate \code{expr}. 50 | #' @param quoted Is \code{expr} a quoted expression (with \code{quote()})? This 51 | #' is useful if you want to save an expression in a variable. 52 | #' 53 | #' @name gio-shiny 54 | #' 55 | #' @export 56 | gioOutput <- function(outputId, width = '100%', height = '400px'){ 57 | htmlwidgets::shinyWidgetOutput(outputId, 'gio', width, height, package = 'gio') 58 | } 59 | 60 | #' @rdname gio-shiny 61 | #' @export 62 | renderGio <- function(expr, env = parent.frame(), quoted = FALSE) { 63 | if (!quoted) { expr <- substitute(expr) } # force quoted 64 | htmlwidgets::shinyRenderWidget(expr, gioOutput, env, quoted = TRUE) 65 | } 66 | -------------------------------------------------------------------------------- /gio/R/utils-pipe.R: -------------------------------------------------------------------------------- 1 | #' Pipe operator 2 | #' 3 | #' See \code{magrittr::\link[magrittr:pipe]{\%>\%}} for details. 4 | #' 5 | #' @name %>% 6 | #' @rdname pipe 7 | #' @keywords internal 8 | #' @export 9 | #' @importFrom magrittr %>% 10 | #' @usage lhs \%>\% rhs 11 | NULL 12 | -------------------------------------------------------------------------------- /gio/README.md: -------------------------------------------------------------------------------- 1 | 2 | # gio 3 | 4 | 5 | 6 | 7 | [gio.js](https://giojs.org/) htmlwidget 8 | 9 | ## History 10 | 11 | ```r 12 | # scaffold widget 13 | htmlwidgets::scaffoldWidget("gio") 14 | 15 | # create directories for JS dependency 16 | dir.create("./inst/htmlwidgets/lib/three", recursive = TRUE) 17 | dir.create("./inst/htmlwidgets/lib/gio", recursive = TRUE) 18 | 19 | # download JS dependency 20 | three <- "https://cdnjs.cloudflare.com/ajax/libs/three.js/110/three.min.js" 21 | gio <- "https://raw.githubusercontent.com/syt123450/giojs/master/build/gio.min.js" 22 | 23 | download.file(three, "./inst/htmlwidgets/lib/three/three.min.js") 24 | download.file(gio, "./inst/htmlwidgets/lib/gio/gio.min.js") 25 | ``` 26 | 27 | File `inst/lib/gio.yaml` edited to look like: 28 | 29 | ```yaml 30 | dependencies: 31 | - name: three 32 | version: 110 33 | src: htmlwidgets/lib/three 34 | script: three.min.js 35 | - name: gio 36 | version: 2.0 37 | src: htmlwidgets/lib/gio 38 | script: gio.min.js 39 | ``` 40 | 41 | A convenience function called `random_data` is exported from `R/data.R`, this is so we can easily generate data and test the widget as we build it. 42 | 43 | ```r 44 | random_data(n = 100) 45 | ``` 46 | -------------------------------------------------------------------------------- /gio/gio.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 | -------------------------------------------------------------------------------- /gio/inst/htmlwidgets/gio.js: -------------------------------------------------------------------------------- 1 | HTMLWidgets.widget({ 2 | 3 | name: 'gio', 4 | 5 | type: 'output', 6 | 7 | factory: function(el, width, height) { 8 | 9 | // TODO: define shared variables for this instance 10 | var controller; 11 | 12 | return { 13 | 14 | renderValue: function(x) { 15 | 16 | // TODO: code to render the widget, e.g. 17 | var container = document.getElementById(el.id); 18 | controller = new GIO.Controller( container ); 19 | controller.addData(x.data); 20 | controller.setStyle(x.style); 21 | 22 | // callback 23 | function callback ( selectedCountry, relatedCountries ) { 24 | Shiny.setInputValue(el.id + "_selected_country", selectedCountry); 25 | } 26 | 27 | controller.onCountryPicked( callback ); 28 | 29 | controller.init(); 30 | 31 | }, 32 | 33 | resize: function(width, height) { 34 | 35 | // TODO: code to re-render the widget with a new size 36 | controller.resizeUpdate(); 37 | 38 | } 39 | 40 | }; 41 | } 42 | }); 43 | -------------------------------------------------------------------------------- /gio/inst/htmlwidgets/gio.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - name: three 3 | version: 110 4 | src: htmlwidgets/lib/three 5 | script: three.min.js 6 | - name: gio 7 | version: 2.0 8 | src: htmlwidgets/lib/gio 9 | script: gio.min.js 10 | -------------------------------------------------------------------------------- /gio/man/gio-shiny.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/gio.R 3 | \name{gio-shiny} 4 | \alias{gio-shiny} 5 | \alias{gioOutput} 6 | \alias{renderGio} 7 | \title{Shiny bindings for gio} 8 | \usage{ 9 | gioOutput(outputId, width = "100\%", height = "400px") 10 | 11 | renderGio(expr, env = parent.frame(), quoted = FALSE) 12 | } 13 | \arguments{ 14 | \item{outputId}{output variable to read from} 15 | 16 | \item{width, height}{Must be a valid CSS unit (like \code{'100\%'}, 17 | \code{'400px'}, \code{'auto'}) or a number, which will be coerced to a 18 | string and have \code{'px'} appended.} 19 | 20 | \item{expr}{An expression that generates a gio} 21 | 22 | \item{env}{The environment in which to evaluate \code{expr}.} 23 | 24 | \item{quoted}{Is \code{expr} a quoted expression (with \code{quote()})? This 25 | is useful if you want to save an expression in a variable.} 26 | } 27 | \description{ 28 | Output and render functions for using gio within Shiny 29 | applications and interactive Rmd documents. 30 | } 31 | -------------------------------------------------------------------------------- /gio/man/gio.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/gio.R 3 | \name{gio} 4 | \alias{gio} 5 | \title{} 6 | \usage{ 7 | gio(data, width = NULL, height = NULL, elementId = NULL) 8 | } 9 | \description{ 10 | 11 | } 12 | -------------------------------------------------------------------------------- /gio/man/pipe.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/utils-pipe.R 3 | \name{\%>\%} 4 | \alias{\%>\%} 5 | \title{Pipe operator} 6 | \usage{ 7 | lhs \%>\% rhs 8 | } 9 | \description{ 10 | See \code{magrittr::\link[magrittr:pipe]{\%>\%}} for details. 11 | } 12 | \keyword{internal} 13 | -------------------------------------------------------------------------------- /gio/man/random_data.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/data.R 3 | \name{random_data} 4 | \alias{random_data} 5 | \title{Random data generator} 6 | \usage{ 7 | random_data(n = 100) 8 | } 9 | \arguments{ 10 | \item{n}{Number of rows.} 11 | } 12 | \description{ 13 | Random data generator 14 | } 15 | -------------------------------------------------------------------------------- /gio/shiny.R: -------------------------------------------------------------------------------- 1 | library(shiny) 2 | 3 | ui <- fluidPage( 4 | gioOutput("globe2"), 5 | verbatimTextOutput("selected") 6 | ) 7 | 8 | server <- function(input, output, session) { 9 | 10 | output$globe2 <- renderGio({ 11 | random_data(1000) %>% 12 | gio() 13 | }) 14 | 15 | output$selected <- renderPrint({ 16 | print(input$globe2_selected_country) 17 | }) 18 | 19 | } 20 | 21 | shinyApp(ui, server) 22 | -------------------------------------------------------------------------------- /playground/.Rbuildignore: -------------------------------------------------------------------------------- 1 | ^playground\.Rproj$ 2 | ^\.Rproj\.user$ 3 | -------------------------------------------------------------------------------- /playground/.Rhistory: -------------------------------------------------------------------------------- 1 | htmlwidgets::scaffoldWidget("play") 2 | -------------------------------------------------------------------------------- /playground/.gitignore: -------------------------------------------------------------------------------- 1 | .Rproj.user 2 | -------------------------------------------------------------------------------- /playground/DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: playground 2 | Title: What the Package Does (One Line, Title Case) 3 | Version: 0.0.0.9000 4 | Authors@R: 5 | person(given = "First", 6 | family = "Last", 7 | role = c("aut", "cre"), 8 | email = "first.last@example.com", 9 | comment = c(ORCID = "YOUR-ORCID-ID")) 10 | Description: What the package does (one paragraph). 11 | License: `use_mit_license()`, `use_gpl3_license()` or friends to 12 | pick a license 13 | Encoding: UTF-8 14 | LazyData: true 15 | Roxygen: list(markdown = TRUE) 16 | RoxygenNote: 7.1.0 17 | -------------------------------------------------------------------------------- /playground/NAMESPACE: -------------------------------------------------------------------------------- 1 | # Generated by roxygen2: do not edit by hand 2 | 3 | export(play) 4 | export(playOutput) 5 | export(renderPlay) 6 | import(htmlwidgets) 7 | -------------------------------------------------------------------------------- /playground/R/play.R: -------------------------------------------------------------------------------- 1 | #' 2 | #' 3 | #' 4 | #' 5 | #' @import htmlwidgets 6 | #' 7 | #' @export 8 | play <- function(message, width = NULL, height = NULL, elementId = NULL) { 9 | 10 | # forward options using x 11 | x = list( 12 | message = as.character(message) 13 | ) 14 | 15 | # create widget 16 | htmlwidgets::createWidget( 17 | name = 'play', 18 | x, 19 | width = width, 20 | height = height, 21 | package = 'playground', 22 | elementId = elementId 23 | ) 24 | } 25 | 26 | #' Shiny bindings for play 27 | #' 28 | #' Output and render functions for using play within Shiny 29 | #' applications and interactive Rmd documents. 30 | #' 31 | #' @param outputId output variable to read from 32 | #' @param width,height Must be a valid CSS unit (like \code{'100\%'}, 33 | #' \code{'400px'}, \code{'auto'}) or a number, which will be coerced to a 34 | #' string and have \code{'px'} appended. 35 | #' @param expr An expression that generates a play 36 | #' @param env The environment in which to evaluate \code{expr}. 37 | #' @param quoted Is \code{expr} a quoted expression (with \code{quote()})? This 38 | #' is useful if you want to save an expression in a variable. 39 | #' 40 | #' @name play-shiny 41 | #' 42 | #' @export 43 | playOutput <- function(outputId, width = '100%', height = '400px'){ 44 | htmlwidgets::shinyWidgetOutput(outputId, 'play', width, height, package = 'playground') 45 | } 46 | 47 | #' @rdname play-shiny 48 | #' @export 49 | renderPlay <- function(expr, env = parent.frame(), quoted = FALSE) { 50 | if (!quoted) { expr <- substitute(expr) } # force quoted 51 | htmlwidgets::shinyRenderWidget(expr, playOutput, env, quoted = TRUE) 52 | } 53 | -------------------------------------------------------------------------------- /playground/inst/htmlwidgets/play.js: -------------------------------------------------------------------------------- 1 | HTMLWidgets.widget({ 2 | 3 | name: 'play', 4 | 5 | type: 'output', 6 | 7 | factory: function(el, width, height) { 8 | 9 | // TODO: define shared variables for this instance 10 | 11 | return { 12 | 13 | renderValue: function(x) { 14 | 15 | console.log(el.id); 16 | 17 | // TODO: code to render the widget, e.g. 18 | el.innerHTML = x.message; 19 | 20 | }, 21 | 22 | resize: function(width, height) { 23 | 24 | // TODO: code to re-render the widget with a new size 25 | 26 | } 27 | 28 | }; 29 | } 30 | }); -------------------------------------------------------------------------------- /playground/inst/htmlwidgets/play.yaml: -------------------------------------------------------------------------------- 1 | # (uncomment to add a dependency) 2 | # dependencies: 3 | # - name: 4 | # version: 5 | # src: 6 | # script: 7 | # stylesheet: 8 | -------------------------------------------------------------------------------- /playground/man/play-shiny.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/play.R 3 | \name{play-shiny} 4 | \alias{play-shiny} 5 | \alias{playOutput} 6 | \alias{renderPlay} 7 | \title{Shiny bindings for play} 8 | \usage{ 9 | playOutput(outputId, width = "100\%", height = "400px") 10 | 11 | renderPlay(expr, env = parent.frame(), quoted = FALSE) 12 | } 13 | \arguments{ 14 | \item{outputId}{output variable to read from} 15 | 16 | \item{width, height}{Must be a valid CSS unit (like \code{'100\%'}, 17 | \code{'400px'}, \code{'auto'}) or a number, which will be coerced to a 18 | string and have \code{'px'} appended.} 19 | 20 | \item{expr}{An expression that generates a play} 21 | 22 | \item{env}{The environment in which to evaluate \code{expr}.} 23 | 24 | \item{quoted}{Is \code{expr} a quoted expression (with \code{quote()})? This 25 | is useful if you want to save an expression in a variable.} 26 | } 27 | \description{ 28 | Output and render functions for using play within Shiny 29 | applications and interactive Rmd documents. 30 | } 31 | -------------------------------------------------------------------------------- /playground/man/play.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/play.R 3 | \name{play} 4 | \alias{play} 5 | \title{} 6 | \usage{ 7 | play(message, width = NULL, height = NULL, elementId = NULL) 8 | } 9 | \description{ 10 | 11 | } 12 | -------------------------------------------------------------------------------- /playground/playground.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 | -------------------------------------------------------------------------------- /typed/.Rbuildignore: -------------------------------------------------------------------------------- 1 | ^typed\.Rproj$ 2 | ^\.Rproj\.user$ 3 | -------------------------------------------------------------------------------- /typed/.gitignore: -------------------------------------------------------------------------------- 1 | .Rproj.user 2 | -------------------------------------------------------------------------------- /typed/DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: typed 2 | Title: What the Package Does (One Line, Title Case) 3 | Version: 0.0.0.9000 4 | Authors@R: 5 | person(given = "First", 6 | family = "Last", 7 | role = c("aut", "cre"), 8 | email = "first.last@example.com", 9 | comment = c(ORCID = "YOUR-ORCID-ID")) 10 | Description: What the package does (one paragraph). 11 | License: `use_mit_license()`, `use_gpl3_license()` or friends to 12 | pick a license 13 | Encoding: UTF-8 14 | LazyData: true 15 | Roxygen: list(markdown = TRUE) 16 | RoxygenNote: 7.1.0 17 | -------------------------------------------------------------------------------- /typed/NAMESPACE: -------------------------------------------------------------------------------- 1 | # Generated by roxygen2: do not edit by hand 2 | 3 | -------------------------------------------------------------------------------- /typed/R/typed.R: -------------------------------------------------------------------------------- 1 | #' 2 | #' 3 | #' 4 | #' 5 | #' @import htmlwidgets 6 | #' 7 | #' @export 8 | typed <- function(message, width = NULL, height = NULL, elementId = NULL) { 9 | 10 | # forward options using x 11 | x = list( 12 | message = message 13 | ) 14 | 15 | # create widget 16 | htmlwidgets::createWidget( 17 | name = 'typed', 18 | x, 19 | width = width, 20 | height = height, 21 | package = 'typed', 22 | elementId = elementId 23 | ) 24 | } 25 | 26 | #' Shiny bindings for typed 27 | #' 28 | #' Output and render functions for using typed within Shiny 29 | #' applications and interactive Rmd documents. 30 | #' 31 | #' @param outputId output variable to read from 32 | #' @param width,height Must be a valid CSS unit (like \code{'100\%'}, 33 | #' \code{'400px'}, \code{'auto'}) or a number, which will be coerced to a 34 | #' string and have \code{'px'} appended. 35 | #' @param expr An expression that generates a typed 36 | #' @param env The environment in which to evaluate \code{expr}. 37 | #' @param quoted Is \code{expr} a quoted expression (with \code{quote()})? This 38 | #' is useful if you want to save an expression in a variable. 39 | #' 40 | #' @name typed-shiny 41 | #' 42 | #' @export 43 | typedOutput <- function(outputId, width = '100%', height = '400px'){ 44 | htmlwidgets::shinyWidgetOutput(outputId, 'typed', width, height, package = 'typed') 45 | } 46 | 47 | #' @rdname typed-shiny 48 | #' @export 49 | renderTyped <- function(expr, env = parent.frame(), quoted = FALSE) { 50 | if (!quoted) { expr <- substitute(expr) } # force quoted 51 | htmlwidgets::shinyRenderWidget(expr, typedOutput, env, quoted = TRUE) 52 | } 53 | -------------------------------------------------------------------------------- /typed/README.md: -------------------------------------------------------------------------------- 1 | 2 | # typed 3 | 4 | 5 | 6 | 7 | [typed.js](https://github.com/mattboldt/typed.js) as htmlwidget 8 | 9 | # History 10 | 11 | What happened in this repository? 12 | 13 | ```r 14 | # scaffold widget 15 | htmlwidgets::scaffoldWidget("typed") 16 | 17 | # create directory for JS dependency 18 | path <- "./inst/htmlwidgets/lib/typed" 19 | dir.create(path, recursive = TRUE) 20 | 21 | # download JS dependency 22 | dep <- "https://cdn.jsdelivr.net/npm/typed.js@2.0.11" 23 | download.file(dep, paste0(path, "/typed.min.js")) 24 | ``` 25 | 26 | File `inst/lib/playground.yaml` edited to look like: 27 | 28 | ```yaml 29 | dependencies: 30 | - name: typed 31 | version: 2.0.11 32 | src: htmlwidgets/lib/typed 33 | script: typed.min.js 34 | ``` -------------------------------------------------------------------------------- /typed/inst/htmlwidgets/lib/typed/typed.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * 3 | * typed.js - A JavaScript Typing Animation Library 4 | * Author: Matt Boldt 5 | * Version: v2.0.11 6 | * Url: https://github.com/mattboldt/typed.js 7 | * License(s): MIT 8 | * 9 | */ 10 | (function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.Typed=e():t.Typed=e()})(this,function(){return function(t){function e(n){if(s[n])return s[n].exports;var i=s[n]={exports:{},id:n,loaded:!1};return t[n].call(i.exports,i,i.exports,e),i.loaded=!0,i.exports}var s={};return e.m=t,e.c=s,e.p="",e(0)}([function(t,e,s){"use strict";function n(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(e,"__esModule",{value:!0});var i=function(){function t(t,e){for(var s=0;st.length)););var u=t.substring(0,e),l=t.substring(u.length+1,e+i),c=t.substring(e+i+1);t=u+l+c,i--}s.timeout=setTimeout(function(){s.toggleBlinking(!1),e>=t.length?s.doneTyping(t,e):s.keepTyping(t,e,i),s.temporaryPause&&(s.temporaryPause=!1,s.options.onTypingResumed(s.arrayPos,s))},n)},n))}},{key:"keepTyping",value:function(t,e,s){0===e&&(this.toggleBlinking(!1),this.options.preStringTyped(this.arrayPos,this)),e+=s;var n=t.substr(0,e);this.replaceText(n),this.typewrite(t,e)}},{key:"doneTyping",value:function(t,e){var s=this;this.options.onStringTyped(this.arrayPos,this),this.toggleBlinking(!0),this.arrayPos===this.strings.length-1&&(this.complete(),this.loop===!1||this.curLoop===this.loopCount)||(this.timeout=setTimeout(function(){s.backspace(t,e)},this.backDelay))}},{key:"backspace",value:function(t,e){var s=this;if(this.pause.status===!0)return void this.setPauseStatus(t,e,!0);if(this.fadeOut)return this.initFadeOut();this.toggleBlinking(!1);var n=this.humanizer(this.backSpeed);this.timeout=setTimeout(function(){e=o.htmlParser.backSpaceHtmlChars(t,e,s);var n=t.substr(0,e);if(s.replaceText(n),s.smartBackspace){var i=s.strings[s.arrayPos+1];i&&n===i.substr(0,e)?s.stopNum=e:s.stopNum=0}e>s.stopNum?(e--,s.backspace(t,e)):e<=s.stopNum&&(s.arrayPos++,s.arrayPos===s.strings.length?(s.arrayPos=0,s.options.onLastStringBackspaced(),s.shuffleStringsIfNeeded(),s.begin()):s.typewrite(s.strings[s.sequence[s.arrayPos]],e))},n)}},{key:"complete",value:function(){this.options.onComplete(this),this.loop?this.curLoop++:this.typingComplete=!0}},{key:"setPauseStatus",value:function(t,e,s){this.pause.typewrite=s,this.pause.curString=t,this.pause.curStrPos=e}},{key:"toggleBlinking",value:function(t){this.cursor&&(this.pause.status||this.cursorBlinking!==t&&(this.cursorBlinking=t,t?this.cursor.classList.add("typed-cursor--blink"):this.cursor.classList.remove("typed-cursor--blink")))}},{key:"humanizer",value:function(t){return Math.round(Math.random()*t/2)+t}},{key:"shuffleStringsIfNeeded",value:function(){this.shuffle&&(this.sequence=this.sequence.sort(function(){return Math.random()-.5}))}},{key:"initFadeOut",value:function(){var t=this;return this.el.className+=" "+this.fadeOutClass,this.cursor&&(this.cursor.className+=" "+this.fadeOutClass),setTimeout(function(){t.arrayPos++,t.replaceText(""),t.strings.length>t.arrayPos?t.typewrite(t.strings[t.sequence[t.arrayPos]],0):(t.typewrite(t.strings[0],0),t.arrayPos=0)},this.fadeOutDelay)}},{key:"replaceText",value:function(t){this.attr?this.el.setAttribute(this.attr,t):this.isInput?this.el.value=t:"html"===this.contentType?this.el.innerHTML=t:this.el.textContent=t}},{key:"bindFocusEvents",value:function(){var t=this;this.isInput&&(this.el.addEventListener("focus",function(e){t.stop()}),this.el.addEventListener("blur",function(e){t.el.value&&0!==t.el.value.length||t.start()}))}},{key:"insertCursor",value:function(){this.showCursor&&(this.cursor||(this.cursor=document.createElement("span"),this.cursor.className="typed-cursor",this.cursor.innerHTML=this.cursorChar,this.el.parentNode&&this.el.parentNode.insertBefore(this.cursor,this.el.nextSibling)))}}]),t}();e["default"]=a,t.exports=e["default"]},function(t,e,s){"use strict";function n(t){return t&&t.__esModule?t:{"default":t}}function i(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(e,"__esModule",{value:!0});var r=Object.assign||function(t){for(var e=1;e":";";t.substr(e+1).charAt(0)!==i&&(e++,!(e+1>t.length)););e++}return e}},{key:"backSpaceHtmlChars",value:function(t,e,s){if("html"!==s.contentType)return e;var n=t.substr(e).charAt(0);if(">"===n||";"===n){var i="";for(i=">"===n?"<":"&";t.substr(e-1).charAt(0)!==i&&(e--,!(e<0)););e--}return e}}]),t}();e["default"]=i;var r=new i;e.htmlParser=r}])}); 11 | //# sourceMappingURL=typed.min.js.map 12 | -------------------------------------------------------------------------------- /typed/inst/htmlwidgets/typed.js: -------------------------------------------------------------------------------- 1 | HTMLWidgets.widget({ 2 | 3 | name: 'typed', 4 | 5 | type: 'output', 6 | 7 | factory: function(el, width, height) { 8 | 9 | // TODO: define shared variables for this instance 10 | 11 | return { 12 | 13 | renderValue: function(x) { 14 | 15 | // TODO: code to render the widget, e.g. 16 | el.innerText = x.message; 17 | 18 | }, 19 | 20 | resize: function(width, height) { 21 | 22 | // TODO: code to re-render the widget with a new size 23 | 24 | } 25 | 26 | }; 27 | } 28 | }); -------------------------------------------------------------------------------- /typed/inst/htmlwidgets/typed.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - name: typed 3 | version: 2.0.11 4 | src: htmlwidgets/lib/typed 5 | script: typed.min.js 6 | -------------------------------------------------------------------------------- /typed/typed.Rproj: -------------------------------------------------------------------------------- 1 | Version: 1.0 2 | 3 | RestoreWorkspace: No 4 | SaveWorkspace: No 5 | AlwaysSaveHistory: Default 6 | 7 | EnableCodeIndexing: Yes 8 | Encoding: UTF-8 9 | 10 | AutoAppendNewline: Yes 11 | StripTrailingWhitespace: Yes 12 | LineEndingConversion: Posix 13 | 14 | BuildType: Package 15 | PackageUseDevtools: Yes 16 | PackageInstallArgs: --no-multiarch --with-keep.source 17 | PackageRoxygenize: rd,collate,namespace 18 | -------------------------------------------------------------------------------- /website/_navbar.html: -------------------------------------------------------------------------------- 1 | 25 | -------------------------------------------------------------------------------- /website/_site.yml: -------------------------------------------------------------------------------- 1 | name: "How to build htmlwidgets" 2 | output: 3 | html_document: 4 | theme: cosmo 5 | highlight: textmate 6 | include: 7 | in_header: includes/header.html 8 | after_body: includes/footer.html 9 | css: assets/css/styles.css 10 | -------------------------------------------------------------------------------- /website/_site/assets/css/styles.css: -------------------------------------------------------------------------------- 1 | /* override shitty bootstrap: I'm not writing a new generator */ 2 | 3 | body{ 4 | padding: 0px !important; 5 | margin: 0px !important; 6 | } 7 | 8 | .main-container{ 9 | margin: 0px !important; 10 | max-width: none !important; 11 | } 12 | 13 | .container-fluid{ 14 | padding: 0px !important; 15 | } 16 | 17 | #header{ 18 | visibility: hidden; 19 | height: 0px; 20 | } 21 | 22 | /* style */ 23 | @import url('https://fonts.googleapis.com/css2?family=Raleway:wght@200;500&display=swap'); 24 | 25 | body{ 26 | font-family: 'Raleway', sans-serif; 27 | } -------------------------------------------------------------------------------- /website/_site/assets/img/laptop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnCoene/how-to-build-htmlwidgets/2c185c87559e818f719729661f8d9d33154fc8cf/website/_site/assets/img/laptop.png -------------------------------------------------------------------------------- /website/_site/assets/img/server.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnCoene/how-to-build-htmlwidgets/2c185c87559e818f719729661f8d9d33154fc8cf/website/_site/assets/img/server.jpg -------------------------------------------------------------------------------- /website/_site/assets/img/social.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnCoene/how-to-build-htmlwidgets/2c185c87559e818f719729661f8d9d33154fc8cf/website/_site/assets/img/social.png -------------------------------------------------------------------------------- /website/_site/faq.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | FAQ 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | htmlwidgets workshop 53 | 54 | 55 | 56 | 63 | 64 | 65 | 70 | 79 | 80 | 81 | 82 | 108 | 109 | 110 | 111 | 112 | 113 | 139 | 140 | 141 | 210 | 211 | 228 | 229 | 230 | 231 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 |
297 | 298 | 299 | 300 | 324 | 325 | 332 | 333 | 334 |
335 |

336 | FAQ 337 |

338 |
339 |

Who is this for?

340 |

R programmers interested in visualisation with little knowledge of JavaScript.

341 |
342 |
343 |

Do I need to know JavaScript?

344 |

No.

345 |
346 |
347 |

Are we going to learn JavaScript?

348 |

No. 

349 |
350 |
351 |

But we are going to write a lot of JavaScript, aren’t we?

352 |

No, we’ll write very little JavaScript.

353 |
354 |
355 |

What level of R programming is required?

356 |

You need to be familiar with the language and its data structures, some experience in package development and understanding of JSON goes a long way but is not necessary.

357 |
358 |
359 |

How long is the workshop?

360 |

Roughly 3 hours of fun!

361 |
362 |
363 |

I know a lot about htmlwidgets, should I attend?

364 |

Probably not, you already know most of what will be taught.

365 |
366 |
367 |

When is it?

368 |

June 20, afternoon 14:00-17:00 CEST

369 |

Please feel free to contact me via Twitter (direct messages are open to anyone) if you have any question; @jdatap

370 |
371 |
372 | 373 | 374 | 375 | 376 |
377 |
378 |
379 |
380 |
How to build htmlwidgets
381 |

Takes place June 20, 2020.

382 |
383 |
384 |
Links
385 | 390 |
391 |
392 |
393 | 399 |
400 | 401 | 407 | 408 | 409 | 410 |
411 | 412 | 424 | 425 | 426 | 427 | 438 | 439 | 440 | 441 | 442 | 443 | 451 | 452 | 453 | 454 | -------------------------------------------------------------------------------- /website/_site/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnCoene/how-to-build-htmlwidgets/2c185c87559e818f719729661f8d9d33154fc8cf/website/_site/favicon.ico -------------------------------------------------------------------------------- /website/_site/includes/footer.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 28 | 29 | -------------------------------------------------------------------------------- /website/_site/includes/header.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | htmlwidgets workshop 28 | 29 | 30 | 31 | 38 | -------------------------------------------------------------------------------- /website/_site/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | How to build htmlwidgets 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | htmlwidgets workshop 53 | 54 | 55 | 56 | 63 | 64 | 65 | 70 | 79 | 80 | 81 | 82 | 108 | 109 | 110 | 111 | 112 | 113 | 139 | 140 | 141 | 210 | 211 | 228 | 229 | 230 | 231 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 |
297 | 298 | 299 | 300 | 324 | 325 | 332 | 333 | 334 |
335 |

336 |
337 |

Learn how to build your own interactive data visualization packages

338 |

339 | How to build htmlwidgets_ 340 |

341 |

e-Rum virtual workshop

342 |
343 |

June 20, 14:00-17:00 CEST

344 |
345 |

346 |
347 | 348 | 349 | 350 | 351 |
352 |
353 |
354 |
355 |
How to build htmlwidgets
356 |

Takes place June 20, 2020.

357 |
358 |
359 |
Links
360 | 365 |
366 |
367 |
368 | 374 |
375 | 376 | 382 | 383 | 384 | 385 |
386 | 387 | 399 | 400 | 401 | 402 | 413 | 414 | 415 | 416 | 417 | 418 | 426 | 427 | 428 | 429 | -------------------------------------------------------------------------------- /website/_site/instructor.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | Instructor 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | htmlwidgets workshop 53 | 54 | 55 | 56 | 63 | 64 | 65 | 70 | 79 | 80 | 81 | 82 | 108 | 109 | 110 | 111 | 112 | 113 | 139 | 140 | 141 | 210 | 211 | 228 | 229 | 230 | 231 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 |
297 | 298 | 299 | 300 | 324 | 325 | 332 | 333 | 334 |
335 |

336 | Instructor 337 |

338 |
339 |
340 | 341 |
342 |
343 |
344 |

Hi, I'm John

345 |

I am a data analyst currently based in Geneva. I have build numerous htmlwidgets:

346 | 388 |

389 | And many others available on 390 | github 391 |

392 |
393 |
394 |
395 |
396 | 397 | 398 | 399 | 400 |
401 |
402 |
403 |
404 |
How to build htmlwidgets
405 |

Takes place June 20, 2020.

406 |
407 |
408 |
Links
409 | 414 |
415 |
416 |
417 | 423 |
424 | 425 | 431 | 432 | 433 | 434 |
435 | 436 | 448 | 449 | 450 | 451 | 462 | 463 | 464 | 465 | 466 | 467 | 475 | 476 | 477 | 478 | -------------------------------------------------------------------------------- /website/_site/material.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | Material 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | htmlwidgets workshop 53 | 54 | 55 | 56 | 63 | 64 | 65 | 70 | 79 | 80 | 81 | 82 | 108 | 109 | 110 | 111 | 112 | 113 | 139 | 140 | 141 | 210 | 211 | 228 | 229 | 230 | 231 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 |
297 | 298 | 299 | 300 | 324 | 325 | 332 | 333 | 334 |
335 |

336 | Material 337 |

338 |

You do not need to know JavaScript to follow along.

339 |

340 | Setup 341 |

342 |

There are two ways to follow along this workshop; from your local machine or from the remote RStudio Cloud projects.

343 |
344 |
345 |
346 |
347 | 348 |

349 | Local_ 350 |

351 |
352 |
353 |

Required:

354 |
    355 |
  • A recent installation of R (R >= 3.6.0)
  • 356 |
  • A text editor or IDE you are comfortable with, e.g.: RStudio.
  • 357 |
  • 358 | Packages devtools and htmlwidgets installed 359 |
    360 | install.packages(c("devtools", "htmlwidgets"))
    361 | 
    362 |
  • 363 |
  • 364 | Clone or download 365 | github.com/JohnCoene/how-to-build-htmlwidgets 366 |
    367 | git clone https://github.com/JohnCoene/how-to-build-htmlwidgets.git
    368 | 
    369 |
  • Register for e-Rum and the workshop
  • 370 | 371 |
372 |
373 |
374 |
375 |
376 |
377 |
378 | 379 |

380 | Remote_ 381 |

382 |
383 |
384 |

385 | You can use the 386 | RStudio Cloud project 387 | in the event that you cannot work on your local machine. 388 |

389 |
390 |
391 |
392 |
393 |
394 | 395 | 396 | 397 | 398 |
399 |
400 |
401 |
402 |
How to build htmlwidgets
403 |

Takes place June 20, 2020.

404 |
405 |
406 |
Links
407 | 412 |
413 |
414 |
415 | 421 |
422 | 423 | 429 | 430 | 431 | 432 |
433 | 434 | 446 | 447 | 448 | 449 | 460 | 461 | 462 | 463 | 464 | 465 | 473 | 474 | 475 | 476 | -------------------------------------------------------------------------------- /website/_site/presentation/dt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnCoene/how-to-build-htmlwidgets/2c185c87559e818f719729661f8d9d33154fc8cf/website/_site/presentation/dt.png -------------------------------------------------------------------------------- /website/_site/presentation/echarts4r.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnCoene/how-to-build-htmlwidgets/2c185c87559e818f719729661f8d9d33154fc8cf/website/_site/presentation/echarts4r.png -------------------------------------------------------------------------------- /website/_site/presentation/globe4r.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnCoene/how-to-build-htmlwidgets/2c185c87559e818f719729661f8d9d33154fc8cf/website/_site/presentation/globe4r.png -------------------------------------------------------------------------------- /website/_site/presentation/grapher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnCoene/how-to-build-htmlwidgets/2c185c87559e818f719729661f8d9d33154fc8cf/website/_site/presentation/grapher.png -------------------------------------------------------------------------------- /website/_site/presentation/index.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "How to build htmlwidgets" 3 | subtitle: "e-Rum 2020 Workshop" 4 | author: "John Coene" 5 | date: "20-06-2020" 6 | output: 7 | xaringan::moon_reader: 8 | lib_dir: libs 9 | css: [default, fc, "style.css"] 10 | nature: 11 | ratio: "16:9" 12 | highlightStyle: default 13 | highlightLines: true 14 | countIncrementalSlides: false 15 | --- 16 | 17 | layout: true 18 | 19 |
20 | 21 | 22 | 23 | --- 24 | 25 | .centerize[ 26 | 27 | ## Plan 28 | 29 | Introduction to the basics (this) ~15 mins

30 | Explore htmlwidgets hands-on ~45 mins

31 | Build a widget for [typed.js](https://github.com/mattboldt/typed.js/)

32 | Short break

33 | Build a widget for [gio.js](https://giojs.org/) 34 | 35 | ] 36 | 37 | --- 38 | 39 | .centerize[ 40 | 41 | Resources 42 | 43 | ## htmlwidgets.john-coene.com 44 | 45 | ] 46 | 47 | --- 48 | 49 | ```{r, include = FALSE} 50 | library(emo) 51 | ``` 52 | 53 | .centerize[ 54 | 55 | # What is "htmlwidgets"? 56 | 57 | An R Package to wrap external JavaScript libraries that produce a visual output. 58 | 59 | ] 60 | 61 | --- 62 | 63 | ## Examples 64 | 65 | Visit [gallery.htmlwidgets.org](https://gallery.htmlwidgets.org/) 66 | 67 | .pull-left[ 68 | **plotly** 69 |
70 | 71 | 72 | **DT** 73 |
74 | 75 | ] 76 | 77 | .pull-right[ 78 | **leaflet** 79 |
80 | 81 | 82 | **wordcloud2** 83 |
84 | 85 | ] 86 | 87 | --- 88 | 89 | ## Shameless Promotions 90 | 91 | A few of the htmlwidgets I have built. 92 | 93 | .pull-left[ 94 | **echarts4r** 95 |
96 | 97 | 98 | **grapher** 99 |
100 | 101 | ] 102 | 103 | .pull-right[ 104 | **sigmajs** 105 |
106 | 107 | 108 | **globe4r** 109 |
110 | 111 | ] 112 | 113 | --- 114 | 115 | .centerize[ 116 | 117 | ## Candidate Libraries 118 | 119 | Understand what is expected of us 120 | 121 | ] 122 | 123 | --- 124 | 125 | .pull-left[ 126 | ## Plotly 127 | 128 | - Import library 129 | - `
` to hold viz 130 | - ` 144 | 145 | 146 | 147 | 148 |
149 | 150 | 151 | 158 | 159 | 160 | 161 | ``` 162 | 163 | ] 164 | 165 | --- 166 | 167 | .pull-left[ 168 | ## Highchart.js 169 | 170 | - Import library 171 | - `
` to hold viz 172 | - ` 186 | 187 | 188 | 189 | 190 |
191 | 192 | 193 | 207 | 208 | 209 | 210 | ``` 211 | 212 | ] 213 | 214 | --- 215 | 216 | .pull-left[ 217 | ## Chart.js 218 | 219 | - Import library 220 | - `` to hold viz 221 | - ` 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 255 | 256 | 257 | 258 | ``` 259 | 260 | ] 261 | 262 | --- 263 | 264 | .centerize[ 265 | 266 | ## How would this be done in R? 267 | 268 | ] 269 | 270 | --- 271 | 272 | ## Crate a static HTML file 273 | 274 | ```html 275 | 276 | 277 | 278 | 279 | ``` 280 | 281 | --- 282 | 283 | ## Import dependencies 284 | 285 | ```html 286 | 287 | 288 | 289 | 290 | 291 | 292 | ``` 293 | 294 | --- 295 | 296 | ## Create Canvas 297 | 298 | ```html 299 | 300 | 301 | 302 | 303 | 304 |
305 | 306 | 307 | ``` 308 | 309 | --- 310 | 311 | ## Serialise R object 312 | 313 | ```html 314 | 315 | 316 | 317 | 318 | 319 |
320 | 321 | 322 | 323 | ``` 324 | 325 | --- 326 | 327 | ## Import custom functions 328 | 329 | ```html 330 | 331 | 332 | 333 | 334 | 335 |
336 | 337 | 338 | 339 | 340 | ``` 341 | 342 | --- 343 | 344 | .centerize[ 345 | 346 | ## Finally, I need to manage this across Shiny and R markdown... 347 | 348 | ] 349 | 350 | --- 351 | 352 | .centerize[ 353 | 354 | ## It __should be__ complicated 355 | 356 | ] 357 | 358 | --- 359 | 360 | .centerize[ 361 | 362 | Thankfully, it isn't 363 | 364 | ## Meet __htmlwidgets__! 365 | 366 | ] 367 | 368 | --- 369 | 370 | .centerize[ 371 | 372 | ## Let's Start 373 | 374 | First, create a package with `usethis` or the RStudio IDE. 375 | 376 | ```r 377 | usethis::create_package('playground') 378 | ``` 379 | 380 | Then, from the root of the package, build the scaffolding for our visualisation. 381 | 382 | ```r 383 | htmlwidgets::scaffoldWidget("playground") 384 | ``` 385 | 386 | ] -------------------------------------------------------------------------------- /website/_site/presentation/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | How to build htmlwidgets 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 393 | 394 | 395 | 483 | 484 | 503 | 504 | 514 | 515 | 516 | -------------------------------------------------------------------------------- /website/_site/presentation/leaflet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnCoene/how-to-build-htmlwidgets/2c185c87559e818f719729661f8d9d33154fc8cf/website/_site/presentation/leaflet.png -------------------------------------------------------------------------------- /website/_site/presentation/libs/header-attrs-2.2/header-attrs.js: -------------------------------------------------------------------------------- 1 | // Pandoc 2.9 adds attributes on both header and div. We remove the former (to 2 | // be compatible with the behavior of Pandoc < 2.8). 3 | document.addEventListener('DOMContentLoaded', function(e) { 4 | var hs = document.querySelectorAll("div.section[class*='level'] > :first-child"); 5 | var i, h, a; 6 | for (i = 0; i < hs.length; i++) { 7 | h = hs[i]; 8 | if (!/^h[1-6]$/i.test(h.tagName)) continue; // it should be a header h1-h6 9 | a = h.attributes; 10 | while (a.length > 0) h.removeAttribute(a[0].name); 11 | } 12 | }); 13 | -------------------------------------------------------------------------------- /website/_site/presentation/libs/remark-css-0.0.1/default.css: -------------------------------------------------------------------------------- 1 | a, a > code { 2 | color: rgb(249, 38, 114); 3 | text-decoration: none; 4 | } 5 | .footnote { 6 | position: absolute; 7 | bottom: 3em; 8 | padding-right: 4em; 9 | font-size: 90%; 10 | } 11 | .remark-code-line-highlighted { background-color: #ffff88; } 12 | 13 | .inverse { 14 | background-color: #272822; 15 | color: #d6d6d6; 16 | text-shadow: 0 0 20px #333; 17 | } 18 | .inverse h1, .inverse h2, .inverse h3 { 19 | color: #f3f3f3; 20 | } 21 | /* Two-column layout */ 22 | .left-column { 23 | color: #777; 24 | width: 20%; 25 | height: 92%; 26 | float: left; 27 | } 28 | .left-column h2:last-of-type, .left-column h3:last-child { 29 | color: #000; 30 | } 31 | .right-column { 32 | width: 75%; 33 | float: right; 34 | padding-top: 1em; 35 | } 36 | .pull-left { 37 | float: left; 38 | width: 47%; 39 | } 40 | .pull-right { 41 | float: right; 42 | width: 47%; 43 | } 44 | .pull-right ~ * { 45 | clear: both; 46 | } 47 | img, video, iframe { 48 | max-width: 100%; 49 | } 50 | blockquote { 51 | border-left: solid 5px lightgray; 52 | padding-left: 1em; 53 | } 54 | .remark-slide table { 55 | margin: auto; 56 | border-top: 1px solid #666; 57 | border-bottom: 1px solid #666; 58 | } 59 | .remark-slide table thead th { border-bottom: 1px solid #ddd; } 60 | th, td { padding: 5px; } 61 | .remark-slide thead, .remark-slide tfoot, .remark-slide tr:nth-child(even) { background: #eee } 62 | 63 | @page { margin: 0; } 64 | @media print { 65 | .remark-slide-scaler { 66 | width: 100% !important; 67 | height: 100% !important; 68 | transform: scale(1) !important; 69 | top: 0 !important; 70 | left: 0 !important; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /website/_site/presentation/libs/remark-css-0.0.1/fc.css: -------------------------------------------------------------------------------- 1 | /* Various left and right split columns*/ 2 | 3 | .pull-left-70 { float: left; width: 66.5%; } 4 | .pull-right-30 { float: right; width: 28.5%; } 5 | .pull-right-30 ~ * { clear: both; } 6 | 7 | .pull-left-60 { float: left; width: 57%; } 8 | .pull-right-40 { float: right; width: 38%; } 9 | .pull-right-40 ~ * { clear: both; } 10 | 11 | .pull-left-50 { float: left; width: 47.5%; } 12 | .pull-right-50 { float: right; width: 47.5%; } 13 | .pull-right-50 ~ * { clear: both; } 14 | 15 | .pull-left-40 { float: left; width: 38%; } 16 | .pull-right-60 { float: right; width: 57%; } 17 | .pull-right-60 ~ * { clear: both; } 18 | 19 | .pull-left-30 { float: left; width: 28.5%; } 20 | .pull-right-70 { float: right; width: 66.5%; } 21 | .pull-right-70 ~ * { clear: both; } 22 | 23 | /* headers and fonts*/ 24 | h1, h2, h3 { 25 | margin-top: 0; 26 | padding-top: 0.5em; 27 | font-family: 'Oswald'; 28 | font-weight: 400; 29 | margin-left: 0; 30 | } 31 | 32 | .large { 33 | font-size: 23px; 34 | } 35 | 36 | .small { 37 | font-size: 15px; 38 | } 39 | 40 | /* regular slide */ 41 | .remark-slide-content { 42 | background-color: #FAFAFA; 43 | border-bottom: 5px solid #ff5f0f; 44 | font-size: 20px; 45 | font-weight: 300; 46 | line-height: 1.2; 47 | padding: 1em 3em 1em 3em; 48 | } 49 | 50 | /* inverse slide */ 51 | .inverse { 52 | background-color: #121316; 53 | color: #ffffff; 54 | } 55 | 56 | /* header colors are the same regardless of inverse or not*/ 57 | .remark-slide-content h1, .inverse h1 { 58 | color: #ff5f0f; 59 | font-size: 35px; 60 | } 61 | 62 | .remark-slide-content h2, .inverse h2 { 63 | color: #b7b7b7; 64 | font-size: 30px; 65 | } 66 | 67 | .remark-slide-content h3, .inverse h3 { 68 | color: #5b5b5b; 69 | font-size: 25px; 70 | } 71 | 72 | /* title slide */ 73 | .title-slide { 74 | background-color: #121316; 75 | } 76 | 77 | .title-slide h1 { 78 | color: #ff5f0f; 79 | font-size: 50px; 80 | font-weight: 500; 81 | text-align: left; 82 | margin-left: 40px; 83 | padding-top: 60px; 84 | } 85 | .title-slide h2 { 86 | color: #FAFAFA; 87 | font-size: 35px; 88 | font-weight: 350; 89 | text-align: left; 90 | margin-left: 40px; 91 | margin-top: -25px; 92 | padding-bottom: -20px; 93 | } 94 | .title-slide h3 { 95 | color: #b7b7b7; 96 | font-size: 25px; 97 | font-weight: 300; 98 | text-align: left; 99 | margin-left: 40px; 100 | margin-bottom: 0; 101 | } 102 | 103 | /* slide number */ 104 | .remark-slide-number { 105 | font-size: 10pt; 106 | font-family: 'EB Garamond'; 107 | color: #272822; 108 | opacity: 1; 109 | } 110 | .inverse .remark-slide-number { 111 | font-size: 10pt; 112 | font-family: 'EB Garamond'; 113 | color: #FAFAFA; 114 | opacity: 1; 115 | } 116 | 117 | /* code */ 118 | .remark-inline-code { 119 | color: #ff4c00; 120 | border-radius: 1px; 121 | padding: 0; 122 | background: #e0e0e0; 123 | } 124 | 125 | .inverse .remark-inline-code { 126 | background: #333333; 127 | } 128 | 129 | .remark-code-line-highlighted { 130 | background-color: #ffead3; 131 | } 132 | -------------------------------------------------------------------------------- /website/_site/presentation/plotly.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnCoene/how-to-build-htmlwidgets/2c185c87559e818f719729661f8d9d33154fc8cf/website/_site/presentation/plotly.png -------------------------------------------------------------------------------- /website/_site/presentation/sigmajs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnCoene/how-to-build-htmlwidgets/2c185c87559e818f719729661f8d9d33154fc8cf/website/_site/presentation/sigmajs.png -------------------------------------------------------------------------------- /website/_site/presentation/style.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css?family=Quicksand|Ubuntu+Mono&display=swap'); 2 | 3 | body{ 4 | font-family: 'Quicksand', sans-serif; 5 | } 6 | 7 | .title-slide{ 8 | background-color:#2a388f; 9 | } 10 | 11 | .title-slide > h1, .title-slide > h2{ 12 | color: #d0d3d6 !important; 13 | } 14 | 15 | h1,h2,h3,h4{ 16 | font-family: 'Quicksand', sans-serif; 17 | } 18 | 19 | .inverse{ 20 | background-color:#2a388f; 21 | } 22 | 23 | h1, h2{ 24 | color: #2a388f !important; 25 | } 26 | 27 | p{ 28 | color: #2a388f !important; 29 | } 30 | 31 | a{ 32 | color: #333e48 !important; 33 | text-decoration: underline; 34 | } 35 | 36 | pre, code{ 37 | font-family: 'Ubuntu Mono', monospace; 38 | } 39 | 40 | .centerize { 41 | text-align: center; 42 | margin: 0; 43 | position: absolute; 44 | top: 50%; 45 | left: 50%; 46 | -ms-transform: translate(-50%, -50%); 47 | transform: translate(-50%, -50%); 48 | width: 90%; 49 | } 50 | 51 | .remark-slide-content{ 52 | border-bottom: 5px solid #2a388f; 53 | } 54 | 55 | .pull-left { 56 | float: left; 57 | width: 47%; 58 | } 59 | .pull-right { 60 | float: right; 61 | width: 47%; 62 | } 63 | 64 | div.my-footer { 65 | background-color: rgba(255,255,255,0); 66 | position: absolute; 67 | bottom: 0px; 68 | left: 0px; 69 | height: 20px; 70 | width: 100%; 71 | } 72 | div.my-footer span { 73 | font-size: 10pt; 74 | color: #2a388f; 75 | position: absolute; 76 | left: 15px; 77 | bottom: 5px; 78 | } -------------------------------------------------------------------------------- /website/_site/presentation/wordcloud2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnCoene/how-to-build-htmlwidgets/2c185c87559e818f719729661f8d9d33154fc8cf/website/_site/presentation/wordcloud2.png -------------------------------------------------------------------------------- /website/_site/program.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | Program 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | htmlwidgets workshop 53 | 54 | 55 | 56 | 63 | 64 | 65 | 70 | 79 | 80 | 81 | 82 | 108 | 109 | 110 | 111 | 112 | 113 | 139 | 140 | 141 | 210 | 211 | 228 | 229 | 230 | 231 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 |
297 | 298 | 299 | 300 | 324 | 325 | 332 | 333 | 334 |
335 |

336 | Program 337 |

338 |

339 | What you will learn 340 |

341 |

You will learn how to build your own packages for visualisations using htmlwidgets. htmlwidgets is the backbone of packages such as leaflet, plotly, highcharter, and many others. The aim is that by the end of the workshop you will be able to write your own such packages!

342 |

343 | Methodology 344 |

345 |

Learn by doing. I believe this remains the best way to go about it; the workshop will therefore have you write a quite a bit of code.

346 |

347 | Plan 348 |

349 |

This is a rough plan, things are likely to change depending on the pace we pick up. The point is not to rush things but if great progress is made, we can delve into more advanced stuff at the end.

350 |
    351 |
  • 352 |

    353 | Introduction to the basics (20-30 mins) 354 |

    355 |
  • 356 |
  • 357 |

    358 | Explore htmlwidgets (45-60 mins) 359 |

    360 |
  • 361 |
  • 362 |

    363 | Coffee Break 364 |

    365 |
  • 366 |
  • 367 |

    368 | Build our second widget (60 mins) 369 |

    370 |
  • 371 |
  • 372 |

    373 | Additional functionalities (30 mins) 374 |

    375 |
  • 376 |
377 |
378 | 379 | 380 | 381 | 382 |
383 |
384 |
385 |
386 |
How to build htmlwidgets
387 |

Takes place June 20, 2020.

388 |
389 |
390 |
Links
391 | 396 |
397 |
398 |
399 | 405 |
406 | 407 | 413 | 414 | 415 | 416 |
417 | 418 | 430 | 431 | 432 | 433 | 444 | 445 | 446 | 447 | 448 | 449 | 457 | 458 | 459 | 460 | -------------------------------------------------------------------------------- /website/_site/site_libs/bootstrap-3.3.5/css/fonts/Lato.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnCoene/how-to-build-htmlwidgets/2c185c87559e818f719729661f8d9d33154fc8cf/website/_site/site_libs/bootstrap-3.3.5/css/fonts/Lato.ttf -------------------------------------------------------------------------------- /website/_site/site_libs/bootstrap-3.3.5/css/fonts/LatoBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnCoene/how-to-build-htmlwidgets/2c185c87559e818f719729661f8d9d33154fc8cf/website/_site/site_libs/bootstrap-3.3.5/css/fonts/LatoBold.ttf -------------------------------------------------------------------------------- /website/_site/site_libs/bootstrap-3.3.5/css/fonts/LatoItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnCoene/how-to-build-htmlwidgets/2c185c87559e818f719729661f8d9d33154fc8cf/website/_site/site_libs/bootstrap-3.3.5/css/fonts/LatoItalic.ttf -------------------------------------------------------------------------------- /website/_site/site_libs/bootstrap-3.3.5/css/fonts/NewsCycle.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnCoene/how-to-build-htmlwidgets/2c185c87559e818f719729661f8d9d33154fc8cf/website/_site/site_libs/bootstrap-3.3.5/css/fonts/NewsCycle.ttf -------------------------------------------------------------------------------- /website/_site/site_libs/bootstrap-3.3.5/css/fonts/NewsCycleBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnCoene/how-to-build-htmlwidgets/2c185c87559e818f719729661f8d9d33154fc8cf/website/_site/site_libs/bootstrap-3.3.5/css/fonts/NewsCycleBold.ttf -------------------------------------------------------------------------------- /website/_site/site_libs/bootstrap-3.3.5/css/fonts/OpenSans.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnCoene/how-to-build-htmlwidgets/2c185c87559e818f719729661f8d9d33154fc8cf/website/_site/site_libs/bootstrap-3.3.5/css/fonts/OpenSans.ttf -------------------------------------------------------------------------------- /website/_site/site_libs/bootstrap-3.3.5/css/fonts/OpenSansBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnCoene/how-to-build-htmlwidgets/2c185c87559e818f719729661f8d9d33154fc8cf/website/_site/site_libs/bootstrap-3.3.5/css/fonts/OpenSansBold.ttf -------------------------------------------------------------------------------- /website/_site/site_libs/bootstrap-3.3.5/css/fonts/OpenSansBoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnCoene/how-to-build-htmlwidgets/2c185c87559e818f719729661f8d9d33154fc8cf/website/_site/site_libs/bootstrap-3.3.5/css/fonts/OpenSansBoldItalic.ttf -------------------------------------------------------------------------------- /website/_site/site_libs/bootstrap-3.3.5/css/fonts/OpenSansItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnCoene/how-to-build-htmlwidgets/2c185c87559e818f719729661f8d9d33154fc8cf/website/_site/site_libs/bootstrap-3.3.5/css/fonts/OpenSansItalic.ttf -------------------------------------------------------------------------------- /website/_site/site_libs/bootstrap-3.3.5/css/fonts/OpenSansLight.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnCoene/how-to-build-htmlwidgets/2c185c87559e818f719729661f8d9d33154fc8cf/website/_site/site_libs/bootstrap-3.3.5/css/fonts/OpenSansLight.ttf -------------------------------------------------------------------------------- /website/_site/site_libs/bootstrap-3.3.5/css/fonts/OpenSansLightItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnCoene/how-to-build-htmlwidgets/2c185c87559e818f719729661f8d9d33154fc8cf/website/_site/site_libs/bootstrap-3.3.5/css/fonts/OpenSansLightItalic.ttf -------------------------------------------------------------------------------- /website/_site/site_libs/bootstrap-3.3.5/css/fonts/Raleway.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnCoene/how-to-build-htmlwidgets/2c185c87559e818f719729661f8d9d33154fc8cf/website/_site/site_libs/bootstrap-3.3.5/css/fonts/Raleway.ttf -------------------------------------------------------------------------------- /website/_site/site_libs/bootstrap-3.3.5/css/fonts/RalewayBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnCoene/how-to-build-htmlwidgets/2c185c87559e818f719729661f8d9d33154fc8cf/website/_site/site_libs/bootstrap-3.3.5/css/fonts/RalewayBold.ttf -------------------------------------------------------------------------------- /website/_site/site_libs/bootstrap-3.3.5/css/fonts/Roboto.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnCoene/how-to-build-htmlwidgets/2c185c87559e818f719729661f8d9d33154fc8cf/website/_site/site_libs/bootstrap-3.3.5/css/fonts/Roboto.ttf -------------------------------------------------------------------------------- /website/_site/site_libs/bootstrap-3.3.5/css/fonts/RobotoBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnCoene/how-to-build-htmlwidgets/2c185c87559e818f719729661f8d9d33154fc8cf/website/_site/site_libs/bootstrap-3.3.5/css/fonts/RobotoBold.ttf -------------------------------------------------------------------------------- /website/_site/site_libs/bootstrap-3.3.5/css/fonts/RobotoLight.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnCoene/how-to-build-htmlwidgets/2c185c87559e818f719729661f8d9d33154fc8cf/website/_site/site_libs/bootstrap-3.3.5/css/fonts/RobotoLight.ttf -------------------------------------------------------------------------------- /website/_site/site_libs/bootstrap-3.3.5/css/fonts/RobotoMedium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnCoene/how-to-build-htmlwidgets/2c185c87559e818f719729661f8d9d33154fc8cf/website/_site/site_libs/bootstrap-3.3.5/css/fonts/RobotoMedium.ttf -------------------------------------------------------------------------------- /website/_site/site_libs/bootstrap-3.3.5/css/fonts/SourceSansPro.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnCoene/how-to-build-htmlwidgets/2c185c87559e818f719729661f8d9d33154fc8cf/website/_site/site_libs/bootstrap-3.3.5/css/fonts/SourceSansPro.ttf -------------------------------------------------------------------------------- /website/_site/site_libs/bootstrap-3.3.5/css/fonts/SourceSansProBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnCoene/how-to-build-htmlwidgets/2c185c87559e818f719729661f8d9d33154fc8cf/website/_site/site_libs/bootstrap-3.3.5/css/fonts/SourceSansProBold.ttf -------------------------------------------------------------------------------- /website/_site/site_libs/bootstrap-3.3.5/css/fonts/SourceSansProItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnCoene/how-to-build-htmlwidgets/2c185c87559e818f719729661f8d9d33154fc8cf/website/_site/site_libs/bootstrap-3.3.5/css/fonts/SourceSansProItalic.ttf -------------------------------------------------------------------------------- /website/_site/site_libs/bootstrap-3.3.5/css/fonts/SourceSansProLight.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnCoene/how-to-build-htmlwidgets/2c185c87559e818f719729661f8d9d33154fc8cf/website/_site/site_libs/bootstrap-3.3.5/css/fonts/SourceSansProLight.ttf -------------------------------------------------------------------------------- /website/_site/site_libs/bootstrap-3.3.5/css/fonts/Ubuntu.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnCoene/how-to-build-htmlwidgets/2c185c87559e818f719729661f8d9d33154fc8cf/website/_site/site_libs/bootstrap-3.3.5/css/fonts/Ubuntu.ttf -------------------------------------------------------------------------------- /website/_site/site_libs/bootstrap-3.3.5/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnCoene/how-to-build-htmlwidgets/2c185c87559e818f719729661f8d9d33154fc8cf/website/_site/site_libs/bootstrap-3.3.5/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /website/_site/site_libs/bootstrap-3.3.5/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnCoene/how-to-build-htmlwidgets/2c185c87559e818f719729661f8d9d33154fc8cf/website/_site/site_libs/bootstrap-3.3.5/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /website/_site/site_libs/bootstrap-3.3.5/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnCoene/how-to-build-htmlwidgets/2c185c87559e818f719729661f8d9d33154fc8cf/website/_site/site_libs/bootstrap-3.3.5/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /website/_site/site_libs/bootstrap-3.3.5/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnCoene/how-to-build-htmlwidgets/2c185c87559e818f719729661f8d9d33154fc8cf/website/_site/site_libs/bootstrap-3.3.5/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /website/_site/site_libs/bootstrap-3.3.5/js/npm.js: -------------------------------------------------------------------------------- 1 | // This file is autogenerated via the `commonjs` Grunt task. You can require() this file in a CommonJS environment. 2 | require('../../js/transition.js') 3 | require('../../js/alert.js') 4 | require('../../js/button.js') 5 | require('../../js/carousel.js') 6 | require('../../js/collapse.js') 7 | require('../../js/dropdown.js') 8 | require('../../js/modal.js') 9 | require('../../js/tooltip.js') 10 | require('../../js/popover.js') 11 | require('../../js/scrollspy.js') 12 | require('../../js/tab.js') 13 | require('../../js/affix.js') -------------------------------------------------------------------------------- /website/_site/site_libs/bootstrap-3.3.5/shim/html5shiv.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @preserve HTML5 Shiv 3.7.2 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed 3 | */ 4 | // Only run this code in IE 8 5 | if (!!window.navigator.userAgent.match("MSIE 8")) { 6 | !function(a,b){function c(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x",d.insertBefore(c.lastChild,d.firstChild)}function d(){var a=t.elements;return"string"==typeof a?a.split(" "):a}function e(a,b){var c=t.elements;"string"!=typeof c&&(c=c.join(" ")),"string"!=typeof a&&(a=a.join(" ")),t.elements=c+" "+a,j(b)}function f(a){var b=s[a[q]];return b||(b={},r++,a[q]=r,s[r]=b),b}function g(a,c,d){if(c||(c=b),l)return c.createElement(a);d||(d=f(c));var e;return e=d.cache[a]?d.cache[a].cloneNode():p.test(a)?(d.cache[a]=d.createElem(a)).cloneNode():d.createElem(a),!e.canHaveChildren||o.test(a)||e.tagUrn?e:d.frag.appendChild(e)}function h(a,c){if(a||(a=b),l)return a.createDocumentFragment();c=c||f(a);for(var e=c.frag.cloneNode(),g=0,h=d(),i=h.length;i>g;g++)e.createElement(h[g]);return e}function i(a,b){b.cache||(b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag()),a.createElement=function(c){return t.shivMethods?g(c,a,b):b.createElem(c)},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+d().join().replace(/[\w\-:]+/g,function(a){return b.createElem(a),b.frag.createElement(a),'c("'+a+'")'})+");return n}")(t,b.frag)}function j(a){a||(a=b);var d=f(a);return!t.shivCSS||k||d.hasCSS||(d.hasCSS=!!c(a,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),l||i(a,d),a}var k,l,m="3.7.2",n=a.html5||{},o=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,p=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,q="_html5shiv",r=0,s={};!function(){try{var a=b.createElement("a");a.innerHTML="",k="hidden"in a,l=1==a.childNodes.length||function(){b.createElement("a");var a=b.createDocumentFragment();return"undefined"==typeof a.cloneNode||"undefined"==typeof a.createDocumentFragment||"undefined"==typeof a.createElement}()}catch(c){k=!0,l=!0}}();var t={elements:n.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video",version:m,shivCSS:n.shivCSS!==!1,supportsUnknownElements:l,shivMethods:n.shivMethods!==!1,type:"default",shivDocument:j,createElement:g,createDocumentFragment:h,addElements:e};a.html5=t,j(b)}(this,document); 7 | }; 8 | -------------------------------------------------------------------------------- /website/_site/site_libs/bootstrap-3.3.5/shim/respond.min.js: -------------------------------------------------------------------------------- 1 | /*! Respond.js v1.4.2: min/max-width media query polyfill * Copyright 2013 Scott Jehl 2 | * Licensed under https://github.com/scottjehl/Respond/blob/master/LICENSE-MIT 3 | * */ 4 | 5 | // Only run this code in IE 8 6 | if (!!window.navigator.userAgent.match("MSIE 8")) { 7 | !function(a){"use strict";a.matchMedia=a.matchMedia||function(a){var b,c=a.documentElement,d=c.firstElementChild||c.firstChild,e=a.createElement("body"),f=a.createElement("div");return f.id="mq-test-1",f.style.cssText="position:absolute;top:-100em",e.style.background="none",e.appendChild(f),function(a){return f.innerHTML='­',c.insertBefore(e,d),b=42===f.offsetWidth,c.removeChild(e),{matches:b,media:a}}}(a.document)}(this),function(a){"use strict";function b(){u(!0)}var c={};a.respond=c,c.update=function(){};var d=[],e=function(){var b=!1;try{b=new a.XMLHttpRequest}catch(c){b=new a.ActiveXObject("Microsoft.XMLHTTP")}return function(){return b}}(),f=function(a,b){var c=e();c&&(c.open("GET",a,!0),c.onreadystatechange=function(){4!==c.readyState||200!==c.status&&304!==c.status||b(c.responseText)},4!==c.readyState&&c.send(null))};if(c.ajax=f,c.queue=d,c.regex={media:/@media[^\{]+\{([^\{\}]*\{[^\}\{]*\})+/gi,keyframes:/@(?:\-(?:o|moz|webkit)\-)?keyframes[^\{]+\{(?:[^\{\}]*\{[^\}\{]*\})+[^\}]*\}/gi,urls:/(url\()['"]?([^\/\)'"][^:\)'"]+)['"]?(\))/g,findStyles:/@media *([^\{]+)\{([\S\s]+?)$/,only:/(only\s+)?([a-zA-Z]+)\s?/,minw:/\([\s]*min\-width\s*:[\s]*([\s]*[0-9\.]+)(px|em)[\s]*\)/,maxw:/\([\s]*max\-width\s*:[\s]*([\s]*[0-9\.]+)(px|em)[\s]*\)/},c.mediaQueriesSupported=a.matchMedia&&null!==a.matchMedia("only all")&&a.matchMedia("only all").matches,!c.mediaQueriesSupported){var g,h,i,j=a.document,k=j.documentElement,l=[],m=[],n=[],o={},p=30,q=j.getElementsByTagName("head")[0]||k,r=j.getElementsByTagName("base")[0],s=q.getElementsByTagName("link"),t=function(){var a,b=j.createElement("div"),c=j.body,d=k.style.fontSize,e=c&&c.style.fontSize,f=!1;return b.style.cssText="position:absolute;font-size:1em;width:1em",c||(c=f=j.createElement("body"),c.style.background="none"),k.style.fontSize="100%",c.style.fontSize="100%",c.appendChild(b),f&&k.insertBefore(c,k.firstChild),a=b.offsetWidth,f?k.removeChild(c):c.removeChild(b),k.style.fontSize=d,e&&(c.style.fontSize=e),a=i=parseFloat(a)},u=function(b){var c="clientWidth",d=k[c],e="CSS1Compat"===j.compatMode&&d||j.body[c]||d,f={},o=s[s.length-1],r=(new Date).getTime();if(b&&g&&p>r-g)return a.clearTimeout(h),h=a.setTimeout(u,p),void 0;g=r;for(var v in l)if(l.hasOwnProperty(v)){var w=l[v],x=w.minw,y=w.maxw,z=null===x,A=null===y,B="em";x&&(x=parseFloat(x)*(x.indexOf(B)>-1?i||t():1)),y&&(y=parseFloat(y)*(y.indexOf(B)>-1?i||t():1)),w.hasquery&&(z&&A||!(z||e>=x)||!(A||y>=e))||(f[w.media]||(f[w.media]=[]),f[w.media].push(m[w.rules]))}for(var C in n)n.hasOwnProperty(C)&&n[C]&&n[C].parentNode===q&&q.removeChild(n[C]);n.length=0;for(var D in f)if(f.hasOwnProperty(D)){var E=j.createElement("style"),F=f[D].join("\n");E.type="text/css",E.media=D,q.insertBefore(E,o.nextSibling),E.styleSheet?E.styleSheet.cssText=F:E.appendChild(j.createTextNode(F)),n.push(E)}},v=function(a,b,d){var e=a.replace(c.regex.keyframes,"").match(c.regex.media),f=e&&e.length||0;b=b.substring(0,b.lastIndexOf("/"));var g=function(a){return a.replace(c.regex.urls,"$1"+b+"$2$3")},h=!f&&d;b.length&&(b+="/"),h&&(f=1);for(var i=0;f>i;i++){var j,k,n,o;h?(j=d,m.push(g(a))):(j=e[i].match(c.regex.findStyles)&&RegExp.$1,m.push(RegExp.$2&&g(RegExp.$2))),n=j.split(","),o=n.length;for(var p=0;o>p;p++)k=n[p],l.push({media:k.split("(")[0].match(c.regex.only)&&RegExp.$2||"all",rules:m.length-1,hasquery:k.indexOf("(")>-1,minw:k.match(c.regex.minw)&&parseFloat(RegExp.$1)+(RegExp.$2||""),maxw:k.match(c.regex.maxw)&&parseFloat(RegExp.$1)+(RegExp.$2||"")})}u()},w=function(){if(d.length){var b=d.shift();f(b.href,function(c){v(c,b.href,b.media),o[b.href]=!0,a.setTimeout(function(){w()},0)})}},x=function(){for(var b=0;b :first-child"); 5 | var i, h, a; 6 | for (i = 0; i < hs.length; i++) { 7 | h = hs[i]; 8 | if (!/^h[1-6]$/i.test(h.tagName)) continue; // it should be a header h1-h6 9 | a = h.attributes; 10 | while (a.length > 0) h.removeAttribute(a[0].name); 11 | } 12 | }); 13 | -------------------------------------------------------------------------------- /website/_site/site_libs/highlightjs-9.12.0/default.css: -------------------------------------------------------------------------------- 1 | .hljs-literal { 2 | color: #990073; 3 | } 4 | 5 | .hljs-number { 6 | color: #099; 7 | } 8 | 9 | .hljs-comment { 10 | color: #998; 11 | font-style: italic; 12 | } 13 | 14 | .hljs-keyword { 15 | color: #900; 16 | font-weight: bold; 17 | } 18 | 19 | .hljs-string { 20 | color: #d14; 21 | } 22 | -------------------------------------------------------------------------------- /website/_site/site_libs/highlightjs-9.12.0/textmate.css: -------------------------------------------------------------------------------- 1 | .hljs-literal { 2 | color: rgb(88, 72, 246); 3 | } 4 | 5 | .hljs-number { 6 | color: rgb(0, 0, 205); 7 | } 8 | 9 | .hljs-comment { 10 | color: rgb(76, 136, 107); 11 | } 12 | 13 | .hljs-keyword { 14 | color: rgb(0, 0, 255); 15 | } 16 | 17 | .hljs-string { 18 | color: rgb(3, 106, 7); 19 | } 20 | -------------------------------------------------------------------------------- /website/_site/site_libs/navigation-1.1/codefolding.js: -------------------------------------------------------------------------------- 1 | 2 | window.initializeCodeFolding = function(show) { 3 | 4 | // handlers for show-all and hide all 5 | $("#rmd-show-all-code").click(function() { 6 | $('div.r-code-collapse').each(function() { 7 | $(this).collapse('show'); 8 | }); 9 | }); 10 | $("#rmd-hide-all-code").click(function() { 11 | $('div.r-code-collapse').each(function() { 12 | $(this).collapse('hide'); 13 | }); 14 | }); 15 | 16 | // index for unique code element ids 17 | var currentIndex = 1; 18 | 19 | // select all R code blocks 20 | var rCodeBlocks = $('pre.r, pre.python, pre.bash, pre.sql, pre.cpp, pre.stan, pre.julia'); 21 | rCodeBlocks.each(function() { 22 | 23 | // create a collapsable div to wrap the code in 24 | var div = $('
'); 25 | show = (show || $(this).hasClass('fold-show')) && !$(this).hasClass('fold-hide'); 26 | if (show) div.addClass('in'); 27 | var id = 'rcode-643E0F36' + currentIndex++; 28 | div.attr('id', id); 29 | $(this).before(div); 30 | $(this).detach().appendTo(div); 31 | 32 | // add a show code button right above 33 | var showCodeText = $('' + (show ? 'Hide' : 'Code') + ''); 34 | var showCodeButton = $(''); 35 | showCodeButton.append(showCodeText); 36 | showCodeButton 37 | .attr('data-toggle', 'collapse') 38 | .attr('data-target', '#' + id) 39 | .attr('aria-expanded', show) 40 | .attr('aria-controls', id); 41 | 42 | var buttonRow = $('
'); 43 | var buttonCol = $('
'); 44 | 45 | buttonCol.append(showCodeButton); 46 | buttonRow.append(buttonCol); 47 | 48 | div.before(buttonRow); 49 | 50 | // update state of button on show/hide 51 | div.on('hidden.bs.collapse', function () { 52 | showCodeText.text('Code'); 53 | }); 54 | div.on('show.bs.collapse', function () { 55 | showCodeText.text('Hide'); 56 | }); 57 | }); 58 | 59 | } 60 | -------------------------------------------------------------------------------- /website/_site/site_libs/navigation-1.1/sourceembed.js: -------------------------------------------------------------------------------- 1 | 2 | window.initializeSourceEmbed = function(filename) { 3 | $("#rmd-download-source").click(function() { 4 | var src = $("#rmd-source-code").html(); 5 | var a = document.createElement('a'); 6 | a.href = "data:text/x-r-markdown;base64," + src; 7 | a.download = filename; 8 | document.body.appendChild(a); 9 | a.click(); 10 | document.body.removeChild(a); 11 | }); 12 | }; 13 | -------------------------------------------------------------------------------- /website/_site/site_libs/navigation-1.1/tabsets.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | /** 4 | * jQuery Plugin: Sticky Tabs 5 | * 6 | * @author Aidan Lister 7 | * adapted by Ruben Arslan to activate parent tabs too 8 | * http://www.aidanlister.com/2014/03/persisting-the-tab-state-in-bootstrap/ 9 | */ 10 | (function($) { 11 | "use strict"; 12 | $.fn.rmarkdownStickyTabs = function() { 13 | var context = this; 14 | // Show the tab corresponding with the hash in the URL, or the first tab 15 | var showStuffFromHash = function() { 16 | var hash = window.location.hash; 17 | var selector = hash ? 'a[href="' + hash + '"]' : 'li.active > a'; 18 | var $selector = $(selector, context); 19 | if($selector.data('toggle') === "tab") { 20 | $selector.tab('show'); 21 | // walk up the ancestors of this element, show any hidden tabs 22 | $selector.parents('.section.tabset').each(function(i, elm) { 23 | var link = $('a[href="#' + $(elm).attr('id') + '"]'); 24 | if(link.data('toggle') === "tab") { 25 | link.tab("show"); 26 | } 27 | }); 28 | } 29 | }; 30 | 31 | 32 | // Set the correct tab when the page loads 33 | showStuffFromHash(context); 34 | 35 | // Set the correct tab when a user uses their back/forward button 36 | $(window).on('hashchange', function() { 37 | showStuffFromHash(context); 38 | }); 39 | 40 | // Change the URL when tabs are clicked 41 | $('a', context).on('click', function(e) { 42 | history.pushState(null, null, this.href); 43 | showStuffFromHash(context); 44 | }); 45 | 46 | return this; 47 | }; 48 | }(jQuery)); 49 | 50 | window.buildTabsets = function(tocID) { 51 | 52 | // build a tabset from a section div with the .tabset class 53 | function buildTabset(tabset) { 54 | 55 | // check for fade and pills options 56 | var fade = tabset.hasClass("tabset-fade"); 57 | var pills = tabset.hasClass("tabset-pills"); 58 | var navClass = pills ? "nav-pills" : "nav-tabs"; 59 | 60 | // determine the heading level of the tabset and tabs 61 | var match = tabset.attr('class').match(/level(\d) /); 62 | if (match === null) 63 | return; 64 | var tabsetLevel = Number(match[1]); 65 | var tabLevel = tabsetLevel + 1; 66 | 67 | // find all subheadings immediately below 68 | var tabs = tabset.find("div.section.level" + tabLevel); 69 | if (!tabs.length) 70 | return; 71 | 72 | // create tablist and tab-content elements 73 | var tabList = $(''); 74 | $(tabs[0]).before(tabList); 75 | var tabContent = $('
'); 76 | $(tabs[0]).before(tabContent); 77 | 78 | // build the tabset 79 | var activeTab = 0; 80 | tabs.each(function(i) { 81 | 82 | // get the tab div 83 | var tab = $(tabs[i]); 84 | 85 | // get the id then sanitize it for use with bootstrap tabs 86 | var id = tab.attr('id'); 87 | 88 | // see if this is marked as the active tab 89 | if (tab.hasClass('active')) 90 | activeTab = i; 91 | 92 | // remove any table of contents entries associated with 93 | // this ID (since we'll be removing the heading element) 94 | $("div#" + tocID + " li a[href='#" + id + "']").parent().remove(); 95 | 96 | // sanitize the id for use with bootstrap tabs 97 | id = id.replace(/[.\/?&!#<>]/g, '').replace(/\s/g, '_'); 98 | tab.attr('id', id); 99 | 100 | // get the heading element within it, grab it's text, then remove it 101 | var heading = tab.find('h' + tabLevel + ':first'); 102 | var headingText = heading.html(); 103 | heading.remove(); 104 | 105 | // build and append the tab list item 106 | var a = $('' + headingText + ''); 107 | a.attr('href', '#' + id); 108 | a.attr('aria-controls', id); 109 | var li = $('
  • '); 110 | li.append(a); 111 | tabList.append(li); 112 | 113 | // set it's attributes 114 | tab.attr('role', 'tabpanel'); 115 | tab.addClass('tab-pane'); 116 | tab.addClass('tabbed-pane'); 117 | if (fade) 118 | tab.addClass('fade'); 119 | 120 | // move it into the tab content div 121 | tab.detach().appendTo(tabContent); 122 | }); 123 | 124 | // set active tab 125 | $(tabList.children('li')[activeTab]).addClass('active'); 126 | var active = $(tabContent.children('div.section')[activeTab]); 127 | active.addClass('active'); 128 | if (fade) 129 | active.addClass('in'); 130 | 131 | if (tabset.hasClass("tabset-sticky")) 132 | tabset.rmarkdownStickyTabs(); 133 | } 134 | 135 | // convert section divs with the .tabset class to tabsets 136 | var tabsets = $("div.section.tabset"); 137 | tabsets.each(function(i) { 138 | buildTabset($(tabsets[i])); 139 | }); 140 | }; 141 | 142 | -------------------------------------------------------------------------------- /website/assets/css/styles.css: -------------------------------------------------------------------------------- 1 | /* override shitty bootstrap: I'm not writing a new generator */ 2 | 3 | body{ 4 | padding: 0px !important; 5 | margin: 0px !important; 6 | } 7 | 8 | .main-container{ 9 | margin: 0px !important; 10 | max-width: none !important; 11 | } 12 | 13 | .container-fluid{ 14 | padding: 0px !important; 15 | } 16 | 17 | #header{ 18 | visibility: hidden; 19 | height: 0px; 20 | } 21 | 22 | /* style */ 23 | @import url('https://fonts.googleapis.com/css2?family=Raleway:wght@200;500&display=swap'); 24 | 25 | body{ 26 | font-family: 'Raleway', sans-serif; 27 | } -------------------------------------------------------------------------------- /website/assets/img/laptop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnCoene/how-to-build-htmlwidgets/2c185c87559e818f719729661f8d9d33154fc8cf/website/assets/img/laptop.png -------------------------------------------------------------------------------- /website/assets/img/server.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnCoene/how-to-build-htmlwidgets/2c185c87559e818f719729661f8d9d33154fc8cf/website/assets/img/server.jpg -------------------------------------------------------------------------------- /website/assets/img/social.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnCoene/how-to-build-htmlwidgets/2c185c87559e818f719729661f8d9d33154fc8cf/website/assets/img/social.png -------------------------------------------------------------------------------- /website/faq.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "FAQ" 3 | --- 4 | 5 |
    6 | 7 |

    FAQ

    8 | 9 | ## Who is this for? 10 | 11 | R programmers interested in visualisation with little knowledge of JavaScript. 12 | 13 | ## Do I need to know JavaScript? 14 | 15 | No. 16 | 17 | ## Are we going to learn JavaScript? 18 | 19 | No. 20 | 21 | ## But we are going to write a lot of JavaScript, aren't we? 22 | 23 | No, we'll write very little JavaScript. 24 | 25 | ## What level of R programming is required? 26 | 27 | You need to be familiar with the language and its data structures, some experience in package development and understanding of JSON goes a long way but is not necessary. 28 | 29 | ## How long is the workshop? 30 | 31 | Roughly 3 hours of fun! 32 | 33 | ## I know a lot about htmlwidgets, should I attend? 34 | 35 | Probably not, you already know most of what will be taught. 36 | 37 | ## When is it? 38 | 39 | June 20, afternoon 14:00-17:00 CEST 40 | 41 | Please feel free to contact me via Twitter (direct messages are open to anyone) if you have any question; @jdatap 42 | 43 |
    -------------------------------------------------------------------------------- /website/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnCoene/how-to-build-htmlwidgets/2c185c87559e818f719729661f8d9d33154fc8cf/website/favicon.ico -------------------------------------------------------------------------------- /website/includes/footer.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 28 | 29 | -------------------------------------------------------------------------------- /website/includes/header.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | htmlwidgets workshop 28 | 29 | 30 | 31 | 38 | -------------------------------------------------------------------------------- /website/index.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "How to build htmlwidgets" 3 | --- 4 | 5 | ```{r, include = FALSE} 6 | library(htmltools) 7 | ``` 8 | 9 |
    10 | 11 | ```{r, echo=FALSE} 12 | img(src="https://2020.erum.io/wp-content/uploads/2019/11/logo_website.png") 13 | div( 14 | class = "card blue darken-1", 15 | div( 16 | class = "card-content white-text", 17 | h1(class = "card-title", "Learn how to build your own interactive data visualization packages"), 18 | h1("How to build htmlwidgets", span("_", class = "orange-text", .noWS = "before")), 19 | h3("e-Rum virtual workshop"), 20 | hr(), 21 | h4("June 20, 14:00-17:00 CEST"), 22 | 23 | ) 24 | ) 25 | ``` 26 | 27 |
    -------------------------------------------------------------------------------- /website/instructor.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Instructor" 3 | --- 4 | 5 | ```{r, include = FALSE} 6 | library(htmltools) 7 | ``` 8 | 9 |
    10 | 11 |

    Instructor

    12 | 13 | ```{r, echo = FALSE} 14 | div( 15 | class = "card horizontal", 16 | div( 17 | class = "card-image", 18 | tags$img(src = "https://github.com/JohnCoene.png") 19 | ), 20 | div( 21 | class = "card-stacked", 22 | div( 23 | class = "card-content", 24 | h2("Hi, I'm John"), 25 | p("I am a data analyst currently based in Geneva. I have build numerous htmlwidgets:"), 26 | tags$ul( 27 | class = "collection", 28 | tags$li( 29 | class = "collection-item", 30 | div( 31 | "echarts4r", 32 | tags$a( 33 | class = "secondary-content blue-text", 34 | href = "https://echarts4r.john-coene.com", 35 | tags$i(class="material-icons", "insert_link") 36 | ) 37 | ) 38 | ), 39 | tags$li( 40 | class = "collection-item", 41 | div( 42 | "sigmajs", 43 | tags$a( 44 | class = "secondary-content blue-text", 45 | href = "https://sigmajs.john-coene.com", 46 | tags$i(class="material-icons", "insert_link") 47 | ) 48 | ) 49 | ), 50 | tags$li( 51 | class = "collection-item", 52 | div( 53 | "grapher", 54 | tags$a( 55 | class = "secondary-content blue-text", 56 | href = "https://grapher.network", 57 | tags$i(class="material-icons", "insert_link") 58 | ) 59 | ) 60 | ), 61 | tags$li( 62 | class = "collection-item", 63 | div( 64 | "globe4r", 65 | tags$a( 66 | class = "secondary-content blue-text", 67 | href = "https://globe4r.john-coene.com", 68 | tags$i(class="material-icons", "insert_link") 69 | ) 70 | ) 71 | ), 72 | tags$li( 73 | class = "collection-item", 74 | div( 75 | "g2r", 76 | tags$a( 77 | class = "secondary-content blue-text", 78 | href = "https://g2r.dev", 79 | tags$i(class="material-icons", "insert_link") 80 | ) 81 | ) 82 | ) 83 | ), 84 | p("And many others available on", tags$a("github", href="https://github.com/JohnCoene")) 85 | ) 86 | ) 87 | ) 88 | ``` 89 | 90 |
    -------------------------------------------------------------------------------- /website/material.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Material" 3 | --- 4 | 5 | ```{r, include = FALSE} 6 | library(htmltools) 7 | ``` 8 | 9 |
    10 | 11 |

    Material

    12 | 13 | You __do not__ need to know JavaScript to follow along. 14 | 15 |

    Setup

    16 | 17 | There are two ways to follow along this workshop; from your local machine or from the remote RStudio Cloud projects. 18 | 19 | ```{r, echo=FALSE} 20 | div( 21 | class = "row", 22 | div( 23 | class = "col m6", 24 | div( 25 | class = "card", 26 | div( 27 | class = "card-image", 28 | tags$img(src = "assets/img/laptop.png", class = "responsive-img"), 29 | h1(class = "card-title blue-text text-darken-1", "Local", span("_", class = "orange-text", .noWS = "before")) 30 | ), 31 | div( 32 | class = "card-content", 33 | p("Required:"), 34 | tags$ul( 35 | class = "collection", 36 | tags$li( 37 | class = "collection-item", "A recent installation of R (R >= 3.6.0)" 38 | ), 39 | tags$li( 40 | class = "collection-item", "A text editor or IDE you are comfortable with, e.g.: RStudio." 41 | ), 42 | tags$li( 43 | class = "collection-item", 44 | "Packages devtools and htmlwidgets installed", 45 | tags$pre(code('install.packages(c("devtools", "htmlwidgets"))')) 46 | ), 47 | tags$li( 48 | class = "collection-item", 49 | "Clone or download", 50 | tags$a( 51 | href = "https://github.com/JohnCoene/how-to-build-htmlwidgets", 52 | "github.com/JohnCoene/how-to-build-htmlwidgets" 53 | ), 54 | tags$pre(code('git clone https://github.com/JohnCoene/how-to-build-htmlwidgets.git')), 55 | tags$li( 56 | class = "collection-item", 57 | "Register for e-Rum and the workshop" 58 | ) 59 | ) 60 | ) 61 | ) 62 | ) 63 | ), 64 | div( 65 | class = "col m6", 66 | div( 67 | class = "card", 68 | div( 69 | class = "card-image", 70 | tags$img(src = "assets/img/server.jpg", class = "responsive-img"), 71 | h1(class = "card-title blue-text text-darken-1", "Remote", span("_", class = "orange-text", .noWS = "before")) 72 | ), 73 | div( 74 | class = "card-content", 75 | p("You can use the", tags$a("RStudio Cloud project", href = "https://rstudio.cloud/project/1336120"), 76 | "in the event that you cannot work on your local machine.") 77 | ) 78 | ) 79 | ) 80 | ) 81 | ``` 82 | 83 |
    -------------------------------------------------------------------------------- /website/presentation/dt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnCoene/how-to-build-htmlwidgets/2c185c87559e818f719729661f8d9d33154fc8cf/website/presentation/dt.png -------------------------------------------------------------------------------- /website/presentation/echarts4r.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnCoene/how-to-build-htmlwidgets/2c185c87559e818f719729661f8d9d33154fc8cf/website/presentation/echarts4r.png -------------------------------------------------------------------------------- /website/presentation/globe4r.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnCoene/how-to-build-htmlwidgets/2c185c87559e818f719729661f8d9d33154fc8cf/website/presentation/globe4r.png -------------------------------------------------------------------------------- /website/presentation/grapher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnCoene/how-to-build-htmlwidgets/2c185c87559e818f719729661f8d9d33154fc8cf/website/presentation/grapher.png -------------------------------------------------------------------------------- /website/presentation/index.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "How to build htmlwidgets" 3 | subtitle: "e-Rum 2020 Workshop" 4 | author: "John Coene" 5 | date: "20-06-2020" 6 | output: 7 | xaringan::moon_reader: 8 | lib_dir: libs 9 | css: [default, fc, "style.css"] 10 | nature: 11 | ratio: "16:9" 12 | highlightStyle: default 13 | highlightLines: true 14 | countIncrementalSlides: false 15 | --- 16 | 17 | layout: true 18 | 19 |
    20 | 21 | 22 | 23 | --- 24 | 25 | .centerize[ 26 | 27 | ## Plan 28 | 29 | Introduction to the basics (this) ~15 mins

    30 | Explore htmlwidgets hands-on ~45 mins

    31 | Build a widget for [typed.js](https://github.com/mattboldt/typed.js/)

    32 | Short break

    33 | Build a widget for [gio.js](https://giojs.org/) 34 | 35 | ] 36 | 37 | --- 38 | 39 | .centerize[ 40 | 41 | Resources 42 | 43 | ## htmlwidgets.john-coene.com 44 | 45 | ] 46 | 47 | --- 48 | 49 | ```{r, include = FALSE} 50 | library(emo) 51 | ``` 52 | 53 | .centerize[ 54 | 55 | # What is "htmlwidgets"? 56 | 57 | An R Package to wrap external JavaScript libraries that produce a visual output. 58 | 59 | ] 60 | 61 | --- 62 | 63 | ## Examples 64 | 65 | Visit [gallery.htmlwidgets.org](https://gallery.htmlwidgets.org/) 66 | 67 | .pull-left[ 68 | **plotly** 69 |
    70 | 71 | 72 | **DT** 73 |
    74 | 75 | ] 76 | 77 | .pull-right[ 78 | **leaflet** 79 |
    80 | 81 | 82 | **wordcloud2** 83 |
    84 | 85 | ] 86 | 87 | --- 88 | 89 | ## Shameless Promotions 90 | 91 | A few of the htmlwidgets I have built. 92 | 93 | .pull-left[ 94 | **echarts4r** 95 |
    96 | 97 | 98 | **grapher** 99 |
    100 | 101 | ] 102 | 103 | .pull-right[ 104 | **sigmajs** 105 |
    106 | 107 | 108 | **globe4r** 109 |
    110 | 111 | ] 112 | 113 | --- 114 | 115 | .centerize[ 116 | 117 | ## Candidate Libraries 118 | 119 | Understand what is expected of us 120 | 121 | ] 122 | 123 | --- 124 | 125 | .pull-left[ 126 | ## Plotly 127 | 128 | - Import library 129 | - `
    ` to hold viz 130 | - ` 144 | 145 | 146 | 147 | 148 |
    149 | 150 | 151 | 158 | 159 | 160 | 161 | ``` 162 | 163 | ] 164 | 165 | --- 166 | 167 | .pull-left[ 168 | ## Highchart.js 169 | 170 | - Import library 171 | - `
    ` to hold viz 172 | - ` 186 | 187 | 188 | 189 | 190 |
    191 | 192 | 193 | 207 | 208 | 209 | 210 | ``` 211 | 212 | ] 213 | 214 | --- 215 | 216 | .pull-left[ 217 | ## Chart.js 218 | 219 | - Import library 220 | - `` to hold viz 221 | - ` 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 255 | 256 | 257 | 258 | ``` 259 | 260 | ] 261 | 262 | --- 263 | 264 | .centerize[ 265 | 266 | ## How would this be done in R? 267 | 268 | ] 269 | 270 | --- 271 | 272 | ## Crate a static HTML file 273 | 274 | ```html 275 | 276 | 277 | 278 | 279 | ``` 280 | 281 | --- 282 | 283 | ## Import dependencies 284 | 285 | ```html 286 | 287 | 288 | 289 | 290 | 291 | 292 | ``` 293 | 294 | --- 295 | 296 | ## Create Canvas 297 | 298 | ```html 299 | 300 | 301 | 302 | 303 | 304 |
    305 | 306 | 307 | ``` 308 | 309 | --- 310 | 311 | ## Serialise R object 312 | 313 | ```html 314 | 315 | 316 | 317 | 318 | 319 |
    320 | 321 | 322 | 323 | ``` 324 | 325 | --- 326 | 327 | ## Import custom functions 328 | 329 | ```html 330 | 331 | 332 | 333 | 334 | 335 |
    336 | 337 | 338 | 339 | 340 | ``` 341 | 342 | --- 343 | 344 | .centerize[ 345 | 346 | ## Finally, I need to manage this across Shiny and R markdown... 347 | 348 | ] 349 | 350 | --- 351 | 352 | .centerize[ 353 | 354 | ## It __should be__ complicated 355 | 356 | ] 357 | 358 | --- 359 | 360 | .centerize[ 361 | 362 | Thankfully, it isn't 363 | 364 | ## Meet __htmlwidgets__! 365 | 366 | ] 367 | 368 | --- 369 | 370 | .centerize[ 371 | 372 | ## Let's Start 373 | 374 | First, create a package with `usethis` or the RStudio IDE. 375 | 376 | ```r 377 | usethis::create_package('playground') 378 | ``` 379 | 380 | Then, from the root of the package, build the scaffolding for our visualisation. 381 | 382 | ```r 383 | htmlwidgets::scaffoldWidget("playground") 384 | ``` 385 | 386 | ] -------------------------------------------------------------------------------- /website/presentation/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | How to build htmlwidgets 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 393 | 394 | 395 | 483 | 484 | 503 | 504 | 514 | 515 | 516 | -------------------------------------------------------------------------------- /website/presentation/leaflet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnCoene/how-to-build-htmlwidgets/2c185c87559e818f719729661f8d9d33154fc8cf/website/presentation/leaflet.png -------------------------------------------------------------------------------- /website/presentation/libs/header-attrs-2.2/header-attrs.js: -------------------------------------------------------------------------------- 1 | // Pandoc 2.9 adds attributes on both header and div. We remove the former (to 2 | // be compatible with the behavior of Pandoc < 2.8). 3 | document.addEventListener('DOMContentLoaded', function(e) { 4 | var hs = document.querySelectorAll("div.section[class*='level'] > :first-child"); 5 | var i, h, a; 6 | for (i = 0; i < hs.length; i++) { 7 | h = hs[i]; 8 | if (!/^h[1-6]$/i.test(h.tagName)) continue; // it should be a header h1-h6 9 | a = h.attributes; 10 | while (a.length > 0) h.removeAttribute(a[0].name); 11 | } 12 | }); 13 | -------------------------------------------------------------------------------- /website/presentation/libs/remark-css-0.0.1/default.css: -------------------------------------------------------------------------------- 1 | a, a > code { 2 | color: rgb(249, 38, 114); 3 | text-decoration: none; 4 | } 5 | .footnote { 6 | position: absolute; 7 | bottom: 3em; 8 | padding-right: 4em; 9 | font-size: 90%; 10 | } 11 | .remark-code-line-highlighted { background-color: #ffff88; } 12 | 13 | .inverse { 14 | background-color: #272822; 15 | color: #d6d6d6; 16 | text-shadow: 0 0 20px #333; 17 | } 18 | .inverse h1, .inverse h2, .inverse h3 { 19 | color: #f3f3f3; 20 | } 21 | /* Two-column layout */ 22 | .left-column { 23 | color: #777; 24 | width: 20%; 25 | height: 92%; 26 | float: left; 27 | } 28 | .left-column h2:last-of-type, .left-column h3:last-child { 29 | color: #000; 30 | } 31 | .right-column { 32 | width: 75%; 33 | float: right; 34 | padding-top: 1em; 35 | } 36 | .pull-left { 37 | float: left; 38 | width: 47%; 39 | } 40 | .pull-right { 41 | float: right; 42 | width: 47%; 43 | } 44 | .pull-right ~ * { 45 | clear: both; 46 | } 47 | img, video, iframe { 48 | max-width: 100%; 49 | } 50 | blockquote { 51 | border-left: solid 5px lightgray; 52 | padding-left: 1em; 53 | } 54 | .remark-slide table { 55 | margin: auto; 56 | border-top: 1px solid #666; 57 | border-bottom: 1px solid #666; 58 | } 59 | .remark-slide table thead th { border-bottom: 1px solid #ddd; } 60 | th, td { padding: 5px; } 61 | .remark-slide thead, .remark-slide tfoot, .remark-slide tr:nth-child(even) { background: #eee } 62 | 63 | @page { margin: 0; } 64 | @media print { 65 | .remark-slide-scaler { 66 | width: 100% !important; 67 | height: 100% !important; 68 | transform: scale(1) !important; 69 | top: 0 !important; 70 | left: 0 !important; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /website/presentation/libs/remark-css-0.0.1/fc.css: -------------------------------------------------------------------------------- 1 | /* Various left and right split columns*/ 2 | 3 | .pull-left-70 { float: left; width: 66.5%; } 4 | .pull-right-30 { float: right; width: 28.5%; } 5 | .pull-right-30 ~ * { clear: both; } 6 | 7 | .pull-left-60 { float: left; width: 57%; } 8 | .pull-right-40 { float: right; width: 38%; } 9 | .pull-right-40 ~ * { clear: both; } 10 | 11 | .pull-left-50 { float: left; width: 47.5%; } 12 | .pull-right-50 { float: right; width: 47.5%; } 13 | .pull-right-50 ~ * { clear: both; } 14 | 15 | .pull-left-40 { float: left; width: 38%; } 16 | .pull-right-60 { float: right; width: 57%; } 17 | .pull-right-60 ~ * { clear: both; } 18 | 19 | .pull-left-30 { float: left; width: 28.5%; } 20 | .pull-right-70 { float: right; width: 66.5%; } 21 | .pull-right-70 ~ * { clear: both; } 22 | 23 | /* headers and fonts*/ 24 | h1, h2, h3 { 25 | margin-top: 0; 26 | padding-top: 0.5em; 27 | font-family: 'Oswald'; 28 | font-weight: 400; 29 | margin-left: 0; 30 | } 31 | 32 | .large { 33 | font-size: 23px; 34 | } 35 | 36 | .small { 37 | font-size: 15px; 38 | } 39 | 40 | /* regular slide */ 41 | .remark-slide-content { 42 | background-color: #FAFAFA; 43 | border-bottom: 5px solid #ff5f0f; 44 | font-size: 20px; 45 | font-weight: 300; 46 | line-height: 1.2; 47 | padding: 1em 3em 1em 3em; 48 | } 49 | 50 | /* inverse slide */ 51 | .inverse { 52 | background-color: #121316; 53 | color: #ffffff; 54 | } 55 | 56 | /* header colors are the same regardless of inverse or not*/ 57 | .remark-slide-content h1, .inverse h1 { 58 | color: #ff5f0f; 59 | font-size: 35px; 60 | } 61 | 62 | .remark-slide-content h2, .inverse h2 { 63 | color: #b7b7b7; 64 | font-size: 30px; 65 | } 66 | 67 | .remark-slide-content h3, .inverse h3 { 68 | color: #5b5b5b; 69 | font-size: 25px; 70 | } 71 | 72 | /* title slide */ 73 | .title-slide { 74 | background-color: #121316; 75 | } 76 | 77 | .title-slide h1 { 78 | color: #ff5f0f; 79 | font-size: 50px; 80 | font-weight: 500; 81 | text-align: left; 82 | margin-left: 40px; 83 | padding-top: 60px; 84 | } 85 | .title-slide h2 { 86 | color: #FAFAFA; 87 | font-size: 35px; 88 | font-weight: 350; 89 | text-align: left; 90 | margin-left: 40px; 91 | margin-top: -25px; 92 | padding-bottom: -20px; 93 | } 94 | .title-slide h3 { 95 | color: #b7b7b7; 96 | font-size: 25px; 97 | font-weight: 300; 98 | text-align: left; 99 | margin-left: 40px; 100 | margin-bottom: 0; 101 | } 102 | 103 | /* slide number */ 104 | .remark-slide-number { 105 | font-size: 10pt; 106 | font-family: 'EB Garamond'; 107 | color: #272822; 108 | opacity: 1; 109 | } 110 | .inverse .remark-slide-number { 111 | font-size: 10pt; 112 | font-family: 'EB Garamond'; 113 | color: #FAFAFA; 114 | opacity: 1; 115 | } 116 | 117 | /* code */ 118 | .remark-inline-code { 119 | color: #ff4c00; 120 | border-radius: 1px; 121 | padding: 0; 122 | background: #e0e0e0; 123 | } 124 | 125 | .inverse .remark-inline-code { 126 | background: #333333; 127 | } 128 | 129 | .remark-code-line-highlighted { 130 | background-color: #ffead3; 131 | } 132 | -------------------------------------------------------------------------------- /website/presentation/plotly.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnCoene/how-to-build-htmlwidgets/2c185c87559e818f719729661f8d9d33154fc8cf/website/presentation/plotly.png -------------------------------------------------------------------------------- /website/presentation/sigmajs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnCoene/how-to-build-htmlwidgets/2c185c87559e818f719729661f8d9d33154fc8cf/website/presentation/sigmajs.png -------------------------------------------------------------------------------- /website/presentation/style.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css?family=Quicksand|Ubuntu+Mono&display=swap'); 2 | 3 | body{ 4 | font-family: 'Quicksand', sans-serif; 5 | } 6 | 7 | .title-slide{ 8 | background-color:#2a388f; 9 | } 10 | 11 | .title-slide > h1, .title-slide > h2{ 12 | color: #d0d3d6 !important; 13 | } 14 | 15 | h1,h2,h3,h4{ 16 | font-family: 'Quicksand', sans-serif; 17 | } 18 | 19 | .inverse{ 20 | background-color:#2a388f; 21 | } 22 | 23 | h1, h2{ 24 | color: #2a388f !important; 25 | } 26 | 27 | p{ 28 | color: #2a388f !important; 29 | } 30 | 31 | a{ 32 | color: #333e48 !important; 33 | text-decoration: underline; 34 | } 35 | 36 | pre, code{ 37 | font-family: 'Ubuntu Mono', monospace; 38 | } 39 | 40 | .centerize { 41 | text-align: center; 42 | margin: 0; 43 | position: absolute; 44 | top: 50%; 45 | left: 50%; 46 | -ms-transform: translate(-50%, -50%); 47 | transform: translate(-50%, -50%); 48 | width: 90%; 49 | } 50 | 51 | .remark-slide-content{ 52 | border-bottom: 5px solid #2a388f; 53 | } 54 | 55 | .pull-left { 56 | float: left; 57 | width: 47%; 58 | } 59 | .pull-right { 60 | float: right; 61 | width: 47%; 62 | } 63 | 64 | div.my-footer { 65 | background-color: rgba(255,255,255,0); 66 | position: absolute; 67 | bottom: 0px; 68 | left: 0px; 69 | height: 20px; 70 | width: 100%; 71 | } 72 | div.my-footer span { 73 | font-size: 10pt; 74 | color: #2a388f; 75 | position: absolute; 76 | left: 15px; 77 | bottom: 5px; 78 | } -------------------------------------------------------------------------------- /website/presentation/wordcloud2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnCoene/how-to-build-htmlwidgets/2c185c87559e818f719729661f8d9d33154fc8cf/website/presentation/wordcloud2.png -------------------------------------------------------------------------------- /website/program.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Program" 3 | --- 4 | 5 |
    6 | 7 |

    Program

    8 | 9 |

    What you will learn

    10 | 11 | You will learn how to build your own packages for visualisations using [htmlwidgets](http://www.htmlwidgets.org/). htmlwidgets is the backbone of packages such as [leaflet](https://rstudio.github.io/leaflet/), [plotly](https://plotly.com/r/), [highcharter](http://jkunst.com/highcharter/), and many others. The aim is that by the end of the workshop you will be able to write your own such packages! 12 | 13 |

    Methodology

    14 | 15 | Learn by doing. I believe this remains the best way to go about it; the workshop will therefore have you write a quite a bit of code. 16 | 17 |

    Plan

    18 | 19 | This is a rough plan, things are likely to change depending on the pace we pick up. The point is not to rush things but if great progress is made, we can delve into more advanced stuff at the end. 20 | 21 |
      22 |
    • Introduction to the basics (20-30 mins)

    • 23 |
    • Explore htmlwidgets (45-60 mins)

    • 24 |
    • Coffee Break

    • 25 |
    • Build our second widget (60 mins)

    • 26 |
    • Additional functionalities (30 mins)

    • 27 |
    28 | 29 |
    --------------------------------------------------------------------------------