JohnCoene / echarts4r

🐳 ECharts 5 for R
http://echarts4r.john-coene.com/
Other
585 stars 82 forks source link

sync multiple drop downs when using e_morph #600

Closed oobd closed 7 months ago

oobd commented 7 months ago

trying to see if there's a way to synchronise multiple drop down across different locations (could be different slide on a xaringan presentation etc)

my current code works one way where if I select 2 and 2 on the first set of filters (A), the second set of filters (B) also changes to 2 and 2 and the charts update accordingly

the issue is that I can't seem to get it to work such that the second set of filters (B) are able to be changed (it just doesn't function) and update the first set of filters (A) / corresponding charts (i.e. if I select 2 and 3 on second set of filters (B), it should also update the first set to 2 and 3 change both charts to 2-3)

image

my guess it the callback function needs further modification but not too sure how, would really appreciate some help with this one if it's something that's possible, thanks!

library(echarts4r)
library(tidyverse)
library(htmlwidgets)
library(htmltools)

df <- data.frame(
  x = seq(50),
  y = rnorm(50, 10, 3),
  z = rnorm(50, 11, 2),
  w = rnorm(50, 9, 2)
)

e1  <- df |> 
  e_charts(x) |> 
  e_line(z) |> 
  e_area(w) |> 
  e_title("1-1")

e2  <- df |> 
  e_charts(x) |> 
  e_bar(y, name = "Serie 1") |> 
  e_step(z, name = "Serie 2") |> 
  e_title("1-2")

e3  <- df |> 
  e_charts(x) |> 
  e_scatter(y, z) |> 
  e_visual_map(z, scale = e_scale) |> # scale color
  e_legend(FALSE) |>
  e_title("1-3")

e4  <- df |> 
  e_charts(x) |> 
  e_polar() |> 
  e_angle_axis(x) |> # angle = x
  e_radius_axis() |> 
  e_bar(y, coord_system = "polar") |> 
  e_scatter(z, coord_system = "polar") %>% 
  e_title("2-1")

funnel <- data.frame(stage = c("View", "Click", "Purchase"), value = c(80, 30, 20))

e5 <- funnel |> 
  e_charts() |> 
  e_funnel(value, stage) |> 
  e_title("2-2")

e6  <- iris |> 
  group_by(Species) |> 
  e_charts(Sepal.Length) |> 
  e_line(Sepal.Width) |> 
  e_title("2-3")

cb <- "() => {
  let x = 0;
  let elements1 = document.getElementsByClassName('echarts-input');
  let elements2 = document.getElementsByClassName('echarts-input2');

  Array.from(elements1).forEach(function(element) {
    element.addEventListener('change', updateCharts);
  });

  Array.from(elements2).forEach(function(element) {
    element.addEventListener('change', updateCharts);
  });

  function updateCharts(e) {
    // Get the values of both dropdowns
    let dropdown1Value = document.getElementById('echarts-select').value;
    let dropdown2Value = document.getElementById('echarts-select2').value;

    // Update the other dropdowns with the selected values
    document.getElementById('echarts-select3').value = dropdown1Value;
    document.getElementById('echarts-select4').value = dropdown2Value;

    // Define the chart option to set
    let chartOption = null;

    if (dropdown1Value === '1' && dropdown2Value === '1') {
      chartOption = opts[0]; // Display e1
    } else if (dropdown1Value === '1' && dropdown2Value === '2') {
      chartOption = opts[1]; // Display e2
    } else if (dropdown1Value === '1' && dropdown2Value === '3') {
      chartOption = opts[2]; // Display e3
    } else if (dropdown1Value === '2' && dropdown2Value === '1') {
      chartOption = opts[3]; // Display e4
    } else if (dropdown1Value === '2' && dropdown2Value === '2') {
      chartOption = opts[4]; // Display e5
    } else if (dropdown1Value === '2' && dropdown2Value === '3') {
      chartOption = opts[5]; // Display e6
    }

    // Update the chart option if defined
    if (chartOption !== null) {
      chart.setOption(chartOption, true);
    }
  }
}"

