Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Question: Arrow integration? #89

Open
JosiahParry opened this issue Jan 13, 2025 · 6 comments
Open

Question: Arrow integration? #89

JosiahParry opened this issue Jan 13, 2025 · 6 comments

Comments

@JosiahParry
Copy link

I'm currently developing bindings to Calcite components at https://github.com/R-ArcGIS/calcite/. Right now this is using Shiny's .setInputValue() which, AFAIK, uses {jsonlite} to deserialize json? This is incredibly slow.

I think it would be incredibly useful to be able to use arrow to handle this. From the R side nanoarrow and from the JS side arrow-js-ffi could be used https://www.npmjs.com/package/arrow-js-ffi.

Am I right in thinking that the way this would have to be handled is with a websocket: https://ambiorix.dev/docs/ambiorix/websocket/?

@JohnCoene
Copy link
Collaborator

Calcite looks pretty cool!

Right now this is using Shiny's .setInputValue() which, AFAIK, uses {jsonlite} to deserialize json? This is incredibly slow.

That is also my understanding, also, since setInputValue()/sendCustomMessage is used for a lot of internal stuff the serialiser/deserialiser cannot be customised so you're stuck with {jsonlite}.

With ambiorix you should be able to have control over this. By default I think the websocket connection is binary. Also the request/response in ambiorix should be able to handle any valid format, see #44 for an example.

Note that there is a way to open endpoints in Shiny too (where you could bypass serialiser, see this horrid video).

I'd love to help more when you have an example!

@jrosell
Copy link

jrosell commented Jan 15, 2025

@JosiahParry congrats for the {caclite} package. It would be awesome to be able to use its components from {ambiorix}. It's a thing we have been dsicussing about with @JohnCoene @kennedymwavu: Be able to reuse htmlwigtes and shiny components in ambiorix (plotly, DT, etc) and have some examples of their implementation in {ambiorix}, so any developer can do it for other components too.

@JohnCoene
Copy link
Collaborator

JohnCoene commented Jan 15, 2025

@jrosell yes, we desperately need something.

Early days I think I used to render the HTML to temp file then read the file in but it felt funny, so I later replaced that with as.character which was improved by @kennedymwavu who changed it for htmltools::renderTags but we're not rendering it entirely correctly.

I think we should be able to get any {htmltools} working fine in ambiorix.

library(ambiorix)
library(htmltools)
library(calcite)

# core app
app <- Ambiorix$new()

app$get("/", \(req, res){
  shell <- calcite_shell(
    calcite_navigation(
      slot = "header",
      calcite_navigation_logo(
        slot = "logo",
        heading = "Snow Plow Map",
        description = "City of AcmeCo"
      ),
      calcite_menu(
        slot = "content-end",
        calcite_menu_item(text = "Drivers", `icon-start` = "license", `text-enabled` = TRUE),
        calcite_menu_item(text = "Routes", `icon-start` = "road-sign", `text-enabled` = TRUE),
        calcite_menu_item(text = "Forecast", `icon-start` = "snow", `text-enabled` = TRUE)
      ),
      calcite_navigation(
        slot = "navigation-secondary",
        calcite_menu(
          slot = "content-start",
          calcite_menu_item(breadcrumb = TRUE, text = "All Routes", `icon-start` = "book", `text-enabled` = TRUE),
          calcite_menu_item(breadcrumb = TRUE, text = "South Hills", `icon-start` = "apps", `text-enabled` = TRUE, active = TRUE)
        )
      ),
      calcite_navigation_user(slot = "user", `full-name` = "Wendell Berry", username = "w_berry")
    )
  )

  tgs <- htmltools::renderTags(shell)
  dps <- htmltools::renderDependencies(tgs$dependencies)

  page <- tags$html(
    tags$head(dps),
    tags$body(tgs)
  )

  res$send(page)
})

app$start()

@JosiahParry
Copy link
Author

This looks great! Works for me locally.

A helper function like:

#' returns html to be sent 
render_htmltools <- function(x) {
  tgs <- htmltools::renderTags(x)
  dps <- htmltools::renderDependencies(tgs$dependencies)
  tags$html(
    tags$head(dps),
    tags$body(tgs)
  )
}

could be handy here so that res$send(render_htmltools(shell)).

For me, the bit I'd like to figure out is how to set and fetch properties from the components and then using their custom event observers.

@JohnCoene
Copy link
Collaborator

For me, the bit I'd like to figure out is how to set and fetch properties from the components and then using their custom event observers.

Do you mind pointing me to something in the docs? I genuinely love web components, happy to see ArcGIS went with that.

@JosiahParry
Copy link
Author

@JohnCoene of course!

Here is the one I worked off of the most: https://developers.arcgis.com/calcite-design-system/components/filter/

Here's a really bad app that uses the filteredItems property

library(shiny)
library(calcite)
library(htmltools)

ui <- calcite_shell(
  calcite_filter(id = "filter"),
  htmlOutput("cards")
)

make_card <- function(.x) {
  calcite_card(
    span(slot = "heading", .x$Species),
    span(
      slot = "description",
      sprintf("This plant has a petal width of %s", .x$Petal.Width)
    )
  )
}

server <- function(input, output, session) {
  .iris <- iris
  .iris[["Species"]] <- as.character(iris$Species)
  update_calcite("filter", items = purrr::transpose(.iris))

  observeEvent(input$filter_value, {
      items <- input$filter_filteredItems$values
      if (length(items) > 0) {
        cat(str(items))
        output$cards <- renderUI(div(tagList(!!!lapply(items, make_card))))
      } else {
        output$cards <- renderUI(div())
      }
  })
}

shinyApp(ui, server)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants