Giter VIP home page Giter VIP logo

crosstalk's Introduction

Crosstalk

CRAN R-CMD-check

Crosstalk is a package for R that enhances the htmlwidgets package. It extends htmlwidgets with a set of classes, functions, and conventions for implementing cross-widget interactions (currently, linked brushing and filtering).

Find out more at the documentation website: http://rstudio.github.io/crosstalk/

Building JavaScript assets

(This section is only for developers who intend to modify the JavaScript source code in Crosstalk itself.)

The JavaScript source code in this package lives under javascript/, however the copy that is actually loaded and used during runtime is in minified form at inst/www/js/. Anytime you make changes to javascript/ source files, you must rebuild the minified JS.

To set up your repo for building the minified JS:

  1. First install nvm if you don't have it already.
  2. In the crosstalk repo's root directory, run nvm install; this will install the version of Node.js we need.
  3. Run nvm use to switch to our version of Node.js.
  4. Run npm install to install all Node.js dependencies.

To actually build the minified JS:

  1. Run nvm use (only needed once per terminal session).
  2. Run node node_modules/.bin/grunt (or if you have installed grunt-cli globally, you can just run grunt).

This will run unit tests, lint, and build the JavaScript dist bundle. When making changes to the JavaScript code, you must always do this first, and then build the R package as normal.

crosstalk's People

Contributors

cpsievert avatar daattali avatar dmurdoch avatar gadenbuie avatar jbkunst avatar jcheng5 avatar kent37 avatar michaelchirico avatar schloerke avatar vladsavelyev avatar wch avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

crosstalk's Issues

Is it possible to remove a selection made by `filter_select`?

After creating a figure (see code below) all points are displayed. Making a selection displays the selected points only. But how to remove the selection, to display all points, without reloading the page? Tried deleting all selection, double clicking, but nothing works except reloading page.

library(plotly)
foo <- data.frame(foo = c("A","B","C"), fc = c(2,3,-1), val = c(0.1,0.01,0.4))
shared_prot <- SharedData$new(foo, ~foo, group = "Choose protein")


x <- filter_select("foo", "Foo :", shared_prot, ~foo)
p <- plot_ly(shared_prot, x = ~fc, y = ~ val , type="scatter" , mode="markers") %>% highlight("plotly_click")# %>% add_markers() 
bscols(x,p)

best regards

Add extraInfo (or similar) argument to SharedData$new()

In rgl, I need to record rgl's internal ID number in a SharedData object. I've also found it convenient to store some extra information about how I want that object to be displayed when selected or filtered.

I've done this by adding the information as extra attributes on the dataframe that goes to SharedData$new(), but it would be cleaner if that method had an optional parameter to hold this info, and a defined way to extract it in Javascript.

Leaflet in crosstalk does not work with flexdashboard

Hello,

if I use your code example below it works properly. But when it is used inside flexdashboard it does not display anymore the leaflet map.
To test it just replace output: html_document with:

output:
flexdashboard::flex_dashboard:
orientation: rows

Below is the example that works without well without flexdashboard.

title: Fiji earthquakes
output: html_document

library(crosstalk)
library(leaflet)
library(DT)

# Wrap data frame in SharedData
sd <- SharedData$new(quakes[sample(nrow(quakes), 100),])

# Create a filter input
filter_slider("mag", "Magnitude", sd, column=~mag, step=0.1, width=250)

# Use SharedData like a dataframe with Crosstalk-enabled widgets
bscols(
  leaflet(sd) %>% addTiles() %>% addMarkers(),
  datatable(sd, extensions="Scroller", style="bootstrap", class="compact", width="100%",
    options=list(deferRender=TRUE, scrollY=300, scroller=TRUE))
)

Don't know how to get location data from object of class SharedData

Get the following error.

Error in pointData.default(data) : 
  Don't know how to get location data from object of class SharedData

When running the following code

library(leaflet)
library(crosstalk)

shared_quakes <- SharedData$new(quakes[sample(nrow(quakes), 100),])

bscols(
  leaflet(shared_quakes, width = "100%", height = 300) %>%
    addTiles() %>%
    addMarkers(),
  d3scatter(shared_quakes, ~depth, ~mag, width = "100%", height = 300)
        )

Highlighting doesn't work when filtering on the same variable as the plot coloring

The problem occurs when you select a species from the dropdown and then lasso one graph -- the other graph doesn't change. When you only filter by other_factor (or don't filter at all) highlighting occurs as normal.

For what it's worth this issue occurs in my own script when I filter on any column -- regardless of if it's used for coloring or anything else. I only noticed that it matters which column you filter on while I was reproducing the problem.

Thank you for an otherwise amazing package!

require(crosstalk)
require(plotly)

iris$other_factor <- sample(x = c("a", "b", "c"), size = nrow(iris), replace = T)
iris_shared <- SharedData$new(iris)

bscols(widths = c(6, 6, 6, 6),
  filter_select("species", "species", iris_shared, ~Species, multiple = T),
  filter_select("otherfactor", "other factor", iris_shared, ~other_factor, multiple = T),
  
  plot_ly(data = iris_shared, x = ~Sepal.Length, y = ~Sepal.Width, color = ~Species) %>%
    add_markers() %>%
    layout(dragmode = "lasso") %>%
    highlight(on = "plotly_selected", off = "plotly_deselect"),
  
  plot_ly(data = iris_shared, x = ~Petal.Length, y = ~Petal.Width, color = ~Species) %>%
    add_markers() %>%
    layout(dragmode = "lasso") %>%
    highlight(on = "plotly_selected", off = "plotly_deselect")
)

linking between leaflet and d3scatter not working

Firstly, my apologies if this is an error on my part. I have been trying to work with the example provided on
http://rstudio-pubs-static.s3.amazonaws.com/209203_02f14fea3274448bbbf8d04c99c6051b.html

There seems to be some oddities with this code (perhaps it is older?) - the sd$transform(as.data.frame) does not seem to work as is. I created work around using:
sd_df <- SharedData$new(world@data, group = sd$groupName())