# Create a container div for both select elements with a gap
dropdown_container <- div(
  tags$select(
    id = "echarts-select",
    class = "form-select echarts-input",
    style = "width: 150px;", # Increase the width here
    tags$option(value = "1", "1"),
    tags$option(value = "2", "2")
  ),
  div(style = "width: 30px;"), # Add a 10px gap between the dropdowns
  tags$select(
    id = "echarts-select2",
    class = "form-select echarts-input",
    style = "width: 150px;", # Increase the width here
    tags$option(value = "1", "1"),
    tags$option(value = "2", "2"),
    tags$option(value = "3", "3")
  ),
  style = "display: flex; flex-direction: row;" # Use flexbox to position elements horizontally
)

e_morph(e1, e2, e3, e4, e5, e6, callback = cb) %>%
  htmlwidgets::prependContent(dropdown_container)

# setup charts for second overall chart area
liquid <- data.frame(val = c(0.6, 0.5, 0.4))

e1 <- liquid |> 
  e_charts() |> 
  e_liquid(val) %>% 
  e_title('1-1')

dates <- seq.Date(Sys.Date() - 30, Sys.Date(), by = "day")

river <- data.frame(
  dates = dates,
  apples = runif(length(dates)),
  bananas = runif(length(dates)),
  pears = runif(length(dates))
)

e2 <- river |> 
  e_charts(dates) |> 
  e_river(apples) |> 
  e_river(bananas) |> 
  e_river(pears) |> 
  e_tooltip(trigger = "axis") |> 
  e_title("1-2")

dates <- seq.Date(as.Date("2017-01-01"), as.Date("2018-12-31"), by = "day")
values <- rnorm(length(dates), 20, 6)

year <- data.frame(date = dates, values = values)

e3 <- year |> 
  e_charts(date) |> 
  e_calendar(range = "2018") |> 
  e_heatmap(values, coord_system = "calendar") |> 
  e_visual_map(max = 30) |> 
  e_title("1-3")

e4 <- e_charts() |> 
  e_gauge(41, "PERCENT") |> 
  e_title("2-1")

df <- data.frame(
  parents = c("","earth", "earth", "mars", "mars", "land", "land", "ocean", "ocean", "fish", "fish", "Everything", "Everything", "Everything"),
  labels = c("Everything", "land", "ocean", "valley", "crater", "forest", "river", "kelp", "fish", "shark", "tuna", "venus","earth", "mars"),
  value = c(0, 30, 40, 10, 10, 20, 10, 20, 20, 8, 12, 10, 70, 20)
)

# create a tree object
universe <- data.tree::FromDataFrameNetwork(df)

# use it in echarts4r
e5 <- universe |> 
  e_charts() |> 
  e_sunburst() %>% 
  e_title('2-2')

e6 <- mtcars |> 
  head() |> 
  tibble::rownames_to_column("model") |> 
  e_charts(model) |> 
  e_pie(carb, radius = c("50%", "70%")) |> 
  e_title("2-3")

# Create a container div for both select elements with a gap
dropdown_container2 <- div(
  tags$select(
    id = "echarts-select3",
    class = "form-select echarts-input2",
    style = "width: 150px;", # Increase the width here
    tags$option(value = "1", "1"),
    tags$option(value = "2", "2")
  ),
  div(style = "width: 30px;"), # Add a 10px gap between the dropdowns
  tags$select(
    id = "echarts-select4",
    class = "form-select echarts-input2",
    style = "width: 150px;", # Increase the width here
    tags$option(value = "1", "1"),
    tags$option(value = "2", "2"),
    tags$option(value = "3", "3")
  ),
  style = "display: flex; flex-direction: row;" # Use flexbox to position elements horizontally
)

e_morph(e1, e2, e3, e4, e5, e6, callback = cb) %>%
  htmlwidgets::prependContent(dropdown_container2)
oobd commented 7 months ago

Ah, figured it out, needed to add a new function in callback that read values from second set of drop downs (updateCharts2) working code below

library(echarts4r)
library(tidyverse)
library(htmlwidgets)
library(htmltools)

