vue-r / vueR

vue.js for R
https://vue-r.github.io/vueR
Other
140 stars 14 forks source link

link vue to crosstalk? #12

Open timelyportfolio opened 3 years ago

timelyportfolio commented 3 years ago

Playing around and here is a quick example. In reality, crosstalk can be subsumed by Vue as simply a shared, reactive state mechanism.

with crosstalk api

library(vueR)
library(crosstalk)
library(htmltools)

data(mtcars)
sd <- SharedData$new(mtcars)

browsable(
    tagList(
    html_dependency_vue(minified = FALSE),
    filter_select("gear", "Gears", sd, ~gear),
    filter_slider("mpg", "Miles per gallon", sd, "mpg"),
    filter_slider("cyl", "Cylinders", sd, "cyl"),
    tags$div(
      id = "app",
      tags$h3("Filter Handle"),
      "{{ filter.filteredKeys ? filter.filteredKeys : 'nothing filtered' }}",
      tags$h3("Selection Handle"),
      "{{ selection.value ? selection.value : 'nothing selected' }}"
    ),
    tags$script(HTML(
sprintf(
"
  const app = new Vue({
    el: '#app',
    data() {
      return {
        filter: new crosstalk.FilterHandle('%1$s'),
        selection: new crosstalk.SelectionHandle('%1$s')
      }
    }
  })
",
sd$groupName()
)
    ))
  )
)

with crosstalk api and plotly

library(crosstalk)
library(plotly)
library(vueR)
library(htmltools)

tx <- highlight_key(txhousing)
widgets <- bscols(
  widths = c(12, 12, 12),
  filter_select("city", "Cities", tx, ~city),
  filter_slider("sales", "Sales", tx, ~sales),
  filter_checkbox("year", "Years", tx, ~year, inline = TRUE)
)
bc <- bscols(
  widths = c(4, 8), widgets, 
  plot_ly(tx, x = ~date, y = ~median, showlegend = FALSE) %>% 
    add_lines(color = ~city, colors = "black")
)

browsable(
  tagList(
    html_dependency_vue(minified = FALSE),
    bc,
    tags$div(
      id = "app",
      tags$h3("Filter Handle"),
      "{{ filter.filteredKeys ? filter.filteredKeys : 'nothing filtered' }}",
      tags$h3("Selection Handle"),
      "{{ selection.value ? selection.value : 'nothing selected' }}"
    ),
    tags$script(HTML(
sprintf(
"
  const app = new Vue({
    el: '#app',
    data() {
      return {
        filter: new crosstalk.FilterHandle('%1$s'),
        selection: new crosstalk.SelectionHandle('%1$s')
      }
    }
  })
",
tx$groupName()
)
    ))
  )
)

library(crosstalk)
library(plotly)
library(vueR)
library(htmltools)

mh <- mtcars %>% highlight_key(~cyl)
pl <- mh %>%
  plot_ly(
    x = ~wt, y = ~mpg, text = ~cyl, mode = "markers+text", 
    textposition = "top", hoverinfo = "x+y"
  ) %>%
  highlight(on = "plotly_hover", off = "plotly_doubleclick")

browsable(
  tagList(
    html_dependency_vue(minified = FALSE),
    pl,
    tags$div(
      id = "app",
      tags$h3("Filter Handle"),
      "{{ filter.filteredKeys ? filter.filteredKeys : 'nothing filtered' }}",
      tags$h3("Selection Handle"),
      "{{ selection.value ? selection.value : 'nothing selected' }}"
    ),
    tags$script(HTML(
sprintf(
"
  const app = new Vue({
    el: '#app',
    data() {
      return {
        filter: new crosstalk.FilterHandle('%1$s'),
        selection: new crosstalk.SelectionHandle('%1$s')
      }
    }
  })
",
mh$groupName()
)
    ))
  )
)

direct (unadvisable)

library(vueR)
library(crosstalk)
library(htmltools)

data(mtcars)
sd <- SharedData$new(mtcars)

browsable(
    tagList(
    html_dependency_vue(minified = FALSE),
    filter_slider("mpg", "Miles per gallon", sd, "mpg"),
    filter_slider("cyl", "Cylinders", sd, "cyl"),
    tags$div(
      id = "app",
      "{{ $data.get()._value ? $data.get()._value : 'nothing filtered' }}"
    ),
    tags$script(HTML(
sprintf(
"
  const app = new Vue({
    el: '#app',
    data() {
      return crosstalk.group('%s').var('filterset')
    }
  })
",
sd$groupName()
)
    ))
  )
)
FrissAnalytics commented 3 years ago

nice!

timelyportfolio commented 3 years ago

I thought it would be fun to use a Vuetify v-select component to control crosstalk. Here is my first approach.

library(crosstalk)
library(plotly)
library(vueR)
library(htmltools)

mh <- mtcars %>% highlight_key(~cyl)
pl <- mh %>%
  plot_ly(
    x = ~wt, y = ~mpg, text = ~cyl, mode = "markers+text", 
    textposition = "top", hoverinfo = "x+y"
  ) %>%
  highlight(on = "plotly_hover", off = "plotly_doubleclick")

browsable(
  tagList(
    vueR::html_dependency_vue(minified = FALSE),
    tags$head(
      tags$link(href="https://cdn.jsdelivr.net/npm/vuetify@2.x/dist/vuetify.min.css", rel="stylesheet"),
      tags$link( href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900", rel="stylesheet"),
      tags$link(href="https://cdn.jsdelivr.net/npm/@mdi/font@4.x/css/materialdesignicons.min.css", rel="stylesheet"),
      tags$script(src="https://cdn.jsdelivr.net/npm/vuetify@2.x/dist/vuetify.js")
    ),
    tag(
      "v-app",
      list(
        id = "app",
        tags$div(HTML("<v-select :items = 'cyl' @change = 'updateSelected' v-model= 'selected' multiple chips/>")),
        pl,
        tags$h3("Filter Handle"),
        "{{ filter.filteredKeys ? filter.filteredKeys : 'nothing filtered' }}",
        tags$h3("Selection Handle"),
        "{{ selection.value ? selection.value : 'nothing selected' }}"
      )
    ),
    tags$script(HTML(
sprintf(
"
  const app = new Vue({
    el: '#app',
    vuetify: new Vuetify(),
    data() {
      return {
        filter: new crosstalk.FilterHandle('%1$s'),
        selection: new crosstalk.SelectionHandle('%1$s'),
        cyl: %2$s.map(d => d.toString()) // crosstalk keys are strings
      }
    },
    computed: {
      selected: function() {return this.selection.value}
    },
    methods: {
      updateSelected: function(val) {
        this.selection.set(Array.isArray(val) ? val : [val])
      }
    }
  })
",
mh$groupName(),
jsonlite::toJSON(sort(unique(mh$data()$cyl)),auto_unbox=TRUE)
)
    ))
  )
)

vuetify_plotly_crosstalk