diff --git a/NAMESPACE b/NAMESPACE index e28f2a5..28150d0 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -44,6 +44,7 @@ export(pie_opts) export(radialBar_opts) export(renderApexchart) export(set_input_click) +export(set_input_zoom) importFrom(ggplot2,aes) importFrom(htmlwidgets,JS) importFrom(htmlwidgets,createWidget) @@ -54,5 +55,6 @@ importFrom(magrittr,"%>%") importFrom(rlang,as_label) importFrom(rlang,eval_tidy) importFrom(shiny,getDefaultReactiveDomain) +importFrom(shiny,registerInputHandler) importFrom(stats,setNames) importFrom(utils,modifyList) diff --git a/R/onLoad.R b/R/onLoad.R new file mode 100644 index 0000000..05055f5 --- /dev/null +++ b/R/onLoad.R @@ -0,0 +1,29 @@ + +#' @importFrom shiny registerInputHandler +.onLoad <- function(...) { + shiny::registerInputHandler("apex_datetime", function(data, ...) { + if (is.null(data)) { + NULL + } else { + to_posix <- function(x) { + if (!is.null(x)) { + x <- as.POSIXct(x/1000, origin = "1970-01-01", tz = "UTC") + } + x + } + result <- try({ + data$x <- list( + min = to_posix(data$x$min), + max = to_posix(data$x$max) + ) + data + }, silent = TRUE) + if (inherits(result, "try-error")) { + warning("apexcharter: Failed to parse dates!", call. = FALSE) + data + } else { + result + } + } + }, force = TRUE) +} diff --git a/R/shiny-input.R b/R/shiny-input.R index 2a895c4..b4cb152 100644 --- a/R/shiny-input.R +++ b/R/shiny-input.R @@ -6,9 +6,10 @@ #' * **pie and donut:** retrieve label. #' * **time-series:** retrieve x-axis value, you have to display markers #' with size > 0 and set tooltip's options `intersect = TRUE` and `shared = FALSE`. +#' * **scatter:** retrieve coordinates. #' #' @param ax An \code{apexcharts} \code{htmlwidget} object. -#' @param inputId The id that will be used server-side for retrieveng category. +#' @param inputId The id that will be used server-side for retrieving click. #' @param multiple Allow multiple selection: \code{TRUE} or \code{FALSE} (default). #' @param effect_type Type of effect for selected element, default is to use lightly darken color. #' @param effect_value A larger value intensifies the select effect, accept value between 0 and 1. @@ -40,3 +41,25 @@ set_input_click <- function(ax, inputId, multiple = FALSE, } + + + +#' Retrieve zoom information in Shiny +#' +#' @param ax An \code{apexcharts} \code{htmlwidget} object. +#' @param inputId The id that will be used server-side for retrieving click. +#' @param session The Shiny session. +#' +#' @return An \code{apexcharts} \code{htmlwidget} object. +#' @export +#' +#' @examples +set_input_zoom <- function(ax, inputId, + session = shiny::getDefaultReactiveDomain()) { + ax$x$input$zoom <- list( + inputId = session$ns(inputId) + ) + ax +} + + diff --git a/inst/examples-input/zoom.R b/inst/examples-input/zoom.R new file mode 100644 index 0000000..e7c54a7 --- /dev/null +++ b/inst/examples-input/zoom.R @@ -0,0 +1,53 @@ + +library(shiny) +library(apexcharter) +data("economics", package = "ggplot2") + +ui <- fluidPage( + tags$h2("Retrieve zoom information"), + fluidRow( + column( + width = 6, + tags$b("Datetime"), + apexchartOutput("chart1") + ), + column( + width = 6, + verbatimTextOutput("result1") + ) + ), + fluidRow( + column( + width = 6, + tags$b("Scatter"), + apexchartOutput("chart2") + ), + column( + width = 6, + verbatimTextOutput("result2") + ) + ) +) + +server <- function(input, output, session) { + + output$chart1 <- renderApexchart({ + apex(economics, aes(date, psavert), type = "line") %>% + set_input_zoom("zoom_ts") + }) + output$result1 <- renderPrint({ + input$zoom_ts + }) + + output$chart2 <- renderApexchart({ + apex(iris, aes(Sepal.Length, Sepal.Width), type = "scatter") %>% + ax_chart(zoom = list(type = "xy")) %>% + set_input_zoom("zoom_scatter") + }) + output$result2 <- renderPrint({ + input$zoom_scatter + }) + +} + +shinyApp(ui, server) diff --git a/inst/htmlwidgets/apexcharter.js b/inst/htmlwidgets/apexcharter.js index c488ab4..829acbe 100644 --- a/inst/htmlwidgets/apexcharter.js +++ b/inst/htmlwidgets/apexcharter.js @@ -53,6 +53,18 @@ HTMLWidgets.widget({ Shiny.setInputValue(x.input.category.inputId, selected); }; } + if (x.input.hasOwnProperty("zoom")) { + ax_opts.chart.events.zoomed = function(chartContext, xaxis, yaxis) { + var id = x.input.zoom.inputId; + if (chartContext.w.config.xaxis.type == "datetime") { + id = id + ":apex_datetime"; + } + Shiny.setInputValue(id, { + x: getXaxis(xaxis), + y: getYaxis(xaxis) + }); + }; + } } // Generate or update chart @@ -155,6 +167,34 @@ function getSelection(opts, serieIndex) { return selected; } +function getYaxis(axis) { + var yzoom = { min: null, max: null }; + if (typeof axis.yaxis != "undefined") { + var y_axis = axis.yaxis[0]; + if (y_axis.hasOwnProperty("min") && typeof y_axis.min != "undefined") { + yzoom.min = y_axis.min; + } + if (y_axis.hasOwnProperty("max") && typeof y_axis.max != "undefined") { + yzoom.max = y_axis.max; + } + } + return yzoom; +} + +function getXaxis(axis) { + var xzoom = { min: null, max: null }; + if (typeof axis.xaxis != "undefined") { + var x_axis = axis.xaxis; + if (x_axis.hasOwnProperty("min") && typeof x_axis.min != "undefined") { + xzoom.min = x_axis.min; + } + if (x_axis.hasOwnProperty("max") && typeof x_axis.max != "undefined") { + xzoom.max = x_axis.max; + } + } + return xzoom; +} + if (HTMLWidgets.shinyMode) { // update serie Shiny.addCustomMessageHandler("update-apexchart-series", function(obj) { diff --git a/man/set_input_click.Rd b/man/set_input_click.Rd index 9b37864..0e67011 100644 --- a/man/set_input_click.Rd +++ b/man/set_input_click.Rd @@ -16,7 +16,7 @@ set_input_click( \arguments{ \item{ax}{An \code{apexcharts} \code{htmlwidget} object.} -\item{inputId}{The id that will be used server-side for retrieveng category.} +\item{inputId}{The id that will be used server-side for retrieving click.} \item{multiple}{Allow multiple selection: \code{TRUE} or \code{FALSE} (default).} @@ -36,5 +36,6 @@ According to type of chart, different values are retrieved: \item \strong{pie and donut:} retrieve label. \item \strong{time-series:} retrieve x-axis value, you have to display markers with size > 0 and set tooltip's options \code{intersect = TRUE} and \code{shared = FALSE}. +\item \strong{scatter:} retrieve coordinates. } } diff --git a/man/set_input_zoom.Rd b/man/set_input_zoom.Rd new file mode 100644 index 0000000..7346931 --- /dev/null +++ b/man/set_input_zoom.Rd @@ -0,0 +1,21 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/shiny-input.R +\name{set_input_zoom} +\alias{set_input_zoom} +\title{Retrieve zoom information in Shiny} +\usage{ +set_input_zoom(ax, inputId, session = shiny::getDefaultReactiveDomain()) +} +\arguments{ +\item{ax}{An \code{apexcharts} \code{htmlwidget} object.} + +\item{inputId}{The id that will be used server-side for retrieving click.} + +\item{session}{The Shiny session.} +} +\value{ +An \code{apexcharts} \code{htmlwidget} object. +} +\description{ +Retrieve zoom information in Shiny +} diff --git a/vignettes/sync-charts.Rmd b/vignettes/sync-charts.Rmd index a0cf11b..51fc9f6 100644 --- a/vignettes/sync-charts.Rmd +++ b/vignettes/sync-charts.Rmd @@ -72,7 +72,7 @@ apex( type = "line" ) %>% ax_chart( - id = "target-chart", + id = "target-chart", # <-- define target id here toolbar = list( autoSelected = "pan", show = FALSE @@ -87,11 +87,12 @@ apex( ) %>% ax_chart( brush = list( - target = "target-chart", + target = "target-chart", # <-- use target id here enabled = TRUE ), + offsetY = -20, selection = list( - enabled = TRUE, + enabled = TRUE, # <-- enable selection and define starting range xaxis = list( min = format_date(economics$date[1]), max = format_date(economics$date[100])