df <- data.frame(
  x = seq(50),
  y = rnorm(50, 10, 3),
  z = rnorm(50, 11, 2),
  w = rnorm(50, 9, 2)
)

e1  <- df |> 
  e_charts(x) |> 
  e_line(z) |> 
  e_area(w) |> 
  e_title("1-1")

e2  <- df |> 
  e_charts(x) |> 
  e_bar(y, name = "Serie 1") |> 
  e_step(z, name = "Serie 2") |> 
  e_title("1-2")

e3  <- df |> 
  e_charts(x) |> 
  e_scatter(y, z) |> 
  e_visual_map(z, scale = e_scale) |> # scale color
  e_legend(FALSE) |>
  e_title("1-3")

e4  <- df |> 
  e_charts(x) |> 
  e_polar() |> 
  e_angle_axis(x) |> # angle = x
  e_radius_axis() |> 
  e_bar(y, coord_system = "polar") |> 
  e_scatter(z, coord_system = "polar") %>% 
  e_title("2-1")

funnel <- data.frame(stage = c("View", "Click", "Purchase"), value = c(80, 30, 20))

e5 <- funnel |> 
  e_charts() |> 
  e_funnel(value, stage) |> 
  e_title("2-2")

e6  <- iris |> 
  group_by(Species) |> 
  e_charts(Sepal.Length) |> 
  e_line(Sepal.Width) |> 
  e_title("2-3")

cb <- "() => {
  let x = 0;
  let elements1 = document.getElementsByClassName('echarts-input');
  let elements2 = document.getElementsByClassName('echarts-input2');

  Array.from(elements1).forEach(function(element) {
    element.addEventListener('change', updateCharts);
  });

  Array.from(elements2).forEach(function(element) {
    element.addEventListener('change', updateCharts2);
  });

  function updateCharts(e) {
    // Get the values of both dropdowns
    let dropdown1Value = document.getElementById('echarts-select').value;
    let dropdown2Value = document.getElementById('echarts-select2').value;

    // Update the other dropdowns with the selected values
    document.getElementById('echarts-select3').value = dropdown1Value;
    document.getElementById('echarts-select4').value = dropdown2Value;

    // Define the chart option to set
    let chartOption = null;

    if (dropdown1Value === '1' && dropdown2Value === '1') {
      chartOption = opts[0]; // Display e1
    } else if (dropdown1Value === '1' && dropdown2Value === '2') {
      chartOption = opts[1]; // Display e2
    } else if (dropdown1Value === '1' && dropdown2Value === '3') {
      chartOption = opts[2]; // Display e3
    } else if (dropdown1Value === '2' && dropdown2Value === '1') {
      chartOption = opts[3]; // Display e4
    } else if (dropdown1Value === '2' && dropdown2Value === '2') {
      chartOption = opts[4]; // Display e5
    } else if (dropdown1Value === '2' && dropdown2Value === '3') {
      chartOption = opts[5]; // Display e6
    }

    // Update the chart option if defined
    if (chartOption !== null) {
      chart.setOption(chartOption, true);
    }
  }

  function updateCharts2(e) {
    // Get the values of both dropdowns
    let dropdown1Value = document.getElementById('echarts-select3').value;
    let dropdown2Value = document.getElementById('echarts-select4').value;

    // Update the other dropdowns with the selected values
    document.getElementById('echarts-select').value = dropdown1Value;
    document.getElementById('echarts-select2').value = dropdown2Value;

    // Define the chart option to set
    let chartOption = null;

    if (dropdown1Value === '1' && dropdown2Value === '1') {
      chartOption = opts[0]; // Display e1
    } else if (dropdown1Value === '1' && dropdown2Value === '2') {
      chartOption = opts[1]; // Display e2
    } else if (dropdown1Value === '1' && dropdown2Value === '3') {
      chartOption = opts[2]; // Display e3
    } else if (dropdown1Value === '2' && dropdown2Value === '1') {
      chartOption = opts[3]; // Display e4
    } else if (dropdown1Value === '2' && dropdown2Value === '2') {
      chartOption = opts[4]; // Display e5
    } else if (dropdown1Value === '2' && dropdown2Value === '3') {
      chartOption = opts[5]; // Display e6
    }

    // Update the chart option if defined
    if (chartOption !== null) {
      chart.setOption(chartOption, true);
    }
  }

}"

