Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ export(ggsql_unregister)
export(ggsql_validate)
export(ggsql_visual)
export(ggsql_warnings)
export(ggsql_widget)
export(odbc_reader)
export(renderGgsql)
export(snowflake_reader)
Expand Down
3 changes: 1 addition & 2 deletions R/engine.R
Original file line number Diff line number Diff line change
Expand Up @@ -361,8 +361,7 @@ ggsql_engine_eval <- function(query, reader, options) {
))
}
writer <- vegalite_writer()
json <- ggsql_render(writer, spec)
widget <- ggsql_widget(json)
widget <- ggsql_widget(writer, spec)
out <- knitr::knit_print(widget, options = options)
knitr::knit_meta_add(attr(out, "knit_meta"))
knitr::engine_output(options, options$code, out = out)
Expand Down
6 changes: 2 additions & 4 deletions R/shiny.R
Original file line number Diff line number Diff line change
Expand Up @@ -122,8 +122,7 @@ renderGgsql <- function(
}

if (inherits(value, "Spec")) {
json <- ggsql_render(vegalite_writer(), value)
return(ggsql_widget(json))
return(ggsql_widget(vegalite_writer(), value))
}

if (!is.character(value) || length(value) != 1L) {
Expand Down Expand Up @@ -151,8 +150,7 @@ renderGgsql <- function(
)
}
spec <- ggsql_execute(r, query)
json <- ggsql_render(vegalite_writer(), spec)
ggsql_widget(json)
ggsql_widget(vegalite_writer(), spec)
})

