| Title: | Interactive Web Widgets for Vellum Scenes |
|---|---|
| Description: | Turns a 'vellum' scene (or a 'vellumplot' plot) into a self-contained, client-side interactive HTML widget: hover tooltips and highlighting, click selection, rectangular brush-select, pan/zoom, and a toolbar -- driven by the per-element data keys, bounding boxes, and 'scene_model()' metadata that 'vellum' emits. Requires no Shiny and no server round-trip. |
| Authors: | David Schoch [aut, cre] |
| Maintainer: | David Schoch <[email protected]> |
| License: | MIT + file LICENSE |
| Version: | 0.8.0.9000 |
| Built: | 2026-10-09 11:17:20 UTC |
| Source: | https://github.com/r-vellum/vellumwidget |
as_widget() is the terminal verb of the interactivity pipeline: it compiles
its input to a vellum scene, emits the SVG (with per-element data-keys) and
the vellum::scene_model() element table, and bundles them with the vellumwidget
JavaScript runtime into a self-contained htmlwidgets::createWidget() widget.
The result does hover tooltips, hover highlighting, and click selection
entirely client-side — no Shiny, no server round-trip. Pan/zoom, brush, and
selection work with mouse, touch (drag to pan, two-finger pinch to zoom), and
keyboard input: the arrows pan and +/-/0 zoom and reset, except that with
accessibility on (the default) the arrows move between marks while one is
focused — see the a11y argument.
as_widget( x, width = NULL, height = NULL, toolbar = TRUE, navigator = FALSE, navigator_height = NULL, hover_mode = c("closest", "x", "y"), crosshair = FALSE, legend_click = c("select", "hide", "mute"), a11y = TRUE, alt = NULL, tooltip_delay = 0, tooltip_follow = TRUE, tooltip_sticky = FALSE, tooltip_style = NULL, export_filename = NULL, export_scale = NULL, group = NULL, crosstalk = NULL, select_mode = c("multiple", "single"), mode = c("auto", "svg", "raster"), raster_threshold = 20000L, text = c("native", "outline"), elementId = NULL )as_widget( x, width = NULL, height = NULL, toolbar = TRUE, navigator = FALSE, navigator_height = NULL, hover_mode = c("closest", "x", "y"), crosshair = FALSE, legend_click = c("select", "hide", "mute"), a11y = TRUE, alt = NULL, tooltip_delay = 0, tooltip_follow = TRUE, tooltip_sticky = FALSE, tooltip_style = NULL, export_filename = NULL, export_scale = NULL, group = NULL, crosstalk = NULL, select_mode = c("multiple", "single"), mode = c("auto", "svg", "raster"), raster_threshold = 20000L, text = c("native", "outline"), elementId = NULL )
x |
A |
width, height
|
Widget size (any valid CSS size, or |
toolbar |
Show the on-hover toolbar (default |
navigator |
Show an overview range navigator below the plot (default
|
navigator_height |
Height of the navigator strip in pixels (default |
hover_mode |
How hover gathers marks into the tooltip. |
crosshair |
Draw a guide rule at the hovered position (default |
legend_click |
What clicking a discrete-legend swatch does. |
a11y |
Accessibility (default |
alt |
Accessible label (alt text) for the chart as a whole. Defaults to
the scene's own title/description — which |
tooltip_delay |
Milliseconds to wait before the tooltip appears on hover
(default |
tooltip_follow |
When |
tooltip_sticky |
When |
tooltip_style |
Optional named list styling the tooltip box:
|
export_filename, export_scale
|
The download filename base (no extension;
default |
group |
Optional linking group name. Widgets sharing a |
crosstalk |
Optional crosstalk::SharedData (or a crosstalk group name
string) to link this widget with the crosstalk ecosystem (plotly, leaflet,
DT, and crosstalk's |
select_mode |
|
mode |
Rendering strategy for the marks. |
raster_threshold |
Keyed-element count above which |
text |
How text is written into the SVG, passed to |
elementId |
Optional explicit widget DOM id. |
Interactivity is driven by the keys/metadata a plot declares. In vellumplot these
come from the reserved data_id / tooltip / hover_group mark arguments; a
plot that declares none renders as a static (but still embeddable) SVG. A
hovered element with a data_id but no tooltip shows its key.
Graph (vgraph()) plots that declare vellumplot::select_neighbours() are
enacted here: hovering a node spotlights its neighbourhood (the node, its
incident edges, and its adjacent nodes) and dims the rest, and hovering an edge
spotlights its two endpoints. The adjacency is reconstructed from the endpoint
identity each edge carries — no extra configuration.
The scene metadata vellumwidget reads — the vellum::scene_model() element
table and the SVG data-key / data-vellum-* attributes — is specified in
vellum's "The scene contract" vignette
(vignette("scene-contract", package = "vellum")).
An htmlwidget of class "vellumwidget".
## Not run: library(vellumplot) df <- data.frame(wt = mtcars$wt, mpg = mtcars$mpg, model = rownames(mtcars)) vplot(df) |> mark_point(x = wt, y = mpg, tooltip = model, data_id = model) |> as_widget() ## End(Not run)## Not run: library(vellumplot) df <- data.frame(wt = mtcars$wt, mpg = mtcars$mpg, model = rownames(mtcars)) vplot(df) |> mark_point(x = wt, y = mpg, tooltip = model, data_id = model) |> as_widget() ## End(Not run)
vellumwidget_proxy() is the server-to-client counterpart of the input
read-back documented in vellumwidget-shiny. It returns a lightweight handle to a
widget that is already on the page, so the server can change what the
widget shows — set the selection, cross-filter it, zoom it — without
re-rendering it (no renderVellumwidget() round-trip, no full redraw, no lost
pan/zoom). This mirrors leaflet::leafletProxy() / DT::dataTableProxy() /
plotly::plotlyProxy().
vellumwidget_proxy(outputId, session = NULL)vellumwidget_proxy(outputId, session = NULL)
outputId |
The id of the |
session |
The Shiny session; defaults to the current reactive domain, so you rarely pass it explicitly. |
Call it inside an observe() / observeEvent() with the same outputId you
gave vellumwidgetOutput(), then pipe the handle through one or more of the verbs
below. Each verb sends a single custom message to the browser and returns the
proxy invisibly, so calls chain with the pipe.
vw_select()Replace the widget's selection with keys (the element
data_ids). Selecting projects across a mark's hover_group and
propagates to any linked / crosstalk widgets, exactly as a user click
would, and updates input$<id>_selected.
vw_clear_selection()Clear the selection.
vw_filter()Cross-filter: show only the elements whose key is in
keys and dim/hide the rest (display tier — the data is untouched).
vw_clear_filter()Remove the filter (show everything).
vw_zoom()Zoom/pan the view to frame the elements in keys.
vw_reset_zoom()Restore the original (full) view.
All keys are the element data keys — the data_id a vellumplot mark
declares — the same identifiers you receive back through input$<id>_selected
and friends.
An object of class "vellumwidget_proxy" (invisibly from the verbs), to be
piped into vw_select() and the other proxy verbs.
vw_select(), vw_filter(), vw_zoom(); vellumwidgetOutput() and
vellumwidget-shiny for reading interactions back.
## Not run: library(shiny) server <- function(input, output, session) { output$plot <- renderVellumwidget(my_widget) # A server-side control drives the plot without redrawing it: observeEvent(input$highlight, { vellumwidget_proxy("plot") |> vw_select(input$highlight) }) observeEvent(input$reset, { vellumwidget_proxy("plot") |> vw_clear_selection() |> vw_reset_zoom() }) } ## End(Not run)## Not run: library(shiny) server <- function(input, output, session) { output$plot <- renderVellumwidget(my_widget) # A server-side control drives the plot without redrawing it: observeEvent(input$highlight, { vellumwidget_proxy("plot") |> vw_select(input$highlight) }) observeEvent(input$reset, { vellumwidget_proxy("plot") |> vw_clear_selection() |> vw_reset_zoom() }) } ## End(Not run)
Pipe a vellumwidget_proxy() handle through these to change what an
already-rendered widget shows, without re-rendering it. Each sends one message
to the browser and returns the proxy invisibly, so they chain.
vw_select(proxy, keys) vw_clear_selection(proxy) vw_filter(proxy, keys) vw_clear_filter(proxy) vw_zoom(proxy, keys) vw_reset_zoom(proxy)vw_select(proxy, keys) vw_clear_selection(proxy) vw_filter(proxy, keys) vw_clear_filter(proxy) vw_zoom(proxy, keys) vw_reset_zoom(proxy)
proxy |
|
keys |
Character vector of element data keys (the |
The proxy, invisibly (for piping).
Standard htmlwidgets output/render helpers so a vellumwidget widget can appear in
a Shiny app or an interactive R Markdown document.
vellumwidgetOutput(outputId, width = "100%", height = "400px") renderVellumwidget(expr, env = parent.frame(), quoted = FALSE)vellumwidgetOutput(outputId, width = "100%", height = "400px") renderVellumwidget(expr, env = parent.frame(), quoted = FALSE)
outputId |
Shiny output slot id. |
width, height
|
Widget size. |
expr |
An expression producing a |
env, quoted
|
Standard non-standard-evaluation plumbing. |
vellumwidgetOutput(): a Shiny output UI element. renderVellumwidget(): a Shiny
render function.
A widget rendered as vellumwidgetOutput("plot") reports the user's interactions back
to the server as reactive inputs, keyed by the output id. All values are the
element data keys (the data_id a vellumplot mark declares); map them back to
your data by that key.
input$plot_selectedCharacter vector of the currently selected keys
(click / brush / keyboard, and any selection arriving from a linked widget).
Updates as state — re-selecting the same set is a no-op. character(0) when
nothing is selected.
input$plot_clickA list list(key=) for each click; key is NULL
for a click on empty space. An event input — fires on every click, even the
same mark twice.
input$plot_hoverThe hovered key, or NULL when the pointer leaves a
mark. Updates as state (re-fires only when the hovered key changes).
input$plot_brushA list list(keys=, x0=, y0=, x1=, y1=) when a
brush (or lasso) gesture completes: the selected keys and the region's
bounding rectangle in the scene's device-pixel (viewBox) coordinates. A
lasso gesture also carries lasso = TRUE. When the plot carries a cartesian
scale (a vellumplot plot), it also carries the region's data-space
bounds x0d,y0d,x1d,y1d and the panel name. An event input.
input$plot_colorfilterA length-2 numeric c(lo, hi) — the value
range selected on a continuous colorbar (see the interactive colorbar
filter); NULL when the full range is shown. State input.
input$plot_zoomA list list(x=, y=, w=, h=, zoomed=) — the current
view (the SVG viewBox in device-pixel coordinates) plus a zoomed flag
(is the view narrower/shorter than the full extent). For a single-panel
cartesian plot it also carries data = list(x=c(lo,hi), y=c(lo,hi), panel=),
the visible range in data coordinates. Updates as state when a zoom/pan
settles (wheel, drag-pan release, pinch, keyboard, reset, zoom-to-selection,
or a proxy vw_zoom()).
Data-space coordinates (x0d/… and zoom$data) come from the per-panel scale
descriptors vellumplot attaches to the scene; a raw vellum scene or a
non-cartesian coordinate system carries none, and only the device-pixel fields
are reported. Some specifics:
They describe the visual axes: x0d/x1d (and zoom$data$x) are the
horizontal axis. Under coord_flip() that is the plot's y aesthetic.
Date/time axes report the numeric epoch (days for Date, seconds for
POSIXct); map back with as.Date() / .POSIXct().
A discrete axis reports fractional band positions (category i sits at
i), not the level labels.
Axes built with a custom scales::transform_*() object (anything beyond
identity / log10 / sqrt / reverse) can't be inverted client-side, so that axis
is omitted from the data-space fields (device-pixel fields still report).
These are emitted only inside a live Shiny session; a static render (knitr,
pkgdown, htmltools::save_html()) produces identical output and no input
traffic. To drive the widget from the server — set the selection, cross-filter
it, or zoom it without a re-render — use vellumwidget_proxy().