# Create a container div for both select elements with a gap
dropdown_container <- div(
  tags$select(
    id = "echarts-select",
    class = "form-select echarts-input",
    style = "width: 150px;", # Increase the width here
    tags$option(value = "1", "1"),
    tags$option(value = "2", "2")
  ),
  div(style = "width: 30px;"), # Add a 10px gap between the dropdowns
  tags$select(
    id = "echarts-select2",
    class = "form-select echarts-input",
    style = "width: 150px;", # Increase the width here
    tags$option(value = "1", "1"),
    tags$option(value = "2", "2"),
    tags$option(value = "3", "3")
  ),
  style = "display: flex; flex-direction: row;" # Use flexbox to position elements horizontally
)

e_morph(e1, e2, e3, e4, e5, e6, callback = cb) %>%
  htmlwidgets::prependContent(dropdown_container)

# setup charts for second overall chart area
liquid <- data.frame(val = c(0.6, 0.5, 0.4))

e1 <- liquid |> 
  e_charts() |> 
  e_liquid(val) %>% 
  e_title('1-1')

dates <- seq.Date(Sys.Date() - 30, Sys.Date(), by = "day")

river <- data.frame(
  dates = dates,
  apples = runif(length(dates)),
  bananas = runif(length(dates)),
  pears = runif(length(dates))
)

e2 <- river |> 
  e_charts(dates) |> 
  e_river(apples) |> 
  e_river(bananas) |> 
  e_river(pears) |> 
  e_tooltip(trigger = "axis") |> 
  e_title("1-2")

dates <- seq.Date(as.Date("2017-01-01"), as.Date("2018-12-31"), by = "day")
values <- rnorm(length(dates), 20, 6)

year <- data.frame(date = dates, values = values)

e3 <- year |> 
  e_charts(date) |> 
  e_calendar(range = "2018") |> 
  e_heatmap(values, coord_system = "calendar") |> 
  e_visual_map(max = 30) |> 
  e_title("1-3")

e4 <- e_charts() |> 
  e_gauge(41, "PERCENT") |> 
  e_title("2-1")

df <- data.frame(
  parents = c("","earth", "earth", "mars", "mars", "land", "land", "ocean", "ocean", "fish", "fish", "Everything", "Everything", "Everything"),
  labels = c("Everything", "land", "ocean", "valley", "crater", "forest", "river", "kelp", "fish", "shark", "tuna", "venus","earth", "mars"),
  value = c(0, 30, 40, 10, 10, 20, 10, 20, 20, 8, 12, 10, 70, 20)
)

# create a tree object
universe <- data.tree::FromDataFrameNetwork(df)

# use it in echarts4r
e5 <- universe |> 
  e_charts() |> 
  e_sunburst() %>% 
  e_title('2-2')

e6 <- mtcars |> 
  head() |> 
  tibble::rownames_to_column("model") |> 
  e_charts(model) |> 
  e_pie(carb, radius = c("50%", "70%")) |> 
  e_title("2-3")

# Create a container div for both select elements with a gap
dropdown_container2 <- div(
  tags$select(
    id = "echarts-select3",
    class = "form-select echarts-input2",
    style = "width: 150px;", # Increase the width here
    tags$option(value = "1", "1"),
    tags$option(value = "2", "2")
  ),
  div(style = "width: 30px;"), # Add a 10px gap between the dropdowns
  tags$select(
    id = "echarts-select4",
    class = "form-select echarts-input2",
    style = "width: 150px;", # Increase the width here
    tags$option(value = "1", "1"),
    tags$option(value = "2", "2"),
    tags$option(value = "3", "3")
  ),
  style = "display: flex; flex-direction: row;" # Use flexbox to position elements horizontally
)

e_morph(e1, e2, e3, e4, e5, e6, callback = cb) %>%
  htmlwidgets::prependContent(dropdown_container2)