Closed
6 changes: 4 additions & 2 deletions .travis.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -28,8 +28,10 @@ before_script:
- git clone https://github.com/cpsievert/plotly-test-table.git ../plotly-test-table
- "wget -q -O - https://github.com/yihui/crandalf/raw/master/inst/scripts/install-pandoc | bash"
- Rscript -e 'if (length(find.package("devtools", quiet = TRUE)) == 0L) { install.packages("devtools", repos = "http://cran.rstudio.com") }'
- Rscript -e 'library(devtools);update_packages("devtools", repos = "http://cran.rstudio.com")'
- Rscript -e 'library(devtools);install_deps(repos = "http://cran.rstudio.com", dependencies = TRUE)'
- Rscript -e 'devtools::update_packages("devtools", repos = "http://cran.rstudio.com")'
- Rscript -e 'devtools::install_deps(repos = "http://cran.rstudio.com", dependencies = TRUE)'
- Rscript -e 'devtools::install_github("rstudio/crosstalk")'
- Rscript -e 'devtools::install_github("cpsievert/htmlwidgets")'

script:
# run R CMD check on the non-pull request build
Expand Down
1 change: 1 addition & 0 deletions DESCRIPTION
Original file line numberDiff line numberDiff line change
Expand Up@@ -30,6 +30,7 @@ Imports:
viridis,
base64enc,
htmlwidgets,
crosstalk,
plyr
Suggests:
dplyr,
Expand Down
15 changes: 10 additions & 5 deletions R/plotly.R
Original file line numberDiff line numberDiff line change
Expand Up@@ -19,6 +19,8 @@
#' @param symbols A character vector of symbol types. Possible values:
#' 'dot', 'cross', 'diamond', 'square', 'triangle-down', 'triangle-left', 'triangle-right', 'triangle-up'
#' @param size A variable name or numeric vector to encode the size of markers.
#' @param key a selection variable for linked views
#' @param set share selections across this grouping variable.
#' @param width Width in pixels (optional, defaults to automatic sizing).
#' @param height Height in pixels (optional, defaults to automatic sizing).
#' @param inherit logical. Should future traces inherit properties from this initial trace?
Expand DownExpand Up@@ -64,9 +66,9 @@
#' }
#'
plot_ly <- function(data = data.frame(), ..., type = "scatter",
group, color, colors, symbol, symbols, size,
width = NULL, height = NULL, inherit = FALSE,
evaluate = FALSE) {
group, color, colors, symbol, symbols, size,
key, set = "A", width = NULL, height = NULL,
inherit = FALSE, evaluate = FALSE) {
# "native" plotly arguments
argz <- substitute(list(...))
# old arguments to this function that are no longer supported
Expand All@@ -83,16 +85,19 @@ plot_ly <- function(data = data.frame(), ..., type = "scatter",
if (!missing(symbol)) argz$symbol <- substitute(symbol)
if (!missing(symbols)) argz$symbols <- substitute(symbols)
if (!missing(size)) argz$size <- substitute(size)
if (!missing(key)) argz$key <- substitute(key)
# trace information
tr <- list(
type = type,
args = argz,
env = list2env(data), # environment in which to evaluate arguments
enclos = parent.frame(), # if objects aren't found in env, look here
inherit = inherit
inherit = inherit,
set = set
)
# plotly objects should always have a _list_ of trace(s)

p <- list(
# plotly objects should always have a _list_ of trace(s)
data = list(tr),
layout = NULL,
url = NULL,
Expand Down
1 change: 1 addition & 0 deletions R/print.R
Original file line numberDiff line numberDiff line change
Expand Up@@ -49,6 +49,7 @@ as.widget <- function(x, ...) {
padding = 5,
browser.fill = TRUE
),
dependencies = crosstalk::dependencies,
...
)
}
Expand Down
14 changes: 12 additions & 2 deletions R/shiny.R
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,6 +7,8 @@
#' @param width,height Must be a valid CSS unit (like \code{"100\%"},
#' \code{"400px"}, \code{"auto"}) or a number, which will be coerced to a
#' string and have \code{"px"} appended.
#' @param inline use an inline (\code{span()}) or block container
#' (\code{div()}) for the output
#' @param expr An expression that generates a plotly
#' @param env The environment in which to evaluate \code{expr}.
#' @param quoted Is \code{expr} a quoted expression (with \code{quote()})? This
Expand All@@ -17,8 +19,16 @@
#' @name plotly-shiny
#'
#' @export
plotlyOutput <- function(outputId, width = "100%", height = "400px") {
htmlwidgets::shinyWidgetOutput(outputId, "plotly", width, height, package = "plotly")
plotlyOutput <- function(outputId, width = "100%", height = "400px",
inline = FALSE) {
htmlwidgets::shinyWidgetOutput(
outputId = outputId,
name = "plotly",
width = width,
height = height,
inline = inline,
package = "plotly"
)
}

#' @rdname plotly-shiny
Expand Down
34 changes: 34 additions & 0 deletions inst/examples/crosstalk.R
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
# access renderPlotly() selections on a shiny server
# https://github.com/cpsievert/shiny_apps/blob/master/plotlyCrosstalk/app.R

# TODO:
# (1) pass selections from another htmlwidget (rcdimple?) to renderPlotly()
# (2) crosstalk without shiny (see examples below)
# (3) define custom event behavior in JS from R?

library(plotly)
library(htmltools)

# click/show selects?
mtcars$gear <- factor(mtcars$gear)
mtcars$cyl <- factor(mtcars$cyl)
p1 <- plot_ly(mtcars, x = wt, y = mpg, color = gear, mode = "markers",
key = gear, set = "A", width = 400)
p2 <- plot_ly(mtcars, x = wt, y = disp, color = gear, mode = "markers",
key = gear, set = "A", width = 400)
# TODO: inline-block?
browsable(tagList(
as.widget(p1),
as.widget(p2)
))

library(dplyr)
m <- count(mtcars, cyl)
p1 <- plot_ly(m, x = cyl, y = n, type = "bar",
key = cyl, set = "A", width = 400)
p2 <- plot_ly(mtcars, x = mpg, y = disp, mode = "markers",
key = cyl, set = "A", width = 400)
browsable(tagList(
as.widget(p1),
as.widget(p2)
))
52 changes: 49 additions & 3 deletions inst/htmlwidgets/plotly.js
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@

HTMLWidgets.widget({
name: "plotly",
type: "output",

initialize: function(el, width, height){
initialize: function(el, width, height){
return {};
},

resize: function(el, width, height, instance) {
// TODO: impose fixed coordinates, if specified (see #342)
Plotly.relayout(el.id, {width: width, height: height});
},

Expand All@@ -15,13 +17,57 @@ HTMLWidgets.widget({
window.PLOTLYENV = window.PLOTLYENV || {};
window.PLOTLYENV.BASE_URL = x.base_url;

var graphDiv = document.getElementById(el.id);

// if no plot exists yet, create one with a particular configuration
if (!instance.plotly) {
Plotly.plot(el.id, x.data, x.layout, x.config);
Plotly.plot(graphDiv, x.data, x.layout, x.config);
instance.plotly = true;
} else {
Plotly.newPlot(el.id, x.data, x.layout);
Plotly.newPlot(graphDiv, x.data, x.layout);
}

var g = x.data[0].set;
var grp = crosstalk.group(g);

graphDiv.on('plotly_click', function(eventData) {
// extract only the data we may want to access in R
var d = eventData.points.map(function(pt) {
var obj = {
curveNumber: pt.curveNumber,
pointNumber: pt.pointNumber,
x: pt.x,
y: pt.y
};
if (pt.data.hasOwnProperty("key")) {
if (typeof pt.pointNumber === "number") {
obj.key = pt.data.key[pt.pointNumber];
} else {
obj.key = pt.data.key[pt.pointNumber[0]][pt.pointNumber[1]];
} // TODO: can pointNumber be 3D?
}
return obj;
});

// tell crosstalk about click data so we can access it in R (and JS)
grp.var("plotly_click").set(d);
});

graphDiv.on('plotly_selected', function(eventData) {
if (eventData !== undefined) {
// convert the array of objects to object of arrays so this converts
// to data frame in R as opposed to a vector
var pts = eventData.points;
var obj = {
curveNumber: pts.map(function(pt) {return pt.curveNumber; }),
pointNumber: pts.map(function(pt) {return pt.pointNumber; }),
x: pts.map(function(pt) {return pt.x; }),
y: pts.map(function(pt) {return pt.y; })
};
grp.var("plotly_selected").set(obj);
}
});

}

});
5 changes: 4 additions & 1 deletion man/plotly-shiny.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content
Closed
6 changes: 4 additions & 2 deletions .travis.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -28,8 +28,10 @@ before_script:
- git clone https://github.com/cpsievert/plotly-test-table.git ../plotly-test-table
- "wget -q -O - https://github.com/yihui/crandalf/raw/master/inst/scripts/install-pandoc | bash"
- Rscript -e 'if (length(find.package("devtools", quiet = TRUE)) == 0L) { install.packages("devtools", repos = "http://cran.rstudio.com") }'
- Rscript -e 'library(devtools);update_packages("devtools", repos = "http://cran.rstudio.com")'
- Rscript -e 'library(devtools);install_deps(repos = "http://cran.rstudio.com", dependencies = TRUE)'
- Rscript -e 'devtools::update_packages("devtools", repos = "http://cran.rstudio.com")'
- Rscript -e 'devtools::install_deps(repos = "http://cran.rstudio.com", dependencies = TRUE)'
- Rscript -e 'devtools::install_github("rstudio/crosstalk")'
- Rscript -e 'devtools::install_github("cpsievert/htmlwidgets")'

script:
# run R CMD check on the non-pull request build
Expand Down
1 change: 1 addition & 0 deletions DESCRIPTION
Original file line numberDiff line numberDiff line change
Expand Up@@ -30,6 +30,7 @@ Imports:
viridis,
base64enc,
htmlwidgets,
crosstalk,
plyr
Suggests:
dplyr,
Expand Down
15 changes: 10 additions & 5 deletions R/plotly.R
Original file line numberDiff line numberDiff line change
Expand Up@@ -19,6 +19,8 @@
#' @param symbols A character vector of symbol types. Possible values:
#' 'dot', 'cross', 'diamond', 'square', 'triangle-down', 'triangle-left', 'triangle-right', 'triangle-up'
#' @param size A variable name or numeric vector to encode the size of markers.
#' @param key a selection variable for linked views
#' @param set share selections across this grouping variable.
#' @param width Width in pixels (optional, defaults to automatic sizing).
#' @param height Height in pixels (optional, defaults to automatic sizing).
#' @param inherit logical. Should future traces inherit properties from this initial trace?
Expand DownExpand Up@@ -64,9 +66,9 @@
#' }
#'
plot_ly <- function(data = data.frame(), ..., type = "scatter",
group, color, colors, symbol, symbols, size,
width = NULL, height = NULL, inherit = FALSE,
evaluate = FALSE) {
group, color, colors, symbol, symbols, size,
key, set = "A", width = NULL, height = NULL,
inherit = FALSE, evaluate = FALSE) {
# "native" plotly arguments
argz <- substitute(list(...))
# old arguments to this function that are no longer supported
Expand All@@ -83,16 +85,19 @@ plot_ly <- function(data = data.frame(), ..., type = "scatter",
if (!missing(symbol)) argz$symbol <- substitute(symbol)
if (!missing(symbols)) argz$symbols <- substitute(symbols)
if (!missing(size)) argz$size <- substitute(size)
if (!missing(key)) argz$key <- substitute(key)
# trace information
tr <- list(
type = type,
args = argz,
env = list2env(data), # environment in which to evaluate arguments
enclos = parent.frame(), # if objects aren't found in env, look here
inherit = inherit
inherit = inherit,
set = set
)
# plotly objects should always have a _list_ of trace(s)

p <- list(
# plotly objects should always have a _list_ of trace(s)
data = list(tr),
layout = NULL,
url = NULL,
Expand Down
1 change: 1 addition & 0 deletions R/print.R
Original file line numberDiff line numberDiff line change
Expand Up@@ -49,6 +49,7 @@ as.widget <- function(x, ...) {
padding = 5,
browser.fill = TRUE
),
dependencies = crosstalk::dependencies,
...
)
}
Expand Down
14 changes: 12 additions & 2 deletions R/shiny.R
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,6 +7,8 @@
#' @param width,height Must be a valid CSS unit (like \code{"100\%"},
#' \code{"400px"}, \code{"auto"}) or a number, which will be coerced to a
#' string and have \code{"px"} appended.
#' @param inline use an inline (\code{span()}) or block container
#' (\code{div()}) for the output
#' @param expr An expression that generates a plotly
#' @param env The environment in which to evaluate \code{expr}.
#' @param quoted Is \code{expr} a quoted expression (with \code{quote()})? This
Expand All@@ -17,8 +19,16 @@
#' @name plotly-shiny
#'
#' @export
plotlyOutput <- function(outputId, width = "100%", height = "400px") {
htmlwidgets::shinyWidgetOutput(outputId, "plotly", width, height, package = "plotly")
plotlyOutput <- function(outputId, width = "100%", height = "400px",
inline = FALSE) {
htmlwidgets::shinyWidgetOutput(
outputId = outputId,
name = "plotly",
width = width,
height = height,
inline = inline,
package = "plotly"
)
}

#' @rdname plotly-shiny
Expand Down
34 changes: 34 additions & 0 deletions inst/examples/crosstalk.R
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
# access renderPlotly() selections on a shiny server
# https://github.com/cpsievert/shiny_apps/blob/master/plotlyCrosstalk/app.R

# TODO:
# (1) pass selections from another htmlwidget (rcdimple?) to renderPlotly()
# (2) crosstalk without shiny (see examples below)
# (3) define custom event behavior in JS from R?

library(plotly)
library(htmltools)

# click/show selects?
mtcars$gear <- factor(mtcars$gear)
mtcars$cyl <- factor(mtcars$cyl)
p1 <- plot_ly(mtcars, x = wt, y = mpg, color = gear, mode = "markers",
key = gear, set = "A", width = 400)
p2 <- plot_ly(mtcars, x = wt, y = disp, color = gear, mode = "markers",
key = gear, set = "A", width = 400)
# TODO: inline-block?
browsable(tagList(
as.widget(p1),
as.widget(p2)
))

library(dplyr)
m <- count(mtcars, cyl)
p1 <- plot_ly(m, x = cyl, y = n, type = "bar",
key = cyl, set = "A", width = 400)
p2 <- plot_ly(mtcars, x = mpg, y = disp, mode = "markers",
key = cyl, set = "A", width = 400)
browsable(tagList(
as.widget(p1),
as.widget(p2)
))
52 changes: 49 additions & 3 deletions inst/htmlwidgets/plotly.js
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@

HTMLWidgets.widget({
name: "plotly",
type: "output",

initialize: function(el, width, height){
initialize: function(el, width, height){
return {};
},

resize: function(el, width, height, instance) {
// TODO: impose fixed coordinates, if specified (see #342)
Plotly.relayout(el.id, {width: width, height: height});
},

Expand All@@ -15,13 +17,57 @@ HTMLWidgets.widget({
window.PLOTLYENV = window.PLOTLYENV || {};
window.PLOTLYENV.BASE_URL = x.base_url;

var graphDiv = document.getElementById(el.id);

// if no plot exists yet, create one with a particular configuration
if (!instance.plotly) {
Plotly.plot(el.id, x.data, x.layout, x.config);
Plotly.plot(graphDiv, x.data, x.layout, x.config);
instance.plotly = true;
} else {
Plotly.newPlot(el.id, x.data, x.layout);
Plotly.newPlot(graphDiv, x.data, x.layout);
}

var g = x.data[0].set;
var grp = crosstalk.group(g);

graphDiv.on('plotly_click', function(eventData) {
// extract only the data we may want to access in R
var d = eventData.points.map(function(pt) {
var obj = {
curveNumber: pt.curveNumber,
pointNumber: pt.pointNumber,
x: pt.x,
y: pt.y
};
if (pt.data.hasOwnProperty("key")) {
if (typeof pt.pointNumber === "number") {
obj.key = pt.data.key[pt.pointNumber];
} else {
obj.key = pt.data.key[pt.pointNumber[0]][pt.pointNumber[1]];
} // TODO: can pointNumber be 3D?
}
return obj;
});

// tell crosstalk about click data so we can access it in R (and JS)
grp.var("plotly_click").set(d);
});

graphDiv.on('plotly_selected', function(eventData) {
if (eventData !== undefined) {
// convert the array of objects to object of arrays so this converts
// to data frame in R as opposed to a vector
var pts = eventData.points;
var obj = {
curveNumber: pts.map(function(pt) {return pt.curveNumber; }),
pointNumber: pts.map(function(pt) {return pt.pointNumber; }),
x: pts.map(function(pt) {return pt.x; }),
y: pts.map(function(pt) {return pt.y; })
};
grp.var("plotly_selected").set(obj);
}
});

}

});
5 changes: 4 additions & 1 deletion man/plotly-shiny.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Closed
6 changes: 4 additions & 2 deletions .travis.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -28,8 +28,10 @@ before_script:
- git clone https://github.com/cpsievert/plotly-test-table.git ../plotly-test-table
- "wget -q -O - https://github.com/yihui/crandalf/raw/master/inst/scripts/install-pandoc | bash"
- Rscript -e 'if (length(find.package("devtools", quiet = TRUE)) == 0L) { install.packages("devtools", repos = "http://cran.rstudio.com") }'
- Rscript -e 'library(devtools);update_packages("devtools", repos = "http://cran.rstudio.com")'
- Rscript -e 'library(devtools);install_deps(repos = "http://cran.rstudio.com", dependencies = TRUE)'
- Rscript -e 'devtools::update_packages("devtools", repos = "http://cran.rstudio.com")'
- Rscript -e 'devtools::install_deps(repos = "http://cran.rstudio.com", dependencies = TRUE)'
- Rscript -e 'devtools::install_github("rstudio/crosstalk")'
- Rscript -e 'devtools::install_github("cpsievert/htmlwidgets")'

script:
# run R CMD check on the non-pull request build
Expand Down
1 change: 1 addition & 0 deletions DESCRIPTION
Original file line numberDiff line numberDiff line change
Expand Up@@ -30,6 +30,7 @@ Imports:
viridis,
base64enc,
htmlwidgets,
crosstalk,
plyr
Suggests:
dplyr,
Expand Down
15 changes: 10 additions & 5 deletions R/plotly.R
Original file line numberDiff line numberDiff line change
Expand Up@@ -19,6 +19,8 @@
#' @param symbols A character vector of symbol types. Possible values:
#' 'dot', 'cross', 'diamond', 'square', 'triangle-down', 'triangle-left', 'triangle-right', 'triangle-up'
#' @param size A variable name or numeric vector to encode the size of markers.
#' @param key a selection variable for linked views
#' @param set share selections across this grouping variable.
#' @param width Width in pixels (optional, defaults to automatic sizing).
#' @param height Height in pixels (optional, defaults to automatic sizing).
#' @param inherit logical. Should future traces inherit properties from this initial trace?
Expand DownExpand Up@@ -64,9 +66,9 @@
#' }
#'
plot_ly <- function(data = data.frame(), ..., type = "scatter",
group, color, colors, symbol, symbols, size,
width = NULL, height = NULL, inherit = FALSE,
evaluate = FALSE) {
group, color, colors, symbol, symbols, size,
key, set = "A", width = NULL, height = NULL,
inherit = FALSE, evaluate = FALSE) {
# "native" plotly arguments
argz <- substitute(list(...))
# old arguments to this function that are no longer supported
Expand All@@ -83,16 +85,19 @@ plot_ly <- function(data = data.frame(), ..., type = "scatter",
if (!missing(symbol)) argz$symbol <- substitute(symbol)
if (!missing(symbols)) argz$symbols <- substitute(symbols)
if (!missing(size)) argz$size <- substitute(size)
if (!missing(key)) argz$key <- substitute(key)
# trace information
tr <- list(
type = type,
args = argz,
env = list2env(data), # environment in which to evaluate arguments
enclos = parent.frame(), # if objects aren't found in env, look here
inherit = inherit
inherit = inherit,
set = set
)
# plotly objects should always have a _list_ of trace(s)

p <- list(
# plotly objects should always have a _list_ of trace(s)
data = list(tr),
layout = NULL,
url = NULL,
Expand Down
1 change: 1 addition & 0 deletions R/print.R
Original file line numberDiff line numberDiff line change
Expand Up@@ -49,6 +49,7 @@ as.widget <- function(x, ...) {
padding = 5,
browser.fill = TRUE
),
dependencies = crosstalk::dependencies,
...
)
}
Expand Down
14 changes: 12 additions & 2 deletions R/shiny.R
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,6 +7,8 @@
#' @param width,height Must be a valid CSS unit (like \code{"100\%"},
#' \code{"400px"}, \code{"auto"}) or a number, which will be coerced to a
#' string and have \code{"px"} appended.
#' @param inline use an inline (\code{span()}) or block container
#' (\code{div()}) for the output
#' @param expr An expression that generates a plotly
#' @param env The environment in which to evaluate \code{expr}.
#' @param quoted Is \code{expr} a quoted expression (with \code{quote()})? This
Expand All@@ -17,8 +19,16 @@
#' @name plotly-shiny
#'
#' @export
plotlyOutput <- function(outputId, width = "100%", height = "400px") {
htmlwidgets::shinyWidgetOutput(outputId, "plotly", width, height, package = "plotly")
plotlyOutput <- function(outputId, width = "100%", height = "400px",
inline = FALSE) {
htmlwidgets::shinyWidgetOutput(
outputId = outputId,
name = "plotly",
width = width,
height = height,
inline = inline,
package = "plotly"
)
}

#' @rdname plotly-shiny
Expand Down
34 changes: 34 additions & 0 deletions inst/examples/crosstalk.R
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
# access renderPlotly() selections on a shiny server
# https://github.com/cpsievert/shiny_apps/blob/master/plotlyCrosstalk/app.R

# TODO:
# (1) pass selections from another htmlwidget (rcdimple?) to renderPlotly()
# (2) crosstalk without shiny (see examples below)
# (3) define custom event behavior in JS from R?

library(plotly)
library(htmltools)

# click/show selects?
mtcars$gear <- factor(mtcars$gear)
mtcars$cyl <- factor(mtcars$cyl)
p1 <- plot_ly(mtcars, x = wt, y = mpg, color = gear, mode = "markers",
key = gear, set = "A", width = 400)
p2 <- plot_ly(mtcars, x = wt, y = disp, color = gear, mode = "markers",
key = gear, set = "A", width = 400)
# TODO: inline-block?
browsable(tagList(
as.widget(p1),
as.widget(p2)
))

library(dplyr)
m <- count(mtcars, cyl)
p1 <- plot_ly(m, x = cyl, y = n, type = "bar",
key = cyl, set = "A", width = 400)
p2 <- plot_ly(mtcars, x = mpg, y = disp, mode = "markers",
key = cyl, set = "A", width = 400)
browsable(tagList(
as.widget(p1),
as.widget(p2)
))
52 changes: 49 additions & 3 deletions inst/htmlwidgets/plotly.js
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@

HTMLWidgets.widget({
name: "plotly",
type: "output",

initialize: function(el, width, height){
initialize: function(el, width, height){
return {};
},

resize: function(el, width, height, instance) {
// TODO: impose fixed coordinates, if specified (see #342)
Plotly.relayout(el.id, {width: width, height: height});
},

Expand All@@ -15,13 +17,57 @@ HTMLWidgets.widget({
window.PLOTLYENV = window.PLOTLYENV || {};
window.PLOTLYENV.BASE_URL = x.base_url;

var graphDiv = document.getElementById(el.id);

// if no plot exists yet, create one with a particular configuration
if (!instance.plotly) {
Plotly.plot(el.id, x.data, x.layout, x.config);
Plotly.plot(graphDiv, x.data, x.layout, x.config);
instance.plotly = true;
} else {
Plotly.newPlot(el.id, x.data, x.layout);
Plotly.newPlot(graphDiv, x.data, x.layout);
}

var g = x.data[0].set;
var grp = crosstalk.group(g);

graphDiv.on('plotly_click', function(eventData) {
// extract only the data we may want to access in R
var d = eventData.points.map(function(pt) {
var obj = {
curveNumber: pt.curveNumber,
pointNumber: pt.pointNumber,
x: pt.x,
y: pt.y
};
if (pt.data.hasOwnProperty("key")) {
if (typeof pt.pointNumber === "number") {
obj.key = pt.data.key[pt.pointNumber];
} else {
obj.key = pt.data.key[pt.pointNumber[0]][pt.pointNumber[1]];
} // TODO: can pointNumber be 3D?
}
return obj;
});

// tell crosstalk about click data so we can access it in R (and JS)
grp.var("plotly_click").set(d);
});

graphDiv.on('plotly_selected', function(eventData) {
if (eventData !== undefined) {
// convert the array of objects to object of arrays so this converts
// to data frame in R as opposed to a vector
var pts = eventData.points;
var obj = {
curveNumber: pts.map(function(pt) {return pt.curveNumber; }),
pointNumber: pts.map(function(pt) {return pt.pointNumber; }),
x: pts.map(function(pt) {return pt.x; }),
y: pts.map(function(pt) {return pt.y; })
};
grp.var("plotly_selected").set(obj);
}
});

}

});
5 changes: 4 additions & 1 deletion man/plotly-shiny.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Closed
6 changes: 4 additions & 2 deletions .travis.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -28,8 +28,10 @@ before_script:
- git clone https://github.com/cpsievert/plotly-test-table.git ../plotly-test-table
- "wget -q -O - https://github.com/yihui/crandalf/raw/master/inst/scripts/install-pandoc | bash"
- Rscript -e 'if (length(find.package("devtools", quiet = TRUE)) == 0L) { install.packages("devtools", repos = "http://cran.rstudio.com") }'
- Rscript -e 'library(devtools);update_packages("devtools", repos = "http://cran.rstudio.com")'
- Rscript -e 'library(devtools);install_deps(repos = "http://cran.rstudio.com", dependencies = TRUE)'
- Rscript -e 'devtools::update_packages("devtools", repos = "http://cran.rstudio.com")'
- Rscript -e 'devtools::install_deps(repos = "http://cran.rstudio.com", dependencies = TRUE)'
- Rscript -e 'devtools::install_github("rstudio/crosstalk")'
- Rscript -e 'devtools::install_github("cpsievert/htmlwidgets")'

script:
# run R CMD check on the non-pull request build
Expand Down
1 change: 1 addition & 0 deletions DESCRIPTION
Original file line numberDiff line numberDiff line change
Expand Up@@ -30,6 +30,7 @@ Imports:
viridis,
base64enc,
htmlwidgets,
crosstalk,
plyr
Suggests:
dplyr,
Expand Down
15 changes: 10 additions & 5 deletions R/plotly.R
Original file line numberDiff line numberDiff line change
Expand Up@@ -19,6 +19,8 @@
#' @param symbols A character vector of symbol types. Possible values:
#' 'dot', 'cross', 'diamond', 'square', 'triangle-down', 'triangle-left', 'triangle-right', 'triangle-up'
#' @param size A variable name or numeric vector to encode the size of markers.
#' @param key a selection variable for linked views
#' @param set share selections across this grouping variable.
#' @param width Width in pixels (optional, defaults to automatic sizing).
#' @param height Height in pixels (optional, defaults to automatic sizing).
#' @param inherit logical. Should future traces inherit properties from this initial trace?
Expand DownExpand Up@@ -64,9 +66,9 @@
#' }
#'
plot_ly <- function(data = data.frame(), ..., type = "scatter",
group, color, colors, symbol, symbols, size,
width = NULL, height = NULL, inherit = FALSE,
evaluate = FALSE) {
group, color, colors, symbol, symbols, size,
key, set = "A", width = NULL, height = NULL,
inherit = FALSE, evaluate = FALSE) {
# "native" plotly arguments
argz <- substitute(list(...))
# old arguments to this function that are no longer supported
Expand All@@ -83,16 +85,19 @@ plot_ly <- function(data = data.frame(), ..., type = "scatter",
if (!missing(symbol)) argz$symbol <- substitute(symbol)
if (!missing(symbols)) argz$symbols <- substitute(symbols)
if (!missing(size)) argz$size <- substitute(size)
if (!missing(key)) argz$key <- substitute(key)
# trace information
tr <- list(
type = type,
args = argz,
env = list2env(data), # environment in which to evaluate arguments
enclos = parent.frame(), # if objects aren't found in env, look here
inherit = inherit
inherit = inherit,
set = set
)
# plotly objects should always have a _list_ of trace(s)

p <- list(
# plotly objects should always have a _list_ of trace(s)
data = list(tr),
layout = NULL,
url = NULL,
Expand Down
1 change: 1 addition & 0 deletions R/print.R
Original file line numberDiff line numberDiff line change
Expand Up@@ -49,6 +49,7 @@ as.widget <- function(x, ...) {
padding = 5,
browser.fill = TRUE
),
dependencies = crosstalk::dependencies,
...
)
}
Expand Down
14 changes: 12 additions & 2 deletions R/shiny.R
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,6 +7,8 @@
#' @param width,height Must be a valid CSS unit (like \code{"100\%"},
#' \code{"400px"}, \code{"auto"}) or a number, which will be coerced to a
#' string and have \code{"px"} appended.
#' @param inline use an inline (\code{span()}) or block container
#' (\code{div()}) for the output
#' @param expr An expression that generates a plotly
#' @param env The environment in which to evaluate \code{expr}.
#' @param quoted Is \code{expr} a quoted expression (with \code{quote()})? This
Expand All@@ -17,8 +19,16 @@
#' @name plotly-shiny
#'
#' @export
plotlyOutput <- function(outputId, width = "100%", height = "400px") {
htmlwidgets::shinyWidgetOutput(outputId, "plotly", width, height, package = "plotly")
plotlyOutput <- function(outputId, width = "100%", height = "400px",
inline = FALSE) {
htmlwidgets::shinyWidgetOutput(
outputId = outputId,
name = "plotly",
width = width,
height = height,
inline = inline,
package = "plotly"
)
}

#' @rdname plotly-shiny
Expand Down
34 changes: 34 additions & 0 deletions inst/examples/crosstalk.R
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
# access renderPlotly() selections on a shiny server
# https://github.com/cpsievert/shiny_apps/blob/master/plotlyCrosstalk/app.R

# TODO:
# (1) pass selections from another htmlwidget (rcdimple?) to renderPlotly()
# (2) crosstalk without shiny (see examples below)
# (3) define custom event behavior in JS from R?

library(plotly)
library(htmltools)

# click/show selects?
mtcars$gear <- factor(mtcars$gear)
mtcars$cyl <- factor(mtcars$cyl)
p1 <- plot_ly(mtcars, x = wt, y = mpg, color = gear, mode = "markers",
key = gear, set = "A", width = 400)
p2 <- plot_ly(mtcars, x = wt, y = disp, color = gear, mode = "markers",
key = gear, set = "A", width = 400)
# TODO: inline-block?
browsable(tagList(
as.widget(p1),
as.widget(p2)
))

library(dplyr)
m <- count(mtcars, cyl)
p1 <- plot_ly(m, x = cyl, y = n, type = "bar",
key = cyl, set = "A", width = 400)
p2 <- plot_ly(mtcars, x = mpg, y = disp, mode = "markers",
key = cyl, set = "A", width = 400)
browsable(tagList(
as.widget(p1),
as.widget(p2)
))
52 changes: 49 additions & 3 deletions inst/htmlwidgets/plotly.js
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@

HTMLWidgets.widget({
name: "plotly",
type: "output",

initialize: function(el, width, height){
initialize: function(el, width, height){
return {};
},

resize: function(el, width, height, instance) {
// TODO: impose fixed coordinates, if specified (see #342)
Plotly.relayout(el.id, {width: width, height: height});
},

Expand All@@ -15,13 +17,57 @@ HTMLWidgets.widget({
window.PLOTLYENV = window.PLOTLYENV || {};
window.PLOTLYENV.BASE_URL = x.base_url;

var graphDiv = document.getElementById(el.id);

// if no plot exists yet, create one with a particular configuration
if (!instance.plotly) {
Plotly.plot(el.id, x.data, x.layout, x.config);
Plotly.plot(graphDiv, x.data, x.layout, x.config);
instance.plotly = true;
} else {
Plotly.newPlot(el.id, x.data, x.layout);
Plotly.newPlot(graphDiv, x.data, x.layout);
}

var g = x.data[0].set;
var grp = crosstalk.group(g);

graphDiv.on('plotly_click', function(eventData) {
// extract only the data we may want to access in R
var d = eventData.points.map(function(pt) {
var obj = {
curveNumber: pt.curveNumber,
pointNumber: pt.pointNumber,
x: pt.x,
y: pt.y
};
if (pt.data.hasOwnProperty("key")) {
if (typeof pt.pointNumber === "number") {
obj.key = pt.data.key[pt.pointNumber];
} else {
obj.key = pt.data.key[pt.pointNumber[0]][pt.pointNumber[1]];
} // TODO: can pointNumber be 3D?
}
return obj;
});

// tell crosstalk about click data so we can access it in R (and JS)
grp.var("plotly_click").set(d);
});

graphDiv.on('plotly_selected', function(eventData) {
if (eventData !== undefined) {
// convert the array of objects to object of arrays so this converts
// to data frame in R as opposed to a vector
var pts = eventData.points;
var obj = {
curveNumber: pts.map(function(pt) {return pt.curveNumber; }),
pointNumber: pts.map(function(pt) {return pt.pointNumber; }),
x: pts.map(function(pt) {return pt.x; }),
y: pts.map(function(pt) {return pt.y; })
};
grp.var("plotly_selected").set(obj);
}
});

}

});
5 changes: 4 additions & 1 deletion man/plotly-shiny.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content
Closed
6 changes: 4 additions & 2 deletions .travis.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -28,8 +28,10 @@ before_script:
- git clone https://github.com/cpsievert/plotly-test-table.git ../plotly-test-table
- "wget -q -O - https://github.com/yihui/crandalf/raw/master/inst/scripts/install-pandoc | bash"
- Rscript -e 'if (length(find.package("devtools", quiet = TRUE)) == 0L) { install.packages("devtools", repos = "http://cran.rstudio.com") }'
- Rscript -e 'library(devtools);update_packages("devtools", repos = "http://cran.rstudio.com")'
- Rscript -e 'library(devtools);install_deps(repos = "http://cran.rstudio.com", dependencies = TRUE)'
- Rscript -e 'devtools::update_packages("devtools", repos = "http://cran.rstudio.com")'
- Rscript -e 'devtools::install_deps(repos = "http://cran.rstudio.com", dependencies = TRUE)'
- Rscript -e 'devtools::install_github("rstudio/crosstalk")'
- Rscript -e 'devtools::install_github("cpsievert/htmlwidgets")'

script:
# run R CMD check on the non-pull request build
Expand Down
1 change: 1 addition & 0 deletions DESCRIPTION
Original file line numberDiff line numberDiff line change
Expand Up@@ -30,6 +30,7 @@ Imports:
viridis,
base64enc,
htmlwidgets,
crosstalk,
plyr
Suggests:
dplyr,
Expand Down
15 changes: 10 additions & 5 deletions R/plotly.R
Original file line numberDiff line numberDiff line change
Expand Up@@ -19,6 +19,8 @@
#' @param symbols A character vector of symbol types. Possible values:
#' 'dot', 'cross', 'diamond', 'square', 'triangle-down', 'triangle-left', 'triangle-right', 'triangle-up'
#' @param size A variable name or numeric vector to encode the size of markers.
#' @param key a selection variable for linked views
#' @param set share selections across this grouping variable.
#' @param width Width in pixels (optional, defaults to automatic sizing).
#' @param height Height in pixels (optional, defaults to automatic sizing).
#' @param inherit logical. Should future traces inherit properties from this initial trace?
Expand DownExpand Up@@ -64,9 +66,9 @@
#' }
#'
plot_ly <- function(data = data.frame(), ..., type = "scatter",
group, color, colors, symbol, symbols, size,
width = NULL, height = NULL, inherit = FALSE,
evaluate = FALSE) {
group, color, colors, symbol, symbols, size,
key, set = "A", width = NULL, height = NULL,
inherit = FALSE, evaluate = FALSE) {
# "native" plotly arguments
argz <- substitute(list(...))
# old arguments to this function that are no longer supported
Expand All@@ -83,16 +85,19 @@ plot_ly <- function(data = data.frame(), ..., type = "scatter",
if (!missing(symbol)) argz$symbol <- substitute(symbol)
if (!missing(symbols)) argz$symbols <- substitute(symbols)
if (!missing(size)) argz$size <- substitute(size)
if (!missing(key)) argz$key <- substitute(key)
# trace information
tr <- list(
type = type,
args = argz,
env = list2env(data), # environment in which to evaluate arguments
enclos = parent.frame(), # if objects aren't found in env, look here
inherit = inherit
inherit = inherit,
set = set
)
# plotly objects should always have a _list_ of trace(s)

p <- list(
# plotly objects should always have a _list_ of trace(s)
data = list(tr),
layout = NULL,
url = NULL,
Expand Down
1 change: 1 addition & 0 deletions R/print.R
Original file line numberDiff line numberDiff line change
Expand Up@@ -49,6 +49,7 @@ as.widget <- function(x, ...) {
padding = 5,
browser.fill = TRUE
),
dependencies = crosstalk::dependencies,
...
)
}
Expand Down
14 changes: 12 additions & 2 deletions R/shiny.R
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,6 +7,8 @@
#' @param width,height Must be a valid CSS unit (like \code{"100\%"},
#' \code{"400px"}, \code{"auto"}) or a number, which will be coerced to a
#' string and have \code{"px"} appended.
#' @param inline use an inline (\code{span()}) or block container
#' (\code{div()}) for the output
#' @param expr An expression that generates a plotly
#' @param env The environment in which to evaluate \code{expr}.
#' @param quoted Is \code{expr} a quoted expression (with \code{quote()})? This
Expand All@@ -17,8 +19,16 @@
#' @name plotly-shiny
#'
#' @export
plotlyOutput <- function(outputId, width = "100%", height = "400px") {
htmlwidgets::shinyWidgetOutput(outputId, "plotly", width, height, package = "plotly")
plotlyOutput <- function(outputId, width = "100%", height = "400px",
inline = FALSE) {
htmlwidgets::shinyWidgetOutput(
outputId = outputId,
name = "plotly",
width = width,
height = height,
inline = inline,
package = "plotly"
)
}

#' @rdname plotly-shiny
Expand Down
34 changes: 34 additions & 0 deletions inst/examples/crosstalk.R
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
# access renderPlotly() selections on a shiny server
# https://github.com/cpsievert/shiny_apps/blob/master/plotlyCrosstalk/app.R

# TODO:
# (1) pass selections from another htmlwidget (rcdimple?) to renderPlotly()
# (2) crosstalk without shiny (see examples below)
# (3) define custom event behavior in JS from R?

library(plotly)
library(htmltools)

# click/show selects?
mtcars$gear <- factor(mtcars$gear)
mtcars$cyl <- factor(mtcars$cyl)
p1 <- plot_ly(mtcars, x = wt, y = mpg, color = gear, mode = "markers",
key = gear, set = "A", width = 400)
p2 <- plot_ly(mtcars, x = wt, y = disp, color = gear, mode = "markers",
key = gear, set = "A", width = 400)
# TODO: inline-block?
browsable(tagList(
as.widget(p1),
as.widget(p2)
))

library(dplyr)
m <- count(mtcars, cyl)
p1 <- plot_ly(m, x = cyl, y = n, type = "bar",
key = cyl, set = "A", width = 400)
p2 <- plot_ly(mtcars, x = mpg, y = disp, mode = "markers",
key = cyl, set = "A", width = 400)
browsable(tagList(
as.widget(p1),
as.widget(p2)
))
52 changes: 49 additions & 3 deletions inst/htmlwidgets/plotly.js
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@

HTMLWidgets.widget({
name: "plotly",
type: "output",

initialize: function(el, width, height){
initialize: function(el, width, height){
return {};
},

resize: function(el, width, height, instance) {
// TODO: impose fixed coordinates, if specified (see #342)
Plotly.relayout(el.id, {width: width, height: height});
},

Expand All@@ -15,13 +17,57 @@ HTMLWidgets.widget({
window.PLOTLYENV = window.PLOTLYENV || {};
window.PLOTLYENV.BASE_URL = x.base_url;

var graphDiv = document.getElementById(el.id);

// if no plot exists yet, create one with a particular configuration
if (!instance.plotly) {
Plotly.plot(el.id, x.data, x.layout, x.config);
Plotly.plot(graphDiv, x.data, x.layout, x.config);
instance.plotly = true;
} else {
Plotly.newPlot(el.id, x.data, x.layout);
Plotly.newPlot(graphDiv, x.data, x.layout);
}

var g = x.data[0].set;
var grp = crosstalk.group(g);

graphDiv.on('plotly_click', function(eventData) {
// extract only the data we may want to access in R
var d = eventData.points.map(function(pt) {
var obj = {
curveNumber: pt.curveNumber,
pointNumber: pt.pointNumber,
x: pt.x,
y: pt.y
};
if (pt.data.hasOwnProperty("key")) {
if (typeof pt.pointNumber === "number") {
obj.key = pt.data.key[pt.pointNumber];
} else {
obj.key = pt.data.key[pt.pointNumber[0]][pt.pointNumber[1]];
} // TODO: can pointNumber be 3D?
}
return obj;
});

// tell crosstalk about click data so we can access it in R (and JS)
grp.var("plotly_click").set(d);
});

graphDiv.on('plotly_selected', function(eventData) {
if (eventData !== undefined) {
// convert the array of objects to object of arrays so this converts
// to data frame in R as opposed to a vector
var pts = eventData.points;
var obj = {
curveNumber: pts.map(function(pt) {return pt.curveNumber; }),
pointNumber: pts.map(function(pt) {return pt.pointNumber; }),
x: pts.map(function(pt) {return pt.x; }),
y: pts.map(function(pt) {return pt.y; })
};
grp.var("plotly_selected").set(obj);
}
});

}

});
5 changes: 4 additions & 1 deletion man/plotly-shiny.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Closed
6 changes: 4 additions & 2 deletions .travis.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -28,8 +28,10 @@ before_script:
- git clone https://github.com/cpsievert/plotly-test-table.git ../plotly-test-table
- "wget -q -O - https://github.com/yihui/crandalf/raw/master/inst/scripts/install-pandoc | bash"
- Rscript -e 'if (length(find.package("devtools", quiet = TRUE)) == 0L) { install.packages("devtools", repos = "http://cran.rstudio.com") }'
- Rscript -e 'library(devtools);update_packages("devtools", repos = "http://cran.rstudio.com")'
- Rscript -e 'library(devtools);install_deps(repos = "http://cran.rstudio.com", dependencies = TRUE)'
- Rscript -e 'devtools::update_packages("devtools", repos = "http://cran.rstudio.com")'
- Rscript -e 'devtools::install_deps(repos = "http://cran.rstudio.com", dependencies = TRUE)'
- Rscript -e 'devtools::install_github("rstudio/crosstalk")'
- Rscript -e 'devtools::install_github("cpsievert/htmlwidgets")'

script:
# run R CMD check on the non-pull request build
Expand Down
1 change: 1 addition & 0 deletions DESCRIPTION
Original file line numberDiff line numberDiff line change
Expand Up@@ -30,6 +30,7 @@ Imports:
viridis,
base64enc,
htmlwidgets,
crosstalk,
plyr
Suggests:
dplyr,
Expand Down
15 changes: 10 additions & 5 deletions R/plotly.R
Original file line numberDiff line numberDiff line change
Expand Up@@ -19,6 +19,8 @@
#' @param symbols A character vector of symbol types. Possible values:
#' 'dot', 'cross', 'diamond', 'square', 'triangle-down', 'triangle-left', 'triangle-right', 'triangle-up'
#' @param size A variable name or numeric vector to encode the size of markers.
#' @param key a selection variable for linked views
#' @param set share selections across this grouping variable.
#' @param width Width in pixels (optional, defaults to automatic sizing).
#' @param height Height in pixels (optional, defaults to automatic sizing).
#' @param inherit logical. Should future traces inherit properties from this initial trace?
Expand DownExpand Up@@ -64,9 +66,9 @@
#' }
#'
plot_ly <- function(data = data.frame(), ..., type = "scatter",
group, color, colors, symbol, symbols, size,
width = NULL, height = NULL, inherit = FALSE,
evaluate = FALSE) {
group, color, colors, symbol, symbols, size,
key, set = "A", width = NULL, height = NULL,
inherit = FALSE, evaluate = FALSE) {
# "native" plotly arguments
argz <- substitute(list(...))
# old arguments to this function that are no longer supported
Expand All@@ -83,16 +85,19 @@ plot_ly <- function(data = data.frame(), ..., type = "scatter",
if (!missing(symbol)) argz$symbol <- substitute(symbol)
if (!missing(symbols)) argz$symbols <- substitute(symbols)
if (!missing(size)) argz$size <- substitute(size)
if (!missing(key)) argz$key <- substitute(key)
# trace information
tr <- list(
type = type,
args = argz,
env = list2env(data), # environment in which to evaluate arguments
enclos = parent.frame(), # if objects aren't found in env, look here
inherit = inherit
inherit = inherit,
set = set
)
# plotly objects should always have a _list_ of trace(s)

p <- list(
# plotly objects should always have a _list_ of trace(s)
data = list(tr),
layout = NULL,
url = NULL,
Expand Down
1 change: 1 addition & 0 deletions R/print.R
Original file line numberDiff line numberDiff line change
Expand Up@@ -49,6 +49,7 @@ as.widget <- function(x, ...) {
padding = 5,
browser.fill = TRUE
),
dependencies = crosstalk::dependencies,
...
)
}
Expand Down
14 changes: 12 additions & 2 deletions R/shiny.R
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,6 +7,8 @@
#' @param width,height Must be a valid CSS unit (like \code{"100\%"},
#' \code{"400px"}, \code{"auto"}) or a number, which will be coerced to a
#' string and have \code{"px"} appended.
#' @param inline use an inline (\code{span()}) or block container
#' (\code{div()}) for the output
#' @param expr An expression that generates a plotly
#' @param env The environment in which to evaluate \code{expr}.
#' @param quoted Is \code{expr} a quoted expression (with \code{quote()})? This
Expand All@@ -17,8 +19,16 @@
#' @name plotly-shiny
#'
#' @export
plotlyOutput <- function(outputId, width = "100%", height = "400px") {
htmlwidgets::shinyWidgetOutput(outputId, "plotly", width, height, package = "plotly")
plotlyOutput <- function(outputId, width = "100%", height = "400px",
inline = FALSE) {
htmlwidgets::shinyWidgetOutput(
outputId = outputId,
name = "plotly",
width = width,
height = height,
inline = inline,
package = "plotly"
)
}

#' @rdname plotly-shiny
Expand Down
34 changes: 34 additions & 0 deletions inst/examples/crosstalk.R
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
# access renderPlotly() selections on a shiny server
# https://github.com/cpsievert/shiny_apps/blob/master/plotlyCrosstalk/app.R

# TODO:
# (1) pass selections from another htmlwidget (rcdimple?) to renderPlotly()
# (2) crosstalk without shiny (see examples below)
# (3) define custom event behavior in JS from R?

library(plotly)
library(htmltools)

# click/show selects?
mtcars$gear <- factor(mtcars$gear)
mtcars$cyl <- factor(mtcars$cyl)
p1 <- plot_ly(mtcars, x = wt, y = mpg, color = gear, mode = "markers",
key = gear, set = "A", width = 400)
p2 <- plot_ly(mtcars, x = wt, y = disp, color = gear, mode = "markers",
key = gear, set = "A", width = 400)
# TODO: inline-block?
browsable(tagList(
as.widget(p1),
as.widget(p2)
))

library(dplyr)
m <- count(mtcars, cyl)
p1 <- plot_ly(m, x = cyl, y = n, type = "bar",
key = cyl, set = "A", width = 400)
p2 <- plot_ly(mtcars, x = mpg, y = disp, mode = "markers",
key = cyl, set = "A", width = 400)
browsable(tagList(
as.widget(p1),
as.widget(p2)
))
52 changes: 49 additions & 3 deletions inst/htmlwidgets/plotly.js
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@

HTMLWidgets.widget({
name: "plotly",
type: "output",

initialize: function(el, width, height){
initialize: function(el, width, height){
return {};
},

resize: function(el, width, height, instance) {
// TODO: impose fixed coordinates, if specified (see #342)
Plotly.relayout(el.id, {width: width, height: height});
},

Expand All@@ -15,13 +17,57 @@ HTMLWidgets.widget({
window.PLOTLYENV = window.PLOTLYENV || {};
window.PLOTLYENV.BASE_URL = x.base_url;

var graphDiv = document.getElementById(el.id);

// if no plot exists yet, create one with a particular configuration
if (!instance.plotly) {
Plotly.plot(el.id, x.data, x.layout, x.config);
Plotly.plot(graphDiv, x.data, x.layout, x.config);
instance.plotly = true;
} else {
Plotly.newPlot(el.id, x.data, x.layout);
Plotly.newPlot(graphDiv, x.data, x.layout);
}

var g = x.data[0].set;
var grp = crosstalk.group(g);

graphDiv.on('plotly_click', function(eventData) {
// extract only the data we may want to access in R
var d = eventData.points.map(function(pt) {
var obj = {
curveNumber: pt.curveNumber,
pointNumber: pt.pointNumber,
x: pt.x,
y: pt.y
};
if (pt.data.hasOwnProperty("key")) {
if (typeof pt.pointNumber === "number") {
obj.key = pt.data.key[pt.pointNumber];
} else {
obj.key = pt.data.key[pt.pointNumber[0]][pt.pointNumber[1]];
} // TODO: can pointNumber be 3D?
}
return obj;
});

// tell crosstalk about click data so we can access it in R (and JS)
grp.var("plotly_click").set(d);
});

graphDiv.on('plotly_selected', function(eventData) {
if (eventData !== undefined) {
// convert the array of objects to object of arrays so this converts
// to data frame in R as opposed to a vector
var pts = eventData.points;
var obj = {
curveNumber: pts.map(function(pt) {return pt.curveNumber; }),
pointNumber: pts.map(function(pt) {return pt.pointNumber; }),
x: pts.map(function(pt) {return pt.x; }),
y: pts.map(function(pt) {return pt.y; })
};
grp.var("plotly_selected").set(obj);
}
});

}

});
5 changes: 4 additions & 1 deletion man/plotly-shiny.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Closed
6 changes: 4 additions & 2 deletions .travis.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -28,8 +28,10 @@ before_script:
- git clone https://github.com/cpsievert/plotly-test-table.git ../plotly-test-table
- "wget -q -O - https://github.com/yihui/crandalf/raw/master/inst/scripts/install-pandoc | bash"
- Rscript -e 'if (length(find.package("devtools", quiet = TRUE)) == 0L) { install.packages("devtools", repos = "http://cran.rstudio.com") }'
- Rscript -e 'library(devtools);update_packages("devtools", repos = "http://cran.rstudio.com")'
- Rscript -e 'library(devtools);install_deps(repos = "http://cran.rstudio.com", dependencies = TRUE)'
- Rscript -e 'devtools::update_packages("devtools", repos = "http://cran.rstudio.com")'
- Rscript -e 'devtools::install_deps(repos = "http://cran.rstudio.com", dependencies = TRUE)'
- Rscript -e 'devtools::install_github("rstudio/crosstalk")'
- Rscript -e 'devtools::install_github("cpsievert/htmlwidgets")'

script:
# run R CMD check on the non-pull request build
Expand Down
1 change: 1 addition & 0 deletions DESCRIPTION
Original file line numberDiff line numberDiff line change
Expand Up@@ -30,6 +30,7 @@ Imports:
viridis,
base64enc,
htmlwidgets,
crosstalk,
plyr
Suggests:
dplyr,
Expand Down
15 changes: 10 additions & 5 deletions R/plotly.R
Original file line numberDiff line numberDiff line change
Expand Up@@ -19,6 +19,8 @@
#' @param symbols A character vector of symbol types. Possible values:
#' 'dot', 'cross', 'diamond', 'square', 'triangle-down', 'triangle-left', 'triangle-right', 'triangle-up'
#' @param size A variable name or numeric vector to encode the size of markers.
#' @param key a selection variable for linked views
#' @param set share selections across this grouping variable.
#' @param width Width in pixels (optional, defaults to automatic sizing).
#' @param height Height in pixels (optional, defaults to automatic sizing).
#' @param inherit logical. Should future traces inherit properties from this initial trace?
Expand DownExpand Up@@ -64,9 +66,9 @@
#' }
#'
plot_ly <- function(data = data.frame(), ..., type = "scatter",
group, color, colors, symbol, symbols, size,
width = NULL, height = NULL, inherit = FALSE,
evaluate = FALSE) {
group, color, colors, symbol, symbols, size,
key, set = "A", width = NULL, height = NULL,
inherit = FALSE, evaluate = FALSE) {
# "native" plotly arguments
argz <- substitute(list(...))
# old arguments to this function that are no longer supported
Expand All@@ -83,16 +85,19 @@ plot_ly <- function(data = data.frame(), ..., type = "scatter",
if (!missing(symbol)) argz$symbol <- substitute(symbol)
if (!missing(symbols)) argz$symbols <- substitute(symbols)
if (!missing(size)) argz$size <- substitute(size)
if (!missing(key)) argz$key <- substitute(key)
# trace information
tr <- list(
type = type,
args = argz,
env = list2env(data), # environment in which to evaluate arguments
enclos = parent.frame(), # if objects aren't found in env, look here
inherit = inherit
inherit = inherit,
set = set
)
# plotly objects should always have a _list_ of trace(s)

p <- list(
# plotly objects should always have a _list_ of trace(s)
data = list(tr),
layout = NULL,
url = NULL,
Expand Down
1 change: 1 addition & 0 deletions R/print.R
Original file line numberDiff line numberDiff line change
Expand Up@@ -49,6 +49,7 @@ as.widget <- function(x, ...) {
padding = 5,
browser.fill = TRUE
),
dependencies = crosstalk::dependencies,
...
)
}
Expand Down
14 changes: 12 additions & 2 deletions R/shiny.R
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,6 +7,8 @@
#' @param width,height Must be a valid CSS unit (like \code{"100\%"},
#' \code{"400px"}, \code{"auto"}) or a number, which will be coerced to a
#' string and have \code{"px"} appended.
#' @param inline use an inline (\code{span()}) or block container
#' (\code{div()}) for the output
#' @param expr An expression that generates a plotly
#' @param env The environment in which to evaluate \code{expr}.
#' @param quoted Is \code{expr} a quoted expression (with \code{quote()})? This
Expand All@@ -17,8 +19,16 @@
#' @name plotly-shiny
#'
#' @export
plotlyOutput <- function(outputId, width = "100%", height = "400px") {
htmlwidgets::shinyWidgetOutput(outputId, "plotly", width, height, package = "plotly")
plotlyOutput <- function(outputId, width = "100%", height = "400px",
inline = FALSE) {
htmlwidgets::shinyWidgetOutput(
outputId = outputId,
name = "plotly",
width = width,
height = height,
inline = inline,
package = "plotly"
)
}

#' @rdname plotly-shiny
Expand Down
34 changes: 34 additions & 0 deletions inst/examples/crosstalk.R
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
# access renderPlotly() selections on a shiny server
# https://github.com/cpsievert/shiny_apps/blob/master/plotlyCrosstalk/app.R

# TODO:
# (1) pass selections from another htmlwidget (rcdimple?) to renderPlotly()
# (2) crosstalk without shiny (see examples below)
# (3) define custom event behavior in JS from R?

library(plotly)
library(htmltools)

# click/show selects?
mtcars$gear <- factor(mtcars$gear)
mtcars$cyl <- factor(mtcars$cyl)
p1 <- plot_ly(mtcars, x = wt, y = mpg, color = gear, mode = "markers",
key = gear, set = "A", width = 400)
p2 <- plot_ly(mtcars, x = wt, y = disp, color = gear, mode = "markers",
key = gear, set = "A", width = 400)
# TODO: inline-block?
browsable(tagList(
as.widget(p1),
as.widget(p2)
))

library(dplyr)
m <- count(mtcars, cyl)
p1 <- plot_ly(m, x = cyl, y = n, type = "bar",
key = cyl, set = "A", width = 400)
p2 <- plot_ly(mtcars, x = mpg, y = disp, mode = "markers",
key = cyl, set = "A", width = 400)
browsable(tagList(
as.widget(p1),
as.widget(p2)
))
52 changes: 49 additions & 3 deletions inst/htmlwidgets/plotly.js
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@

HTMLWidgets.widget({
name: "plotly",
type: "output",

initialize: function(el, width, height){
initialize: function(el, width, height){
return {};
},

resize: function(el, width, height, instance) {
// TODO: impose fixed coordinates, if specified (see #342)
Plotly.relayout(el.id, {width: width, height: height});
},

Expand All@@ -15,13 +17,57 @@ HTMLWidgets.widget({
window.PLOTLYENV = window.PLOTLYENV || {};
window.PLOTLYENV.BASE_URL = x.base_url;

var graphDiv = document.getElementById(el.id);

// if no plot exists yet, create one with a particular configuration
if (!instance.plotly) {
Plotly.plot(el.id, x.data, x.layout, x.config);
Plotly.plot(graphDiv, x.data, x.layout, x.config);
instance.plotly = true;
} else {
Plotly.newPlot(el.id, x.data, x.layout);
Plotly.newPlot(graphDiv, x.data, x.layout);
}

var g = x.data[0].set;
var grp = crosstalk.group(g);

graphDiv.on('plotly_click', function(eventData) {
// extract only the data we may want to access in R
var d = eventData.points.map(function(pt) {
var obj = {
curveNumber: pt.curveNumber,
pointNumber: pt.pointNumber,
x: pt.x,
y: pt.y
};
if (pt.data.hasOwnProperty("key")) {
if (typeof pt.pointNumber === "number") {
obj.key = pt.data.key[pt.pointNumber];
} else {
obj.key = pt.data.key[pt.pointNumber[0]][pt.pointNumber[1]];
} // TODO: can pointNumber be 3D?
}
return obj;
});

// tell crosstalk about click data so we can access it in R (and JS)
grp.var("plotly_click").set(d);
});

graphDiv.on('plotly_selected', function(eventData) {
if (eventData !== undefined) {
// convert the array of objects to object of arrays so this converts
// to data frame in R as opposed to a vector
var pts = eventData.points;
var obj = {
curveNumber: pts.map(function(pt) {return pt.curveNumber; }),
pointNumber: pts.map(function(pt) {return pt.pointNumber; }),
x: pts.map(function(pt) {return pt.x; }),
y: pts.map(function(pt) {return pt.y; })
};
grp.var("plotly_selected").set(obj);
}
});

}

});
5 changes: 4 additions & 1 deletion man/plotly-shiny.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content
Closed
6 changes: 4 additions & 2 deletions .travis.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -28,8 +28,10 @@ before_script:
- git clone https://github.com/cpsievert/plotly-test-table.git ../plotly-test-table
- "wget -q -O - https://github.com/yihui/crandalf/raw/master/inst/scripts/install-pandoc | bash"
- Rscript -e 'if (length(find.package("devtools", quiet = TRUE)) == 0L) { install.packages("devtools", repos = "http://cran.rstudio.com") }'
- Rscript -e 'library(devtools);update_packages("devtools", repos = "http://cran.rstudio.com")'
- Rscript -e 'library(devtools);install_deps(repos = "http://cran.rstudio.com", dependencies = TRUE)'
- Rscript -e 'devtools::update_packages("devtools", repos = "http://cran.rstudio.com")'
- Rscript -e 'devtools::install_deps(repos = "http://cran.rstudio.com", dependencies = TRUE)'
- Rscript -e 'devtools::install_github("rstudio/crosstalk")'
- Rscript -e 'devtools::install_github("cpsievert/htmlwidgets")'

script:
# run R CMD check on the non-pull request build
Expand Down
1 change: 1 addition & 0 deletions DESCRIPTION
Original file line numberDiff line numberDiff line change
Expand Up@@ -30,6 +30,7 @@ Imports:
viridis,
base64enc,
htmlwidgets,
crosstalk,
plyr
Suggests:
dplyr,
Expand Down
15 changes: 10 additions & 5 deletions R/plotly.R
Original file line numberDiff line numberDiff line change
Expand Up@@ -19,6 +19,8 @@
#' @param symbols A character vector of symbol types. Possible values:
#' 'dot', 'cross', 'diamond', 'square', 'triangle-down', 'triangle-left', 'triangle-right', 'triangle-up'
#' @param size A variable name or numeric vector to encode the size of markers.
#' @param key a selection variable for linked views
#' @param set share selections across this grouping variable.
#' @param width Width in pixels (optional, defaults to automatic sizing).
#' @param height Height in pixels (optional, defaults to automatic sizing).
#' @param inherit logical. Should future traces inherit properties from this initial trace?
Expand DownExpand Up@@ -64,9 +66,9 @@
#' }
#'
plot_ly <- function(data = data.frame(), ..., type = "scatter",
group, color, colors, symbol, symbols, size,
width = NULL, height = NULL, inherit = FALSE,
evaluate = FALSE) {
group, color, colors, symbol, symbols, size,
key, set = "A", width = NULL, height = NULL,
inherit = FALSE, evaluate = FALSE) {
# "native" plotly arguments
argz <- substitute(list(...))
# old arguments to this function that are no longer supported
Expand All@@ -83,16 +85,19 @@ plot_ly <- function(data = data.frame(), ..., type = "scatter",
if (!missing(symbol)) argz$symbol <- substitute(symbol)
if (!missing(symbols)) argz$symbols <- substitute(symbols)
if (!missing(size)) argz$size <- substitute(size)
if (!missing(key)) argz$key <- substitute(key)
# trace information
tr <- list(
type = type,
args = argz,
env = list2env(data), # environment in which to evaluate arguments
enclos = parent.frame(), # if objects aren't found in env, look here
inherit = inherit
inherit = inherit,
set = set
)
# plotly objects should always have a _list_ of trace(s)

p <- list(
# plotly objects should always have a _list_ of trace(s)
data = list(tr),
layout = NULL,
url = NULL,
Expand Down
1 change: 1 addition & 0 deletions R/print.R
Original file line numberDiff line numberDiff line change
Expand Up@@ -49,6 +49,7 @@ as.widget <- function(x, ...) {
padding = 5,
browser.fill = TRUE
),
dependencies = crosstalk::dependencies,
...
)
}
Expand Down
14 changes: 12 additions & 2 deletions R/shiny.R
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,6 +7,8 @@
#' @param width,height Must be a valid CSS unit (like \code{"100\%"},
#' \code{"400px"}, \code{"auto"}) or a number, which will be coerced to a
#' string and have \code{"px"} appended.
#' @param inline use an inline (\code{span()}) or block container
#' (\code{div()}) for the output
#' @param expr An expression that generates a plotly
#' @param env The environment in which to evaluate \code{expr}.
#' @param quoted Is \code{expr} a quoted expression (with \code{quote()})? This
Expand All@@ -17,8 +19,16 @@
#' @name plotly-shiny
#'
#' @export
plotlyOutput <- function(outputId, width = "100%", height = "400px") {
htmlwidgets::shinyWidgetOutput(outputId, "plotly", width, height, package = "plotly")
plotlyOutput <- function(outputId, width = "100%", height = "400px",
inline = FALSE) {
htmlwidgets::shinyWidgetOutput(
outputId = outputId,
name = "plotly",
width = width,
height = height,
inline = inline,
package = "plotly"
)
}

#' @rdname plotly-shiny
Expand Down
34 changes: 34 additions & 0 deletions inst/examples/crosstalk.R
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
# access renderPlotly() selections on a shiny server
# https://github.com/cpsievert/shiny_apps/blob/master/plotlyCrosstalk/app.R

# TODO:
# (1) pass selections from another htmlwidget (rcdimple?) to renderPlotly()
# (2) crosstalk without shiny (see examples below)
# (3) define custom event behavior in JS from R?

library(plotly)
library(htmltools)

# click/show selects?
mtcars$gear <- factor(mtcars$gear)
mtcars$cyl <- factor(mtcars$cyl)
p1 <- plot_ly(mtcars, x = wt, y = mpg, color = gear, mode = "markers",
key = gear, set = "A", width = 400)
p2 <- plot_ly(mtcars, x = wt, y = disp, color = gear, mode = "markers",
key = gear, set = "A", width = 400)
# TODO: inline-block?
browsable(tagList(
as.widget(p1),
as.widget(p2)
))

library(dplyr)
m <- count(mtcars, cyl)
p1 <- plot_ly(m, x = cyl, y = n, type = "bar",
key = cyl, set = "A", width = 400)
p2 <- plot_ly(mtcars, x = mpg, y = disp, mode = "markers",
key = cyl, set = "A", width = 400)
browsable(tagList(
as.widget(p1),
as.widget(p2)
))
52 changes: 49 additions & 3 deletions inst/htmlwidgets/plotly.js
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@

HTMLWidgets.widget({
name: "plotly",
type: "output",

initialize: function(el, width, height){
initialize: function(el, width, height){
return {};
},

resize: function(el, width, height, instance) {
// TODO: impose fixed coordinates, if specified (see #342)
Plotly.relayout(el.id, {width: width, height: height});
},

Expand All@@ -15,13 +17,57 @@ HTMLWidgets.widget({
window.PLOTLYENV = window.PLOTLYENV || {};
window.PLOTLYENV.BASE_URL = x.base_url;

var graphDiv = document.getElementById(el.id);

// if no plot exists yet, create one with a particular configuration
if (!instance.plotly) {
Plotly.plot(el.id, x.data, x.layout, x.config);
Plotly.plot(graphDiv, x.data, x.layout, x.config);
instance.plotly = true;
} else {
Plotly.newPlot(el.id, x.data, x.layout);
Plotly.newPlot(graphDiv, x.data, x.layout);
}

var g = x.data[0].set;
var grp = crosstalk.group(g);

graphDiv.on('plotly_click', function(eventData) {
// extract only the data we may want to access in R
var d = eventData.points.map(function(pt) {
var obj = {
curveNumber: pt.curveNumber,
pointNumber: pt.pointNumber,
x: pt.x,
y: pt.y
};
if (pt.data.hasOwnProperty("key")) {
if (typeof pt.pointNumber === "number") {
obj.key = pt.data.key[pt.pointNumber];
} else {
obj.key = pt.data.key[pt.pointNumber[0]][pt.pointNumber[1]];
} // TODO: can pointNumber be 3D?
}
return obj;
});

// tell crosstalk about click data so we can access it in R (and JS)
grp.var("plotly_click").set(d);
});

graphDiv.on('plotly_selected', function(eventData) {
if (eventData !== undefined) {
// convert the array of objects to object of arrays so this converts
// to data frame in R as opposed to a vector
var pts = eventData.points;
var obj = {
curveNumber: pts.map(function(pt) {return pt.curveNumber; }),
pointNumber: pts.map(function(pt) {return pt.pointNumber; }),
x: pts.map(function(pt) {return pt.x; }),
y: pts.map(function(pt) {return pt.y; })
};
grp.var("plotly_selected").set(obj);
}
});

}

});
5 changes: 4 additions & 1 deletion man/plotly-shiny.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.