This may be the culprit or not. When I amend the code on the site above - and run. I am able to get linking between DT and d3scatter but not with leaflet map, as the version above shows. It seems like the SharedData is not being respected when used with leaflet?

Can others re-create the issue that I am experiencing? Does the native code posted on the website work for others? Again - if this is an oversight on my part I apologize

The code I am using is (knitted to flexdashboard:

`---
title: "Gapminder data using crosstalk"
output:
flexdashboard::flex_dashboard:
orientation: rows
theme: lumen

library(flexdashboard) ; library(crosstalk) ; library(dplyr) ; library(rgdal)
## This is the site works and where I have modified code from: http://rstudio-pubs-static.s3.amazonaws.com/209203_02f14fea3274448bbbf8d04c99c6051b.html
## These are the steps for creating (and writing) the SpatialPolygonsDataFrame using data from the gapminder package and the boundary vector layer supplied by www.naturalearthdata.com in the rworldmap package.

library(gapminder) ; library(countrycode)
df <- gapminder %>% filter(year == 2007) %>% mutate(ISO3 = countrycode(country, "country.name", "iso3c"), gdpPercap = round(gdpPercap, 0), lifeExp = round(lifeExp, 0))
# This removes a duplicate ISO3 ref (KOR) - save for a more robust solution. 
df<- df %>% filter(country != "Korea, Dem. Rep.")
###

### ### Run this code chunk for the first time

#library(rworldmap) ; library(spatialEco)
#data(countriesLow)
#world <- countriesLow
#world <- sp::merge(world, df, by.x = "ISO3", by.y = "ISO3", sort = FALSE)
#world <- world[, c("country", "continent.y",  "year", "lifeExp", "pop", "gdpPercap")] 
#world_NA <- world[is.na(world@data$country) ,]
#writeOGR(world_NA, ".", "world_NA", driver="ESRI Shapefile")
#world <- sp.na.omit(world, col.name = "country")
#writeOGR(world, ".", "world", driver="ESRI Shapefile")

### ### 

world <- readOGR(".", "world", verbose = FALSE)
world@data <- rename(world@data, continent = cntnnt_)
world_NA <- readOGR(".", "world_NA", verbose = FALSE)

sd <- SharedData$new(world)
#Added this to allow sharedDate to work ()
sd_df <- SharedData$new(world@data, group = sd$groupName())

Inputs {.sidebar}

#sd$transform(as.data.frame) does not work?  (throughout the replaced sd with sd_df)
filter_slider("lifeExp", "Life expectancy (years)", sd_df, ~lifeExp)
filter_slider("gdpPrcp", "Income per person ($)", sd_df, ~gdpPrcp)

This R Markdown document uses the experimental crosstalk R package to enable interactivity between different widgets that display the same Gapminder data.

Credits:
The following packages were used: flexdashboard, rgdal, dplyr, leaflet, d3scatter, DT, gapminder, countrycode,
rworldmap, and spatialEco.

Row {data-height=550}

library(leaflet)
pal <- colorFactor(c("#1f77b4", "#ff7f0e", "#2ca02c", "#d62728", "#9467bd"), domain = c("Africa", "Americas", "Asia", "Europe", "Oceania"), ordered = FALSE)
leaflet(sd_df) %>% 
setView(9.998176, 14.531777, zoom = 2) %>%
addProviderTiles("CartoDB.Positron") %>% 
addPolygons(data = world_NA, color = "#969696", weight = 1, fillColor = "#808080") %>% 
addPolygons(data = world, color = "#969696", weight = 2, fillColor = ~pal(continent), fillOpacity = 0.8, label = ~as.character(country))

Row {data-height=450}

library(d3scatter)
d3scatter(sd_df, x = ~gdpPrcp, y = ~lifeExp, color = ~continent, x_label = "Income per person", y_label = "Life expectancy")

library(DT)
datatable(sd_df, rownames = FALSE, extensions = 'Scroller', 
          options = list(scrollY = 200, scroller = TRUE, columnDefs = list(list(className = 'dt-left', targets = 0:3))))

SharedData objects should have the same group by default

I know this is an opinion, but I think ~90% of the time this is the right behavior. Sometimes it's confusing even to me why a link isn't working -- then I eventually remember to match the group argument in each SharedData$new() call.

filter_select default value

Would it be possible, please, to add a selected parameter cf for shiny selectInput
i.e
"The initially selected value (or multiple values if multiple = TRUE). If not specified then defaults to the first value for single-select lists and no values for multiple select lists."

Also a width option would be useful

SharedData behaves unexpectedly when passed a character vector as its key

The following reprex shows the issue.

library(crosstalk)
SharedData$new(data.frame(id = letters[1:10], val = rnorm(10L)))$key()
#>  [1] "1"  "2"  "3"  "4"  "5"  "6"  "7"  "8"  "9"  "10"
SharedData$new(data.frame(id = letters[1:10], val = rnorm(10L)), key = ~id)$key()
#>  [1] a b c d e f g h i j
#> Levels: a b c d e f g h i j
SharedData$new(data.frame(id = letters[1:10], val = rnorm(10L)), key = "id")$key()
#> [1] "id"

If, when typeof(key) == "character", the intention is that represents the name of the column in the data.frame, then it's flat broken.

If, when typeof(key) == "character", the intention is it should be a vector of values the same length as the data set, then two things need to occur.

  1. initialize() needs to check that the key length matches the number of rows of data
  2. this sentence in the documentation should be cleaned up:

Character vector or one-sided formula that indicates the name of the column that represents the key or ID of the data frame

A reasonable reading of that is :

Character vector or one-sided formula that indicates the name of the column that represents the key or ID of the data frame

not

Character vector or one-sided formula that indicates the name of the column that represents the key or ID of the data frame

Let me know which it is and I'll attempt a PR.

Add support for leaflet sf

When using a leaflet map that relies on sf data, the linking doesn't work:

  library(crosstalk)
  library(d3scatter)
  library(leaflet)
  library(sf)
  library(tidyverse)
  
  demo(nc, ask = FALSE, echo = FALSE, verbose = FALSE)
  
  nc_pts_WGS84 <- nc %>% st_transform(4326) %>% st_centroid()
  
  shared_nc <- SharedData$new(nc_pts_WGS84)
  
  bscols(leaflet(width = "100%", height = 300) %>%
           addTiles() %>%
           addMarkers(data = nc_pts_WGS84),
         d3scatter(shared_nc, ~BIR74, ~SID79, width = "100%", height = 300)
         
  )

A screenshot of the result (notice the absence of the leaflet rectangular select tool):

crosstalk-ss

shiny should be in Suggests package instead of Imports

crosstalk does not require shiny, but installing crosstalk causes Shiny to be installed, which brings in a lot of other dependencies. This is an issue when installing a crosstalk-enabled package like DT for a situation where shiny isn't actually required or desired, like r-lib/devtools#2112.

If shiny were in Suggests instead of Imports, then it would reduce the cost of installing a package like DT. It likely won't cause any problems for someone who actually uses DT with Shiny, because they would have Shiny installed anyway.

Security Vulnerability

My company uses Blackduck ( https://www.blackducksoftware.com/ ) to monitor security issues in our R packages and flagged crosstalk with a security vulernability in jQuery Core. The descriptions says: JQuery Core contains a flaw that allows a cross-site scripting attack. This flaw exists because the ajax/scripts.js script does not validate input when handling third-party text before returning it to users. This may allow a remote attacked to create a specially crafted request that would execute arbitrary script code in user's browser session within the trust relationship between their browser and the server.

Can you address this security issue?

filter_slider should round min/max values and tick labels

This example:

library(crosstalk)
set.seed(2017)
sd <- SharedData$new(data.frame(x = rnorm(10)))
filter_slider("x", "x", sd, ~ x, round = 0)

asks to round the values to zero decimal places, but the displayed values aren't rounded at all. The min and max values are displayed as "36,645,575,099" and "1.56,322,261,81" (with the leading digits of the min cut off), and the ticks are labelled to 2 decimal places.

Add support for single slider type

I would like at make a feature request for changing the slider from double to single. I'm happy to work on this feature if I can get a better understanding of the workflow of this package. I see there's data-type defined in controls.R but don't know what else needs changed.

crosstalk and DT buttons

Hello,

This is not an issue, its more of a suggestion for something i think would be a cool feature.

In particular, crosstalk works with DT for displaying data within html documents, but its not capable to support DT once the extension 'buttons' is included.

DT itself can support filtering and exporting data from tables, but I think t would be more intuitive it one could use cross-talk type of filtering through buttons within a sidebar, and once the filtering is conducted, export the data through a DT button.

Would something like this be feasable in the short-term?

Best regards, Stefan.

problem selecting barplot elements

how can i select barplots with crosstalk?

library(crosstalk)
library(plotly)
shared_data <- SharedData$new(data.frame(x=1:10,y=rnorm(10),z=sample(1:4,10,replace = TRUE)),key = ~x)

p1 <- plot_ly(shared_data,x=~x,y=~y,text=~z,type='bar')%>%highlight(on = 'plotly_click',color='red')
p2 <- plot_ly(shared_data,x=~x,y=~abs(y),text=~z,type='bar')%>%highlight('plotly_click',color='red')
p3 <- plot_ly(shared_data,x=~x,y=~y,text=~z,type='scatter',mode='markers')%>%highlight('plotly_selected',color='red')

bscols(subplot(p1_ly, subplot(p2_ly,p3_ly,nrows=1),nrows=2))
Session info ---------------------------------------------------------------------------------------------
 setting  value                       
 version  R version 3.3.3 (2017-03-06)
 system   x86_64, darwin13.4.0        
 ui       RStudio (1.1.383)           
 language (EN)                        
 collate  en_US.UTF-8                 
 tz       America/New_York            
 date     2018-02-05                  

Packages -------------------------------------------------------------------------------------------------
 package      * version    date       source                            
 assertthat     0.2.0      2017-04-11 CRAN (R 3.3.2)                    
 base         * 3.3.3      2017-03-07 local                             
 bindr          0.1        2016-11-13 CRAN (R 3.3.2)                    
 bindrcpp       0.2        2017-06-17 CRAN (R 3.3.2)                    
 colorspace     1.3-2      2016-12-14 CRAN (R 3.3.2)                    
 crosstalk    * 1.0.1      2018-02-05 Github (rstudio/crosstalk@0a3b8f4)
 curl           3.1        2017-12-12 CRAN (R 3.3.2)                    
 data.table     1.10.4-3   2017-10-27 CRAN (R 3.3.2)                    
 datasets     * 3.3.3      2017-03-07 local                             
 devtools       1.13.4     2017-11-09 CRAN (R 3.3.2)                    
 digest         0.6.13     2017-12-14 CRAN (R 3.3.2)                    
 dplyr          0.7.4      2017-09-28 CRAN (R 3.3.2)                    
 ggplot2      * 2.2.1.9000 2018-02-04 Github (hadley/ggplot2@a2dc248)   
 glue           1.2.0      2017-10-29 CRAN (R 3.3.2)                    
 graphics     * 3.3.3      2017-03-07 local                             
 grDevices    * 3.3.3      2017-03-07 local                             
 grid           3.3.3      2017-03-07 local                             
 gtable         0.2.0      2016-02-26 CRAN (R 3.3.0)                    
 htmltools      0.3.6      2017-04-28 CRAN (R 3.3.2)                    
 htmlwidgets    0.9        2017-07-10 CRAN (R 3.3.2)                    
 httpuv         1.3.5      2017-07-04 CRAN (R 3.3.2)                    
 httr           1.3.1      2017-08-20 CRAN (R 3.3.2)                    
 jsonlite       1.5        2017-06-01 CRAN (R 3.3.2)                    
 lazyeval       0.2.1      2017-10-29 CRAN (R 3.3.2)                    
 magrittr       1.5        2014-11-22 CRAN (R 3.3.0)                    
 memoise        1.1.0      2017-04-21 CRAN (R 3.3.2)                    
 methods      * 3.3.3      2017-03-07 local                             
 mime           0.5        2016-07-07 CRAN (R 3.3.0)                    
 munsell        0.4.3      2016-02-13 CRAN (R 3.3.0)                    
 pkgconfig      2.0.1      2017-03-21 CRAN (R 3.3.2)                    
 plotly       * 4.7.1      2017-07-29 CRAN (R 3.3.2)                    
 plyr           1.8.4      2016-06-08 CRAN (R 3.3.0)                    
 purrr          0.2.4      2017-10-18 CRAN (R 3.3.2)                    
 R6             2.2.2      2017-06-17 CRAN (R 3.3.2)                    
 RColorBrewer   1.1-2      2014-12-07 CRAN (R 3.3.0)                    
 Rcpp           0.12.14    2017-11-23 CRAN (R 3.3.2)                    
 remotes        1.1.1      2017-12-20 CRAN (R 3.3.2)                    
 rgeolocate     1.0.1      2017-08-02 CRAN (R 3.3.2)                    
 rlang          0.1.6.9003 2018-02-04 Github (tidyverse/rlang@c6747f9)  
 scales         0.5.0.9000 2017-11-16 Github (hadley/scales@d767915)    
 shiny          1.0.5      2017-08-23 CRAN (R 3.3.2)                    
 stats        * 3.3.3      2017-03-07 local                             
 tibble         1.3.4      2017-08-22 CRAN (R 3.3.2)                    
 tidyr          0.7.2      2017-10-16 CRAN (R 3.3.2)                    
 tools          3.3.3      2017-03-07 local                             
 utils        * 3.3.3      2017-03-07 local                             
 viridisLite    0.2.0      2017-03-24 CRAN (R 3.3.2)                    
 withr          2.1.1.9000 2018-02-04 Github (jimhester/withr@df18523)  
 xtable         1.8-2      2016-02-05 CRAN (R 3.3.0)                    
 yaml           2.1.16     2017-12-12 CRAN (R 3.3.2)

How do I handle layout when I do not want to use bscols?

library(crosstalk)
library(d3scatter)
shared_mtcars <- SharedData$new(mtcars)

bscols(
            filter_select("auto", "Automatic", shared_mtcars, ~ifelse(am == 0, "Yes", "No")),
           d3scatter(shared_mtcars, ~wt, ~mpg, ~factor(cyl), width="100%", height=250)
)   

crosstalkissue_2

If I remove bscols() only the plot gets shown. There does not appear to be a bsrows equivalent. If i try list, the filter appears momentarily but is then replaced by plot

Filter_select and crosstalk not appearing together or working

This may be related to #34 but wanted to post in case it's not. In this flexdashboard the filter doesn't appear with the graph and the following error appears:

Warning in origRenderFunc() :
Ignoring explicitly provided widget ID "187c1d327e87"; Shiny doesn't use them

Here is the code:

---
title: "Flower Selection"
output: 
  flexdashboard::flex_dashboard:
orientation: columns
vertical_layout: fill
---

```{r setup,include=FALSE}
library(shiny)
library(flexdashboard)
library(DT)
library(plotly)
library(ggplot2)
library(crosstalk)
```

Sidebar {.sidebar}


Download and upload data from:  https://osdn.net/frs/g_redir.php?m=netix&f=%2Firisdss%2FIRIS.csv

{r,input_panel}
fileInput('file1', 'Choose CSV File',
                         accept=c('text/csv', 
                                  'text/comma-separated-values,text/plain', 
                                  '.csv'))
               tags$br()
               checkboxInput('header', 'Header', TRUE)
               radioButtons('sep', 'Separator',
                            c(Comma=',',
                              Semicolon=';',
                              Tab='\t'),
                            ',')
               radioButtons('quote', 'Quote',
                            c(None='',
                              'Double Quote'='"',
                              'Single Quote'="'"),
                            '"')

  data_1<-reactive({
  req(input$file1)

    inFile <- input$file1 

    df <- read.csv(inFile$datapath, header = input$header, sep = input$sep,
               quote = input$quote,col.names=c("f1","m1","f2","m2","f3","m3","f4","m4","Species"))

})
```


Column {data-width=600}
=====================================

### Chart

```{r echo=FALSE}
ui <- fluidPage(
fluidRow(
 column(8, plotlyOutput("plot1"))
   )
)

server <- function(input, output, session) {

output$plot1<-renderPlotly({

dat1<- SharedData$new(data_1)

filter_select("Flower","Pick a Flower",dat1,~Species)

p<-ggplot(dat1,aes(f1,f2,group=Species))+geom_line()
ggplotly(p)
})
}
shinyApp(ui, server)
```

Feature request: Support styling of controls

There doesn't seem to be any easy way to add CSS styles to controls by, for example, setting class or id of the generated HTML. Support for adding bootstrap classes to checkboxes would be nice.

add range selection/filter handle?

As I work through converting some htmlwidgets, a range selection/ filter handle seems like a nice convenience, especially for time based data. In my mind, the handle would only maintain the closed interval from a selection or filter rather than all the keys within the interval.

SharedData across shiny modules

I am having some trouble understanding the scope of SharedData objects across modules. I am not 100% whether this is a shiny callModule question or even a plotly question, as it involves all three (i.e. crosstalk, plotly, modules).

Below is an app with a plotly scatterplot that has selection enabled. Whenever I select points on the graph, the selection should get updated in the verbatimTextOutput element.

mozilla_firefox

This works fine without using modules:

library(tidyverse)
library(plotly)
library(crosstalk)


ui <- bootstrapPage(
  wellPanel(
    plotlyOutput(outputId = "plot"),
    verbatimTextOutput(outputId = "selection")
  )
)

server <- function(input, output, session) {

  data <- mtcars %>%
    rownames_to_column("name") %>%
    SharedData$new()

  output$plot <- renderPlotly({
    plot_ly(data = data, x = ~disp, y = ~mpg, mode = "marker", type = "scatter") %>%
      highlight(on = "plotly_selected", off = "plotly_deselect")
  })

  output$selection <- renderPrint({
    print(data$selection())
  })

}


shinyApp(ui = ui, server = server)

This also works when using modules and passing the SharedData object to the module:

library(tidyverse)
library(plotly)
library(crosstalk)


testModuleUI <- function(id, label = 'testModule'){
  ns <- NS(id)
  wellPanel(
    plotlyOutput(outputId = ns("plot")),
    verbatimTextOutput(outputId = ns("selection"))
  )
}

testModule <- function(input, output, session, data){

  output$plot <- renderPlotly({
    plot_ly(data = data, x = ~disp, y = ~mpg, mode = "markers", type = "scatter") %>%
      highlight(on = "plotly_selected", off = "plotly_deselect")
  })

  output$selection <- renderPrint({
    print(data$selection())
  })

}


ui <- bootstrapPage(
  testModuleUI(id = "testModule")
)

server <- function(input, output, session) {
  data <- mtcars %>%
    rownames_to_column("name") %>%
    SharedData$new()

  callModule(module = testModule, id = "testModule", data = data)
}


shinyApp(ui = ui, server = server)

However, when the SharedData is created in the module and all of a sudden the selections do not update up in the verbatimTextOutput.

library(tidyverse)
library(plotly)
library(crosstalk)


testModuleUI <- function(id, label = 'testModule'){
  ns <- NS(id)
  wellPanel(
    plotlyOutput(outputId = ns("plot")),
    verbatimTextOutput(outputId = ns("selection"))
  )
}

testModule <- function(input, output, session){

  data <- mtcars %>%
    rownames_to_column("name") %>%
    SharedData$new()

  output$plot <- renderPlotly({
    plot_ly(data = data, x = ~disp, y = ~mpg, mode = "markers", type = "scatter") %>%
      highlight(on = "plotly_selected", off = "plotly_deselect")
  })

  output$selection <- renderPrint({
    print(data$selection())
  })

}


ui <- bootstrapPage(
  testModuleUI(id = "testModule")
)

server <- function(input, output, session) {
  callModule(module = testModule, id = "testModule")
}


shinyApp(ui = ui, server = server)

Where am I not understanding how this works?

Thank you for your help!

Crosstalk with leaflet on Windows - basemap will not draw in Chrome/Firefox/IE

I'm reporting this to crosstalk. Not sure if it should be cross-reported in leaflet.

While exploring flexdashboards I discovered this problem when starting to use crosstalk with DT, leaflet and via crosstalk. When I compiled the rmarkdown, the resulting leaflet map will not draw with a basemap -- when opening the resulting html in a standard web browser.

I want to note, the base map drew fine in the Rstudio's web browser. But, also, when trying to open the resulting knitted html page in a standard browser, the map did have accurately geolocated points -- just no basemap. All the while the web browser kept churning until it timed out.

I've reproduced this error on 4 different Windows workstations. But, I cannot reproduce the problem using R/Rstudio on a Linux docker container.

The other problem, after it arose, I could no longer create any leaflet maps, regardless of flexdashboards, DT, or crosstalk. To fix this problem I eventually installed the original leaflet version.

I tried several different versions of leaflet (1.1.1 and 1.1.0.90000) until going back to version 1.0.1. At that point (1.0.1) I can create leaflet maps again and they work in standard browsers.

I still cannot generate crosstalks that work with DT and leaflet -- again, unless on a linux-based R.

The procedures I followed to get my leaflet working again are here - https://github.com/libjohn/crosstalk-leaflet-error/tree/master#crosstalk-leaflet-error. Files that reproduce the problem are in the same repo.

Possible to force filter_select to display as ASCII strings?

I'd like to display my filter_select options in the drop down menu as:
"< $10M" and "> $10M"
instead of
"< $10M" and "> $10M"

I'm probably searching the wrong keywords to find a quick solution. Is this a possibility? How do I achieve this?

Unable to select first element

Hello,

First let me say thank you for this package!

Unfortunately, I am unable to select the first item when using ggplotly and crosstalk, but everything else functions as intended for all subsequent rows. I have tested bar plots and scatter plots which are both affected by this. I have attached a gif below to better explain the issue I am experiencing, alongside some reproducible code. It does appear to register the click as the data table updates to state "No matching records found".

test

I am unsure whether to post this here or to the plotly github, so please let me know if you need any further information. I have updated all of the packages to the latest github versions and I have put my code into a gist below. I've also put the webpage (and code) inside the attached zip.

Thank you.

Code: https://gist.github.com/AdamLeckenby/199e34d21a0f3bd131f138b492dbfc90

Plot_Issue.zip

Session Info:

R version 3.4.4 (2018-03-15)
Platform: x86_64-w64-mingw32/x64 (64-bit)
Running under: Windows >= 8 x64 (build 9200)

Matrix products: default

locale:
[1] LC_COLLATE=English_United Kingdom.1252 LC_CTYPE=English_United Kingdom.1252 LC_MONETARY=English_United Kingdom.1252
[4] LC_NUMERIC=C LC_TIME=English_United Kingdom.1252

attached base packages:
[1] stats graphics grDevices utils datasets methods base

other attached packages:
[1] crosstalk_1.0.1 DT_0.4.5 flexdashboard_0.5.1 dplyr_0.7.4 plotly_4.7.1.9000 ggplot2_2.2.1.9000

loaded via a namespace (and not attached):
[1] Rcpp_0.12.16 pillar_1.2.1 compiler_3.4.4 plyr_1.8.4 bindr_0.1.1 tools_3.4.4
[7] digest_0.6.15 evaluate_0.10.1 jsonlite_1.5 tibble_1.4.2 gtable_0.2.0 viridisLite_0.3.0
[13] pkgconfig_2.0.1 rlang_0.2.0.9001 shiny_1.0.5 yaml_2.1.18 bindrcpp_0.2 withr_2.1.2
[19] stringr_1.3.0 httr_1.3.1 knitr_1.20 htmlwidgets_1.0 rprojroot_1.3-2 grid_3.4.4
[25] glue_1.2.0 data.table_1.10.4-3 R6_2.2.2 rmarkdown_1.9 purrr_0.2.4 tidyr_0.8.0
[31] magrittr_1.5 backports_1.1.2 scales_0.5.0.9000 htmltools_0.3.6 assertthat_0.2.0 mime_0.5
[37] xtable_1.8-2 colorspace_1.3-2 httpuv_1.3.6.2 labeling_0.3 stringi_1.1.7 lazyeval_0.2.1
[43] munsell_0.4.3

filter_select and filter_checkbox tamper with flexdashboard navbar

I've been experimenting with crosstalk in a flexdashboard and I noticed filter_select and filter_checkbox change the navbar color to black and the text to grey.

Minimal reprex:


---
title: "Untitled"
output: 
  flexdashboard::flex_dashboard:
    orientation: columns
    vertical_layout: fill

---

Page 1
===================================== 

Column 
-----------------------------------------------------------------------

### Chart A

```{r}
library(htmltools)
library(crosstalk)
library(leaflet)

sd <- SharedData$new(quakes)

filter_slider("mag", "Magnitude", sd, column = ~mag, step = 0.1)
filter_select("class", "Class", sd, group = ~depth)
leaflet(sd) %>% addTiles() %>% addMarkers()
```

Interaction between leaflet and datatable

I would like to use filter in my datatable and that it filters the markers in the map (shades the other or something like that).
Currently, it is possible to make a selection in the map that filters in the datatable, but not the inverse?
Please see my reproducible example.
I would like for example to search for "a" in the table and that only corresponding markers appear in the map.

Make a selection box appearing only on double click

While running a shiny application, the 'make a selection' box does not appear until you double click on the icon. Thereafter if you resize it, it disappears again. I have tried this in both Firefox and Chrome.

Here's the code I am using:

library(crosstalk)
library(leaflet)
library(DT)
library(shiny)

ui <- fluidPage(
  fluidRow(
    column(6, leafletOutput('cmap',height=600, width='100%')),
    column(6, dataTableOutput('cmapTable'))
  )
)

server <- function(input, output, session) {
  sDraft <- SharedData$new(df)

  output$cmap <- renderLeaflet({
    leaflet(sDraft) %>% addTiles() %>%
      addMarkers(lng = ~lon, lat=~lat)
    })

  output$cmapTable <- renderDataTable({
    datatable(sDraft, options = list(scrollX = TRUE),filter='top')
  },server = FALSE)
}

shinyApp(ui, server)

Here's the df data:

structure(list(mmsi = c(538004533L, 538004534L, 538004536L, 538005117L, 
538005118L, 538005120L), Name = c("STI GARNET", "STI TOPAZ", 
"STI ONYX", "STI LE ROCHER", "STI FONTVIEILLE", "STI VILLE"), 
    Dwt = c(51840L, 51840L, 49990L, 49990L, 49990L, 49990L), 
    lat = c(25.0050144, 11.213192, 1.26014328, 13.41435, 32.3204, 
    5.7550416), lon = c(55.05124, 121.773193, 103.914574, -18.28864, 
    -117.113571, 81.47743)), row.names = c(NA, -6L), class = c("grouped_df", 
"tbl_df", "tbl", "data.frame"), vars = "mmsi", drop = TRUE, indices = list(
    0L, 1L, 2L, 3L, 4L, 5L), group_sizes = c(1L, 1L, 1L, 1L, 
1L, 1L), biggest_group_size = 1L, labels = structure(list(mmsi = c(538004533L, 
538004534L, 538004536L, 538005117L, 538005118L, 538005120L)), row.names = c(NA, 
-6L), class = "data.frame", vars = "mmsi", drop = TRUE))

I am on a MacBook Pro (macOS High Sierra Version 10.13.6)

Error: Unknown column 'g' (related to plotly?)

Thanks for this awesome package. I noticed a possible issue. I have a markdown file using Crosstalk that knits fine and the control properly filters a datatable.

Then if I add plotly I get the following: "Error: Unknown column 'g' Execution halted". This occurs for filter_select and filter_checkbox. I haven't tried filter_slider.

I think I installed the alternate ploty library as specified in the Readme, but I could very well be doing something wrong on my end. I'd be happy to try to create a minimal example if you would find that helpful.

Linked-brushing not working on Polar map projections

There an issue with the map brushing tool on a polar map projection. The brushing box is distorted and does not select map markers. Is there a way to change brushing projection or correct this?

Below is a reproducible example of the issue

library(leaflet)
library(DT)
library(crosstalk)

extent <- 11000000 + 9036842.762 + 667
origin = c(-extent, extent)
maxResolution <- ((extent - -extent) / 256)
defZoom <- 3
bounds <- list(c(-extent, extent),c(extent, -extent))
minZoom <- 0
maxZoom <- 18
resolutions <- purrr::map_dbl(minZoom:maxZoom,function(x) maxResolution/(2^x))

# 6 Projection EPSG Codes
projections <- c('3571', '3572', '3573', '3574', '3575', '3576')
# Corresponding proj4defs codes for each projection
proj4defs <- list(
  '3571' = '+proj=laea +lat_0=90 +lon_0=180 +x_0=0 +y_0=0 +datum=WGS84 +units=m +no_defs',
  '3572' = '+proj=laea +lat_0=90 +lon_0=-150 +x_0=0 +y_0=0 +datum=WGS84 +units=m +no_defs',
  '3573' = '+proj=laea +lat_0=90 +lon_0=-100 +x_0=0 +y_0=0 +datum=WGS84 +units=m +no_defs',
  '3574' = '+proj=laea +lat_0=90 +lon_0=-40 +x_0=0 +y_0=0 +datum=WGS84 +units=m +no_defs',
  '3575' = '+proj=laea +lat_0=90 +lon_0=10 +x_0=0 +y_0=0 +datum=WGS84 +units=m +no_defs',
  '3576' = '+proj=laea +lat_0=90 +lon_0=90 +x_0=0 +y_0=0 +datum=WGS84 +units=m +no_defs'
)

# create a CRS instance for each projection
crses <- purrr::map(projections, function(code) {
  leafletCRS(
    crsClass = 'L.Proj.CRS',
    code = sprintf("EPSG:%s",code),
    proj4def = proj4defs[[code]],
    origin = origin,
    resolutions = resolutions,
    bounds = bounds
  )
})

# Tile URL Template for each projection
tileURLtemplates <- purrr::map(projections, function(code) {
  sprintf('http://{s}.tiles.arcticconnect.org/osm_%s/{z}/{x}/{y}.png',
          code)
})


# Create data points for map
dat <- data.frame(latitude = c(80, 80), longitude = c(-143, 143))

# Wrap data frame in SharedData
sd <- SharedData$new(dat)


# We can't add all 6 tiles to our leaflet map,
# because each one is in a different projection,
# and you can have only one projection per map in Leaflet.
# So we create 6 maps.
polarmaps <- purrr::map2(crses, tileURLtemplates,
                         function(crs, tileURLTemplate) {
                           leaflet(sd, options= leafletOptions(
                             crs=crs, minZoom = minZoom, maxZoom = maxZoom)) %>%
                             setView(0, 90, defZoom) %>%
                             addTiles(urlTemplate = tileURLTemplate,
                                      attribution = "Map © ArcticConnect. Data © OpenStreetMap contributors",
                                      options = tileOptions(subdomains = "abc", noWrap = TRUE,
                                                            continuousWorld = FALSE))
                         })


# Use SharedData like a dataframe with Crosstalk-enabled widgets
bscols(
polarmaps[[1]] %>%
  addGraticule() %>%
  addCircleMarkers(data = sd, lat = ~latitude, lng = ~longitude),
datatable(sd, extensions="Scroller", style="bootstrap", class="compact", width="100%",
          options=list(deferRender=TRUE, scrollY=300, scroller=TRUE))
)

provide index/list of groups

There seems to be no way to explore available groups in crosstalk. Is this by design, and if so what are the reasons? Ideally, it seems like there should be a public method to provide a list of groups available, so that we can subscribe in JavaScript. Otherwise, we only know groups through the SharedData instance in R.

Plotly × Leaflet: using plotly modebar removes leaflet selection

When linking plotly and leaflet htmlwidgets, it appears that any use of the plotly modebar causes the leaflet selection to reset.

Tools like zoom and pan are quite useful for exploring dense visualizations, so the incompatibility of these tools with the crosstalk linking seems like a missed opportunity.

Regex:

library(crosstalk)
library(d3scatter)
library(leaflet)
library(sf)
library(plotly)
library(tidyverse)

demo(nc, ask = FALSE, echo = FALSE, verbose = FALSE)

shared_nc_pt <- nc %>% st_transform(4326) %>% st_centroid() %>% SharedData$new()

bscols(leaflet(data = shared_nc_pt) %>%
         addTiles() %>%
         addMarkers(),
       plot_ly(shared_nc_pt, x = ~BIR74, y=  ~SID79, type = 'scatter', mode = 'markers')       
)

Error in fiji earthquake example

From the introduction
fijierrora

fijierror
`

sessionInfo()
R version 3.3.1 (2016-06-21)
Platform: x86_64-w64-mingw32/x64 (64-bit)
Running under: Windows >= 8 x64 (build 9200)

locale:
[1] LC_COLLATE=English_Canada.1252 LC_CTYPE=English_Canada.1252 LC_MONETARY=English_Canada.1252
[4] LC_NUMERIC=C LC_TIME=English_Canada.1252

attached base packages:
[1] stats graphics grDevices utils datasets methods base

other attached packages:
[1] leaflet_1.0.2.9010 devtools_1.12.0 DT_0.2 crosstalk_1.0.0

loaded via a namespace (and not attached):
[1] Rcpp_0.12.8 knitr_1.15.1 magrittr_1.5 xtable_1.8-2 R6_2.2.0 httr_1.2.1
[7] stringr_1.1.0 tools_3.3.1 git2r_0.16.0 withr_1.0.2 htmltools_0.3.5 yaml_2.1.14
[13] lazyeval_0.2.0.9000 rprojroot_1.1 digest_0.6.10 shiny_0.14.2 htmlwidgets_0.8 curl_2.3
[19] memoise_1.0.0 evaluate_0.10 mime_0.5 rmarkdown_1.3 stringi_1.1.2 backports_1.0.4
[25] jsonlite_1.1 httpuv_1.3.3

`

filter_slider will not work with dates if a datatable is present

Minimal reproducible example here (flexdashboard): https://github.com/papapalpatine/Bugs/blob/master/FilterSlider_DataTable_Bug.Rmd

Basically, I can either have a data table, or have a filter slider that can use dates, but if I try to have both, none of them show up in the resulting output file.

See lines 15-16 of example in link to switch up which options apply:
generate_dataTable <- TRUE; filter_slider_with_date <- FALSE; #Either of these will work BUT NOT BOTH

filter_slider can work with numbers without a problem with respect to including a datatable. But ask it to work with dates or times and you get a blank file if you also try to include a datatable. The issue occurs regardless of whether the data table is applied to a SharedData object or not.

Anyone know what's going on here?

Provide a way to set a default selection

I'm currently doing this via the defaultValues argument in plotly's highlight() function, and have found it useful for sharing interesting selection states, but this should really be a property of the data, not the plot.

any priority for conversion of htmlwidgets

Likely premature, but is there any order of priority for conversion of htmlwidgets to crosstalk. It seems leaflet, plotly, and parcoords offer some level of support. DT had support, but I think is now obsolete given the new API. I thought dygraphs would be very interesting.

Bug in example code on main page

A straight cut/paste of the code on Crosstalk's homepage fails to generate the htmlwidget.

library(crosstalk)
library(leaflet)
library(DT)

# Wrap data frame in SharedData
sd <- SharedData$new(quakes[sample(nrow(quakes), 100),])

# Create a filter input
filter_slider("mag", "Magnitude", sd, column=~mag, step=0.1, width=250)

# Use SharedData like a dataframe with Crosstalk-enabled widgets
bscols(
  leaflet(sd) %>% addTiles() %>% addMarkers(),
  datatable(sd, extensions="Scroller", style="bootstrap", class="compact", width="100%",
    options=list(deferRender=TRUE, scrollY=300, scroller=TRUE))
)
Assuming 'long' and 'lat' are longitude and latitude, respectively
Classes 'SharedData', 'R6' <SharedData>
  Public:
    .updateSelection: function (value)
    clearSelection: function (ownerId = "")
    clone: function (deep = FALSE)
    data: function (withSelection = FALSE, withFilter = TRUE, withKey = FALSE)
    groupName: function ()
    initialize: function (data, key = NULL, group = createUniqueId(4, prefix = "SharedData"))
    key: function ()
    origData: function ()
    selection: function (value, ownerId = "")
  Private:
    .data: data.frame
    .filterCV: ClientValue, R6
    .group: SharedDatae843a7bf
    .key: NULL
    .rv: reactivevalues
    .selectionCV: ClientValue, R6
Error in datatable(sd, extensions = "Scroller", style = "bootstrap", class = "compact",  :
  'data' must be 2-dimensional (e.g. data frame or matrix)
> sessionInfo()
R version 3.4.0 (2017-04-21)
Platform: x86_64-apple-darwin15.6.0 (64-bit)
Running under: macOS Sierra 10.12.6

Matrix products: default
BLAS: /Library/Frameworks/R.framework/Versions/3.4/Resources/lib/libRblas.0.dylib
LAPACK: /Library/Frameworks/R.framework/Versions/3.4/Resources/lib/libRlapack.dylib

locale:
[1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base

other attached packages:
[1] DT_0.2          leaflet_1.1.0   crosstalk_1.0.1

loaded via a namespace (and not attached):
 [1] htmlwidgets_0.9     shiny_1.0.3         compiler_3.4.0
 [4] lazyeval_0.2.0.9000 magrittr_1.5        R6_2.2.2
 [7] htmltools_0.3.6     Rcpp_0.12.12        jsonlite_1.5
[10] digest_0.6.12       xtable_1.8-2        httpuv_1.3.5
[13] mime_0.5
>

Feature Request for `filter_select` and `filter_checkbox`

For filter_select, the ability to type in a name, filter, and select by hitting return. At the moment, one can only scroll and click to select. Any keyboard interactions just disables any option to select anything.

For filter_checkbox, the option to set up a grid of checkboxes. Currently, it spits out a list, one item after the other. Were one to, say, select from a set of US states, one would get a 50 rows of checkboxes.

Gaps between bars and x axis not updated when filter_select is applied

I experienced strange behavior of a bar plot when using crosstalk::filter_select. It does not update the x axis according to the filter, and shows gaps between the bars.

Here is a reproducible example:

library(crosstalk)
library(dplyr)
library(plotly)

# setting the seed
set.seed(123)

# create data
df <- data.frame(
  a = sample(letters, 40, replace = T),
  b = sample(letters, 40, replace = T),
  val = sample(20:100, 40, replace = F)
) %>%
  mutate(chr = paste0(a, b)) %>% 
  select(-c(a, b))

# make R6 class that represents a shared data frame
df <- SharedData$new(df)

# make the interactive plot 
crosstalk::bscols(
  list(crosstalk::filter_select(
    id = "chr",
    label = "Character",
    sharedData = df,
    group = ~ chr
  )),
  plot_ly(df, x = ~ chr, y = ~ val)
)

When the interactive plot is created, if we choose the following in the dropdown menu: "am", "gc", "rs", the filter creates the following barplot:

image

There are gaps between the bars and the x axis is not updated when the filter is applied.

Is it possible to use crosstalk to fix this issue.

Many thanks in advance

Bug with filter_slider and dates

I encountered an obscure error with filter_slider applied to an object of class Date.

Here is a minimal example to reproduce it:
https://gist.github.com/bhogan-mitre/81d1eaffc56deb342e8dbff7f2d3194f

If the filter_slider of Date comes second in the list of inputs, it throws an error:

Uncaught TypeError: Cannot read property 'utc' of undefined

If the inputs are reversed so the filter_slider is first, it renders fine.

filter_slider("date", "Date", shared_mtcars, ~date, width = "100%")
filter_checkbox("cyl", "Cylinders", shared_mtcars, ~cyl, inline = TRUE)

Note that I am using this with flexdashboard, so that may be a contributing factor as well.

Interaction between Polygons in Leaflet and Datatable

My goal is to be able to click on a polygon and the datatable renders automatically and only shows the corresponding values? Similar to what is done here with plotly.

https://blog.cpsievert.me/2018/03/30/visualizing-geo-spatial-data-with-sf-and-plotly/

Is this possible? What I tried so far is the following:

sd <- some_spatial_dataframe %>% SharedData$new()

sd_df <- some_spatial_dataframe %>% data.frame() %>%
SharedData$new(group = sd$groupName())

crosstalk::bscols(
leaflet(sd) %>%
addProviderTiles("CartoDB.Positron") %>%
addPolygons(),
DT::datatable(data = sd_df)
)

Unfortunately, this doesn't work at all. There is no interaction between leaflet and datatable.

crosstalk messes up ioslides_presentation

If I put a crosstalk control in an ioslides_presentation, the styles get all messed up. For example:

---
output: ioslides_presentation
---


## CSS messed up

```{r eval = TRUE}
library(crosstalk)
sd <- SharedData$new(mtcars)
filter_checkbox("cylinderselector", 
                        "Cylinders", sd, ~ cyl, inline = TRUE)

has the style messed up on the second slide: fonts too small, background white instead of black, etc. If eval = FALSE is used instead, it looks fine (but doesn't include the control).

The cause of this is that ioslides_presentattion sets a style on pre, for example, and so does some other CSS that crosstalk pulls in. If I include my own CSS code and prefix the selectors to make them more specific, I can fix most of the problems, e.g. instead of

pre {
  font-family: 'Source Code Pro', 'Courier New', monospace;
  ...

I can use

slide.current pre {
  font-family: 'Source Code Pro', 'Courier New', monospace;
  ...

but I'd like to know if there's some way to turn off (some of?) the crosstalk includes, or to automatically give precedence to the ioslides_presentation includes.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.