htmlwidgets::shinyRenderWidget(
Expand Down
6 changes: 2 additions & 4 deletions R/spec.R
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ Spec <- R6::R6Class(
},

print = function(...) {
json <- ggsql_render(vegalite_writer(), self)
widget <- ggsql_widget(json)
widget <- ggsql_widget(vegalite_writer(), self)
print(widget)
}
)
Expand All @@ -38,8 +37,7 @@ knit_print.Spec <- function(x, ..., inline = FALSE) {
switch(
writer_type,
vegalite = {
json <- ggsql_render(vegalite_writer(), x)
widget <- ggsql_widget(json)
widget <- ggsql_widget(vegalite_writer(), x)
knitr::knit_print(widget, options = options)
},
vegalite_svg = ,
Expand Down
46 changes: 42 additions & 4 deletions R/widget.R
Original file line number Diff line number Diff line change
@@ -1,13 +1,51 @@
#' @noRd
#' Create a ggsql htmlwidget
#'
#' Create a `ggsql_vega` htmlwidget from a writer and spec.
#'
#' @param writer A `Writer` object created by e.g. [vegalite_writer()].
#' @param spec A `Spec` object returned by [ggsql_execute()].
#' @param width,height Optional widget dimensions passed to
#' [htmlwidgets::createWidget()].
#' @param min_width Optional minimum render width for small containers. When
#' supplied, the widget renders at at least this width and scales down to fit
#' narrower hosts.
#'
#' @return An `htmlwidget` with class `ggsql_vega`.
#'
#' @export
ggsql_widget <- function(
spec_json,
writer,
spec,
width = NULL,
height = NULL
height = NULL,
min_width = NULL
) {
check_r6(writer, "Writer")
check_r6(spec, "Spec")

if (!is.null(min_width)) {
if (
!is.numeric(min_width) ||
length(min_width) != 1L ||
is.na(min_width) ||
!is.finite(min_width) ||
min_width <= 0
) {
cli::cli_abort(
"{.arg min_width} must be `NULL` or a single positive number."
)
}

min_width <- as.numeric(min_width)
}

spec_json <- ggsql_render(writer, spec)

widget <- htmlwidgets::createWidget(
name = "ggsql_vega",
x = list(
spec = jsonlite::parse_json(spec_json)
spec = jsonlite::parse_json(spec_json),
min_width = min_width
),
width = width,
height = height,
Expand Down
2 changes: 1 addition & 1 deletion inst/htmlwidgets/ggsql_vega.css
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,5 @@ ggsql-vega .ggsql-vega-scale-wrapper {
}

ggsql-vega .ggsql-vega-container {
transform-origin: top left;
transform-origin: left center;
}
25 changes: 18 additions & 7 deletions inst/htmlwidgets/ggsql_vega.js
Original file line number Diff line number Diff line change
Expand Up @@ -123,20 +123,19 @@
}

// srcts/vega/widget.ts
var MIN_WIDTH = 450;
function readHostBox(el, width, height) {
const hostWidth = typeof width === "number" && width > 0 ? width : el.clientWidth || 0;
const styledHeight = typeof el.style.height === "string" && /px$/.test(el.style.height) ? parseFloat(el.style.height) : 0;
const hostHeight = typeof height === "number" && height > 0 ? height : el.clientHeight || styledHeight || 0;
return { hostWidth, hostHeight };
}
function buildSimpleLayout(hostWidth, hostHeight) {
function buildSimpleLayout(hostWidth, hostHeight, minWidth) {
return {
hostWidth,
hostHeight,
renderWidth: Math.max(hostWidth, MIN_WIDTH),
renderWidth: minWidth === null ? hostWidth : Math.max(hostWidth, minWidth),
renderHeight: hostHeight,
scale: hostWidth > 0 && hostWidth < MIN_WIDTH ? hostWidth / MIN_WIDTH : 1
scale: minWidth !== null && hostWidth > 0 && hostWidth < minWidth ? hostWidth / minWidth : 1
};
}
var VegaWidget = class extends HTMLElement {
Expand All @@ -160,6 +159,7 @@
this._embedToken = null;
this._scaleWrapper = null;
this._vegaContainer = null;
this.classList.remove("ggsql-scaled");
}
createStructure() {
this.innerHTML = "";
Expand All @@ -178,6 +178,8 @@
this._vegaContainer.style.width = `${layout.renderWidth}px`;
this._vegaContainer.style.height = `${layout.renderHeight}px`;
this._vegaContainer.style.transform = layout.scale < 1 ? `scale(${layout.scale})` : "";
if (layout.scale < 1) this.classList.add("ggsql-scaled");
else this.classList.remove("ggsql-scaled");
}
buildSimpleSpec(spec, layout) {
return {
Expand All @@ -204,6 +206,7 @@
this.applyLayout(layout);
view.width(layout.renderWidth).height(layout.renderHeight).resize().runAsync().catch((err) => {
if (self._view !== view) return;
self.classList.remove("ggsql-scaled");
self.textContent = `ggsql render error: ${String(err)}`;
});
}
Expand Down Expand Up @@ -243,23 +246,31 @@
self.applyLayout(self._layout);
}).catch((err) => {
if (self._embedToken !== token || self._vegaContainer !== container) return;
self.classList.remove("ggsql-scaled");
self.textContent = `ggsql render error: ${String(err)}`;
});
}
renderValue(x) {
const host = readHostBox(this);
const minWidth = x.min_width ?? null;
this._value = x;
this._isCompound = isCompoundSpec(x.spec);
if (this._isCompound) {
this.renderCompound(buildSimpleLayout(host.hostWidth, host.hostHeight));
this.renderCompound(
buildSimpleLayout(host.hostWidth, host.hostHeight, minWidth)
);
return;
}
this.renderSimple(buildSimpleLayout(host.hostWidth, host.hostHeight));
this.renderSimple(buildSimpleLayout(host.hostWidth, host.hostHeight, minWidth));
}
resize(width, height) {
if (!this._value) return;
const host = readHostBox(this, width, height);
const layout = buildSimpleLayout(host.hostWidth, host.hostHeight);
const layout = buildSimpleLayout(
host.hostWidth,
host.hostHeight,
this._value.min_width ?? null
);
if (this._isCompound) {
if (this.hasMaterialCompoundResize(layout)) this.renderCompound(layout);
else {
Expand Down
26 changes: 26 additions & 0 deletions man/ggsql_widget.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading